Compare commits
2 commits
ci/digest-
...
build/dev-
| Author | SHA1 | Date | |
|---|---|---|---|
| 50f1ade92c | |||
| 364b787578 |
105 changed files with 4074 additions and 4306 deletions
9
.busted
9
.busted
|
|
@ -1,9 +0,0 @@
|
||||||
return {
|
|
||||||
_all = {
|
|
||||||
lua = "nlua",
|
|
||||||
},
|
|
||||||
default = {
|
|
||||||
ROOT = { "./spec/" },
|
|
||||||
helper = "spec/minimal_init.lua",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
insert_final_newline = true
|
|
||||||
charset = utf-8
|
|
||||||
|
|
||||||
[*.lua]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
1
.envrc
Normal file
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
||||||
80
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
80
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
|
|
@ -1,80 +0,0 @@
|
||||||
name: Bug Report
|
|
||||||
description: Report a bug
|
|
||||||
title: 'bug: '
|
|
||||||
labels: [bug]
|
|
||||||
body:
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Prerequisites
|
|
||||||
options:
|
|
||||||
- label:
|
|
||||||
I have searched [existing
|
|
||||||
issues](https://github.com/barrettruth/canola.nvim/issues)
|
|
||||||
required: true
|
|
||||||
- label: I have updated to the latest version
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: 'Neovim version'
|
|
||||||
description: 'Output of `nvim --version`'
|
|
||||||
render: text
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: 'Operating system'
|
|
||||||
placeholder: 'e.g. Arch Linux, macOS 15, Ubuntu 24.04'
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: What happened? What did you expect?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Steps to reproduce
|
|
||||||
description: Minimal steps to trigger the bug
|
|
||||||
value: |
|
|
||||||
1.
|
|
||||||
2.
|
|
||||||
3.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: 'Health check'
|
|
||||||
description: 'Output of `:checkhealth oil`'
|
|
||||||
render: text
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Minimal reproduction
|
|
||||||
description: |
|
|
||||||
Save the script below as `repro.lua`, edit if needed, and run:
|
|
||||||
```
|
|
||||||
nvim -u repro.lua
|
|
||||||
```
|
|
||||||
Confirm the bug reproduces with this config before submitting.
|
|
||||||
render: lua
|
|
||||||
value: |
|
|
||||||
vim.env.LAZY_STDPATH = '.repro'
|
|
||||||
load(vim.fn.system('curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua'))()
|
|
||||||
require('lazy.nvim').setup({
|
|
||||||
spec = {
|
|
||||||
{
|
|
||||||
'barrettruth/canola.nvim',
|
|
||||||
init = function()
|
|
||||||
vim.g.oil = {}
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
131
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
131
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
name: Bug Report
|
||||||
|
description: File a bug/issue
|
||||||
|
title: "bug: "
|
||||||
|
labels: [bug]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Before reporting a bug, make sure to search [existing issues](https://github.com/stevearc/oil.nvim/issues)
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Did you check the docs and existing issues?
|
||||||
|
options:
|
||||||
|
- label: I have read the docs
|
||||||
|
required: true
|
||||||
|
- label: I have searched the existing issues
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: "Neovim version (nvim -v)"
|
||||||
|
placeholder: "0.8.0 commit db1b0ee3b30f"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: "Operating system/version"
|
||||||
|
placeholder: "MacOS 11.5"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Describe the bug
|
||||||
|
description: A clear and concise description of what the bug is. Please include any related errors you see in Neovim.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: What is the severity of this bug?
|
||||||
|
options:
|
||||||
|
- minor (annoyance)
|
||||||
|
- tolerable (can work around it)
|
||||||
|
- breaking (some functionality is broken)
|
||||||
|
- blocking (cannot use plugin)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Steps To Reproduce
|
||||||
|
description: Steps to reproduce the behavior.
|
||||||
|
placeholder: |
|
||||||
|
1. nvim -u repro.lua
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: A concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Directory structure
|
||||||
|
description: The structure of the directory used to reproduce the bug
|
||||||
|
placeholder: |
|
||||||
|
a/b/foo.txt
|
||||||
|
a/bar.md
|
||||||
|
a/c/baz.txt
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Repro
|
||||||
|
description:
|
||||||
|
Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua`
|
||||||
|
This uses lazy.nvim (a plugin manager).
|
||||||
|
You can add your config with the `config` key the same way you can do with packer.nvim.
|
||||||
|
value: |
|
||||||
|
-- save as repro.lua
|
||||||
|
-- run with nvim -u repro.lua
|
||||||
|
-- DO NOT change the paths
|
||||||
|
local root = vim.fn.fnamemodify("./.repro", ":p")
|
||||||
|
|
||||||
|
-- set stdpaths to use .repro
|
||||||
|
for _, name in ipairs({ "config", "data", "state", "runtime", "cache" }) do
|
||||||
|
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
|
||||||
|
end
|
||||||
|
|
||||||
|
-- bootstrap lazy
|
||||||
|
local lazypath = root .. "/plugins/lazy.nvim"
|
||||||
|
if not vim.loop.fs_stat(lazypath) then
|
||||||
|
vim.fn.system({
|
||||||
|
"git",
|
||||||
|
"clone",
|
||||||
|
"--filter=blob:none",
|
||||||
|
"--single-branch",
|
||||||
|
"https://github.com/folke/lazy.nvim.git",
|
||||||
|
lazypath,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
vim.opt.runtimepath:prepend(lazypath)
|
||||||
|
|
||||||
|
-- install plugins
|
||||||
|
local plugins = {
|
||||||
|
"folke/tokyonight.nvim",
|
||||||
|
{
|
||||||
|
"stevearc/oil.nvim",
|
||||||
|
config = function()
|
||||||
|
require("oil").setup({
|
||||||
|
-- add any needed settings here
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
-- add any other plugins here
|
||||||
|
}
|
||||||
|
require("lazy").setup(plugins, {
|
||||||
|
root = root .. "/plugins",
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.cmd.colorscheme("tokyonight")
|
||||||
|
-- add anything else here
|
||||||
|
render: Lua
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Did you check the bug with a clean config?
|
||||||
|
options:
|
||||||
|
- label: I have confirmed that the bug reproduces with `nvim -u repro.lua` using the repro.lua file above.
|
||||||
|
required: true
|
||||||
5
.github/ISSUE_TEMPLATE/config.yaml
vendored
5
.github/ISSUE_TEMPLATE/config.yaml
vendored
|
|
@ -1,5 +0,0 @@
|
||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: Questions
|
|
||||||
url: https://github.com/barrettruth/canola.nvim/discussions
|
|
||||||
about: Ask questions and discuss ideas
|
|
||||||
30
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
30
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
|
|
@ -1,30 +0,0 @@
|
||||||
name: Feature Request
|
|
||||||
description: Suggest a feature
|
|
||||||
title: 'feat: '
|
|
||||||
labels: [enhancement]
|
|
||||||
body:
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Prerequisites
|
|
||||||
options:
|
|
||||||
- label:
|
|
||||||
I have searched [existing
|
|
||||||
issues](https://github.com/barrettruth/canola.nvim/issues)
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Problem
|
|
||||||
description: What problem does this solve?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Proposed solution
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Alternatives considered
|
|
||||||
43
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
43
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
name: Feature Request
|
||||||
|
description: Submit a feature request
|
||||||
|
title: "feature request: "
|
||||||
|
labels: [enhancement]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Before submitting a feature request, make sure to search for [existing requests](https://github.com/stevearc/oil.nvim/issues)
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Did you check existing requests?
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing issues
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Describe the feature
|
||||||
|
description: A short summary of the feature you want
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Provide background
|
||||||
|
description: Describe the reasoning behind why you want the feature.
|
||||||
|
placeholder: I am trying to do X. My current workflow is Y.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: What is the significance of this feature?
|
||||||
|
options:
|
||||||
|
- nice to have
|
||||||
|
- strongly desired
|
||||||
|
- cannot use this plugin without it
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional details
|
||||||
|
description: Any additional information you would like to provide. Things you've tried, alternatives considered, examples from other plugins, etc.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
158
.github/scripts/upstream_digest.py
vendored
158
.github/scripts/upstream_digest.py
vendored
|
|
@ -1,158 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
from datetime import date, timedelta
|
|
||||||
|
|
||||||
|
|
||||||
UPSTREAM = "stevearc/oil.nvim"
|
|
||||||
UPSTREAM_MD = "doc/upstream.md"
|
|
||||||
|
|
||||||
PRS_HEADING = "## Open upstream PRs"
|
|
||||||
ISSUES_HEADING = "## Upstream issues"
|
|
||||||
|
|
||||||
|
|
||||||
def get_last_tracked_number():
|
|
||||||
try:
|
|
||||||
with open(UPSTREAM_MD) as f:
|
|
||||||
content = f.read()
|
|
||||||
numbers = re.findall(
|
|
||||||
r"\[#(\d+)\]\(https://github\.com/stevearc/oil\.nvim", content
|
|
||||||
)
|
|
||||||
if numbers:
|
|
||||||
return max(int(n) for n in numbers)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def gh(*args):
|
|
||||||
result = subprocess.run(
|
|
||||||
["gh"] + list(args),
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
return result.stdout.strip()
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_items(last_number, since_date):
|
|
||||||
merged_prs = json.loads(
|
|
||||||
gh(
|
|
||||||
"pr", "list",
|
|
||||||
"--repo", UPSTREAM,
|
|
||||||
"--state", "merged",
|
|
||||||
"--limit", "100",
|
|
||||||
"--json", "number,title,url",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
open_prs = json.loads(
|
|
||||||
gh(
|
|
||||||
"pr", "list",
|
|
||||||
"--repo", UPSTREAM,
|
|
||||||
"--state", "open",
|
|
||||||
"--limit", "100",
|
|
||||||
"--json", "number,title,createdAt,url",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
open_issues = json.loads(
|
|
||||||
gh(
|
|
||||||
"issue", "list",
|
|
||||||
"--repo", UPSTREAM,
|
|
||||||
"--state", "open",
|
|
||||||
"--limit", "100",
|
|
||||||
"--json", "number,title,createdAt,url",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if last_number is not None:
|
|
||||||
merged_prs = [x for x in merged_prs if x["number"] > last_number]
|
|
||||||
open_prs = [x for x in open_prs if x["number"] > last_number]
|
|
||||||
open_issues = [x for x in open_issues if x["number"] > last_number]
|
|
||||||
else:
|
|
||||||
cutoff = since_date.isoformat()
|
|
||||||
merged_prs = []
|
|
||||||
open_prs = [x for x in open_prs if x.get("createdAt", "") >= cutoff]
|
|
||||||
open_issues = [x for x in open_issues if x.get("createdAt", "") >= cutoff]
|
|
||||||
|
|
||||||
merged_prs.sort(key=lambda x: x["number"])
|
|
||||||
open_prs.sort(key=lambda x: x["number"])
|
|
||||||
open_issues.sort(key=lambda x: x["number"])
|
|
||||||
|
|
||||||
return merged_prs, open_prs, open_issues
|
|
||||||
|
|
||||||
|
|
||||||
def append_to_section(content, heading, new_rows):
|
|
||||||
lines = content.split("\n")
|
|
||||||
in_section = False
|
|
||||||
last_table_row = -1
|
|
||||||
|
|
||||||
for i, line in enumerate(lines):
|
|
||||||
if line.startswith("## "):
|
|
||||||
in_section = line.strip() == heading
|
|
||||||
if in_section and line.startswith("|"):
|
|
||||||
last_table_row = i
|
|
||||||
|
|
||||||
if last_table_row == -1:
|
|
||||||
return content
|
|
||||||
|
|
||||||
return "\n".join(
|
|
||||||
lines[: last_table_row + 1] + new_rows + lines[last_table_row + 1 :]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
last_number = get_last_tracked_number()
|
|
||||||
since_date = date.today() - timedelta(days=30)
|
|
||||||
|
|
||||||
merged_prs, open_prs, open_issues = fetch_items(last_number, since_date)
|
|
||||||
|
|
||||||
total = len(merged_prs) + len(open_prs) + len(open_issues)
|
|
||||||
if total == 0:
|
|
||||||
print("No new upstream activity.")
|
|
||||||
return
|
|
||||||
|
|
||||||
with open(UPSTREAM_MD) as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
pr_rows = []
|
|
||||||
for pr in open_prs:
|
|
||||||
pr_rows.append(f"| [#{pr['number']}]({pr['url']}) | {pr['title']} | open |")
|
|
||||||
for pr in merged_prs:
|
|
||||||
pr_rows.append(
|
|
||||||
f"| [#{pr['number']}]({pr['url']}) | {pr['title']} | merged — not cherry-picked |"
|
|
||||||
)
|
|
||||||
|
|
||||||
issue_rows = []
|
|
||||||
for issue in open_issues:
|
|
||||||
issue_rows.append(
|
|
||||||
f"| [#{issue['number']}]({issue['url']}) | open | {issue['title']} |"
|
|
||||||
)
|
|
||||||
|
|
||||||
if pr_rows:
|
|
||||||
content = append_to_section(content, PRS_HEADING, pr_rows)
|
|
||||||
if issue_rows:
|
|
||||||
content = append_to_section(content, ISSUES_HEADING, issue_rows)
|
|
||||||
|
|
||||||
with open(UPSTREAM_MD, "w") as f:
|
|
||||||
f.write(content)
|
|
||||||
|
|
||||||
github_output = os.environ.get("GITHUB_OUTPUT")
|
|
||||||
if github_output:
|
|
||||||
with open(github_output, "a") as f:
|
|
||||||
f.write("changed=true\n")
|
|
||||||
|
|
||||||
print(
|
|
||||||
f"Added {len(open_prs)} open PR(s), {len(merged_prs)} merged PR(s), "
|
|
||||||
f"{len(open_issues)} issue(s) to {UPSTREAM_MD}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
try:
|
|
||||||
main()
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
print(f"gh command failed: {e.stderr}", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
@ -8,7 +8,7 @@ jobs:
|
||||||
# issues in my "needs triage" filter.
|
# issues in my "needs triage" filter.
|
||||||
remove_question:
|
remove_question:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.sender.login != 'barrettruth'
|
if: github.event.sender.login != 'stevearc'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions-ecosystem/action-remove-labels@v1
|
- uses: actions-ecosystem/action-remove-labels@v1
|
||||||
|
|
|
||||||
16
.github/workflows/install_nvim.sh
vendored
Normal file
16
.github/workflows/install_nvim.sh
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
version="${NVIM_TAG-stable}"
|
||||||
|
dl_name="nvim-linux-x86_64.appimage"
|
||||||
|
# The appimage name changed in v0.10.4
|
||||||
|
if python -c 'from packaging.version import Version; import sys; sys.exit(not (Version(sys.argv[1]) < Version("v0.10.4")))' "$version" 2>/dev/null; then
|
||||||
|
dl_name="nvim.appimage"
|
||||||
|
fi
|
||||||
|
curl -sL "https://github.com/neovim/neovim/releases/download/${version}/${dl_name}" -o nvim.appimage
|
||||||
|
chmod +x nvim.appimage
|
||||||
|
./nvim.appimage --appimage-extract >/dev/null
|
||||||
|
rm -f nvim.appimage
|
||||||
|
mkdir -p ~/.local/share/nvim
|
||||||
|
mv squashfs-root ~/.local/share/nvim/appimage
|
||||||
|
sudo ln -s "$HOME/.local/share/nvim/appimage/AppRun" /usr/bin/nvim
|
||||||
|
/usr/bin/nvim --version
|
||||||
2
.github/workflows/luarocks.yaml
vendored
2
.github/workflows/luarocks.yaml
vendored
|
|
@ -7,7 +7,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
quality:
|
quality:
|
||||||
uses: ./.github/workflows/quality.yaml
|
uses: ./.github/workflows/tests.yml
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
needs: quality
|
needs: quality
|
||||||
|
|
|
||||||
85
.github/workflows/mirror_upstream_prs.yml
vendored
Normal file
85
.github/workflows/mirror_upstream_prs.yml
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
name: Mirror Upstream PRs
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 8 * * *"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
mirror:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Mirror new upstream PRs
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
const upstream = { owner: 'stevearc', repo: 'oil.nvim' };
|
||||||
|
const fork = { owner: 'barrettruth', repo: 'oil.nvim' };
|
||||||
|
|
||||||
|
const since = new Date();
|
||||||
|
since.setDate(since.getDate() - 1);
|
||||||
|
const sinceISO = since.toISOString();
|
||||||
|
|
||||||
|
const { data: prs } = await github.rest.pulls.list({
|
||||||
|
...upstream,
|
||||||
|
state: 'open',
|
||||||
|
sort: 'created',
|
||||||
|
direction: 'desc',
|
||||||
|
per_page: 30,
|
||||||
|
});
|
||||||
|
|
||||||
|
const recentPRs = prs.filter(pr => {
|
||||||
|
if (new Date(pr.created_at) < since) return false;
|
||||||
|
if (pr.user.login === 'dependabot[bot]') return false;
|
||||||
|
if (pr.user.login === 'dependabot-preview[bot]') return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (recentPRs.length === 0) {
|
||||||
|
console.log('No new upstream PRs in the last 24 hours.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: existingIssues } = await github.rest.issues.listForRepo({
|
||||||
|
...fork,
|
||||||
|
state: 'all',
|
||||||
|
labels: 'upstream/pr',
|
||||||
|
per_page: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mirroredNumbers = new Set();
|
||||||
|
for (const issue of existingIssues) {
|
||||||
|
const match = issue.title.match(/^upstream#(\d+)/);
|
||||||
|
if (match) mirroredNumbers.add(parseInt(match[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const pr of recentPRs) {
|
||||||
|
if (mirroredNumbers.has(pr.number)) {
|
||||||
|
console.log(`Skipping PR #${pr.number} — already mirrored.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const labels = pr.labels.map(l => l.name).join(', ') || 'none';
|
||||||
|
|
||||||
|
await github.rest.issues.create({
|
||||||
|
...fork,
|
||||||
|
title: `upstream#${pr.number}: ${pr.title}`,
|
||||||
|
body: [
|
||||||
|
`Mirrored from [stevearc/oil.nvim#${pr.number}](${pr.html_url}).`,
|
||||||
|
'',
|
||||||
|
`**Author:** @${pr.user.login}`,
|
||||||
|
`**Labels:** ${labels}`,
|
||||||
|
`**Created:** ${pr.created_at}`,
|
||||||
|
'',
|
||||||
|
'---',
|
||||||
|
'',
|
||||||
|
pr.body || '*No description provided.*',
|
||||||
|
].join('\n'),
|
||||||
|
labels: ['upstream/pr'],
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Created issue for upstream PR #${pr.number}: ${pr.title}`);
|
||||||
|
}
|
||||||
73
.github/workflows/quality.yaml
vendored
73
.github/workflows/quality.yaml
vendored
|
|
@ -1,73 +0,0 @@
|
||||||
name: quality
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
push:
|
|
||||||
branches: [main, ci/upstream-digest]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
changes:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
lua: ${{ steps.changes.outputs.lua }}
|
|
||||||
markdown: ${{ steps.changes.outputs.markdown }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: dorny/paths-filter@v3
|
|
||||||
id: changes
|
|
||||||
with:
|
|
||||||
filters: |
|
|
||||||
lua:
|
|
||||||
- 'lua/**'
|
|
||||||
- 'plugin/**'
|
|
||||||
- 'spec/**'
|
|
||||||
- '*.lua'
|
|
||||||
- '.luarc.json'
|
|
||||||
- '*.toml'
|
|
||||||
- 'vim.yaml'
|
|
||||||
markdown:
|
|
||||||
- '*.md'
|
|
||||||
|
|
||||||
lua-format:
|
|
||||||
name: Lua Format Check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: changes
|
|
||||||
if: ${{ needs.changes.outputs.lua == 'true' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: cachix/install-nix-action@v31
|
|
||||||
- run: nix develop --command stylua --check lua spec
|
|
||||||
|
|
||||||
lua-lint:
|
|
||||||
name: Lua Lint Check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: changes
|
|
||||||
if: ${{ needs.changes.outputs.lua == 'true' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: cachix/install-nix-action@v31
|
|
||||||
- run: nix develop --command selene --display-style quiet .
|
|
||||||
|
|
||||||
lua-typecheck:
|
|
||||||
name: Lua Type Check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: changes
|
|
||||||
if: ${{ needs.changes.outputs.lua == 'true' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: stevearc/nvim-typecheck-action@v2
|
|
||||||
with:
|
|
||||||
path: lua
|
|
||||||
luals-version: 3.16.4
|
|
||||||
|
|
||||||
markdown-format:
|
|
||||||
name: Markdown Format Check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: changes
|
|
||||||
if: ${{ needs.changes.outputs.markdown == 'true' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: cachix/install-nix-action@v31
|
|
||||||
- run: nix develop --command prettier --check .
|
|
||||||
22
.github/workflows/test.yaml
vendored
22
.github/workflows/test.yaml
vendored
|
|
@ -1,22 +0,0 @@
|
||||||
name: test
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
push:
|
|
||||||
branches: [main, ci/upstream-digest]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
nvim: [stable, nightly]
|
|
||||||
name: Test (Neovim ${{ matrix.nvim }})
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: nvim-neorocks/nvim-busted-action@v1
|
|
||||||
with:
|
|
||||||
nvim_version: ${{ matrix.nvim }}
|
|
||||||
65
.github/workflows/tests.yml
vendored
Normal file
65
.github/workflows/tests.yml
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
name: Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
selene:
|
||||||
|
name: Selene
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: NTBBloodbath/selene-action@v1.0.0
|
||||||
|
with:
|
||||||
|
args: --display-style quiet .
|
||||||
|
|
||||||
|
stylua:
|
||||||
|
name: StyLua
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Stylua
|
||||||
|
uses: JohnnyMorganz/stylua-action@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
version: v2.0.2
|
||||||
|
args: --check lua tests
|
||||||
|
|
||||||
|
typecheck:
|
||||||
|
name: typecheck
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: stevearc/nvim-typecheck-action@v2
|
||||||
|
with:
|
||||||
|
path: lua
|
||||||
|
|
||||||
|
run_tests:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- nvim_tag: v0.8.3
|
||||||
|
- nvim_tag: v0.9.4
|
||||||
|
- nvim_tag: v0.10.4
|
||||||
|
- nvim_tag: v0.11.0
|
||||||
|
|
||||||
|
name: Run tests
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
env:
|
||||||
|
NVIM_TAG: ${{ matrix.nvim_tag }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Neovim and dependencies
|
||||||
|
run: |
|
||||||
|
bash ./.github/workflows/install_nvim.sh
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
bash ./run_tests.sh
|
||||||
50
.github/workflows/upstream-digest.yaml
vendored
50
.github/workflows/upstream-digest.yaml
vendored
|
|
@ -1,50 +0,0 @@
|
||||||
name: upstream digest
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 9 * * 1'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
digest:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Update upstream tracker
|
|
||||||
id: digest
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: python3 .github/scripts/upstream_digest.py
|
|
||||||
|
|
||||||
- name: Format doc/upstream.md
|
|
||||||
if: steps.digest.outputs.changed == 'true'
|
|
||||||
run: npx --yes prettier --write doc/upstream.md
|
|
||||||
|
|
||||||
- name: Push and open PR if needed
|
|
||||||
if: steps.digest.outputs.changed == 'true'
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
BRANCH="ci/upstream-digest"
|
|
||||||
git config user.name "github-actions[bot]"
|
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
git checkout -b "${BRANCH}"
|
|
||||||
git add doc/upstream.md
|
|
||||||
git commit -m "docs(upstream): upstream digest $(date +%Y-%m-%d)"
|
|
||||||
git config --unset http.https://github.com/.extraheader
|
|
||||||
git remote set-url origin "https://x-access-token:${{ secrets.DIGEST_PAT }}@github.com/barrettruth/canola.nvim.git"
|
|
||||||
git push --force origin "${BRANCH}"
|
|
||||||
if ! GH_TOKEN="${{ secrets.GITHUB_TOKEN }}" gh pr list --head "${BRANCH}" --state open --json number --jq '.[0].number' | grep -q .; then
|
|
||||||
PR_URL=$(GH_TOKEN="${{ secrets.GITHUB_TOKEN }}" gh pr create \
|
|
||||||
--title "docs(upstream): upstream digest" \
|
|
||||||
--body "Automated weekly digest of new upstream activity. Triage by updating statuses and notes." \
|
|
||||||
--base main \
|
|
||||||
--head "${BRANCH}")
|
|
||||||
GH_TOKEN="${{ secrets.DIGEST_PAT }}" gh pr review "${PR_URL}" --approve
|
|
||||||
gh pr merge "${PR_URL}" --auto --squash
|
|
||||||
fi
|
|
||||||
54
.gitignore
vendored
54
.gitignore
vendored
|
|
@ -1,12 +1,48 @@
|
||||||
doc/tags
|
# Compiled Lua sources
|
||||||
*.log
|
luac.out
|
||||||
.*cache*
|
|
||||||
CLAUDE.md
|
# luarocks build files
|
||||||
.claude/
|
*.src.rock
|
||||||
node_modules/
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
*.os
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
*.def
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
||||||
.direnv/
|
.direnv/
|
||||||
.envrc
|
.testenv/
|
||||||
venv/
|
doc/tags
|
||||||
perf/tmp/
|
scripts/nvim-typecheck-action
|
||||||
scripts/benchmark.nvim
|
scripts/benchmark.nvim
|
||||||
|
perf/tmp/
|
||||||
profile.json
|
profile.json
|
||||||
|
|
|
||||||
0
.gitmodules
vendored
Normal file
0
.gitmodules
vendored
Normal file
|
|
@ -3,14 +3,7 @@
|
||||||
"version": "LuaJIT",
|
"version": "LuaJIT",
|
||||||
"pathStrict": true
|
"pathStrict": true
|
||||||
},
|
},
|
||||||
"workspace": {
|
|
||||||
"checkThirdParty": false,
|
|
||||||
"ignoreDir": [".direnv"]
|
|
||||||
},
|
|
||||||
"type": {
|
"type": {
|
||||||
"checkTableShape": true
|
"checkTableShape": true
|
||||||
},
|
|
||||||
"completion": {
|
|
||||||
"callSnippet": "Replace"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"proseWrap": "always",
|
|
||||||
"printWidth": 80,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"useTabs": false,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"semi": false,
|
|
||||||
"singleQuote": true
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
column_width = 100
|
column_width = 100
|
||||||
line_endings = "Unix"
|
|
||||||
indent_type = "Spaces"
|
indent_type = "Spaces"
|
||||||
indent_width = 2
|
indent_width = 2
|
||||||
quote_style = "AutoPreferSingle"
|
|
||||||
call_parentheses = "Always"
|
|
||||||
[sort_requires]
|
[sort_requires]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
.direnv/
|
|
||||||
1
LICENSE
1
LICENSE
|
|
@ -1,7 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022 Steven Arcangeli
|
Copyright (c) 2022 Steven Arcangeli
|
||||||
Copyright (c) 2025 Barrett Ruth
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
48
Makefile
Normal file
48
Makefile
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
## help: print this help message
|
||||||
|
.PHONY: help
|
||||||
|
help:
|
||||||
|
@echo 'Usage:'
|
||||||
|
@sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /'
|
||||||
|
|
||||||
|
## all: lint and run tests
|
||||||
|
.PHONY: all
|
||||||
|
all: lint test
|
||||||
|
|
||||||
|
## test: run tests
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
./run_tests.sh
|
||||||
|
|
||||||
|
## lint: run linters and LuaLS typechecking
|
||||||
|
.PHONY: lint
|
||||||
|
lint: scripts/nvim-typecheck-action
|
||||||
|
./scripts/nvim-typecheck-action/typecheck.sh --workdir scripts/nvim-typecheck-action lua
|
||||||
|
selene --display-style quiet .
|
||||||
|
stylua --check lua tests
|
||||||
|
|
||||||
|
## profile: use LuaJIT profiler to profile the plugin
|
||||||
|
.PHONY: profile
|
||||||
|
profile: scripts/benchmark.nvim
|
||||||
|
nvim --clean -u perf/bootstrap.lua -c 'lua jit_profile()'
|
||||||
|
|
||||||
|
## flame_profile: create a trace in the chrome profiler format
|
||||||
|
.PHONY: flame_profile
|
||||||
|
flame_profile: scripts/benchmark.nvim
|
||||||
|
nvim --clean -u perf/bootstrap.lua -c 'lua flame_profile()'
|
||||||
|
|
||||||
|
## benchmark: benchmark performance opening directory with many files
|
||||||
|
.PHONY: benchmark
|
||||||
|
benchmark: scripts/benchmark.nvim
|
||||||
|
nvim --clean -u perf/bootstrap.lua -c 'lua benchmark()'
|
||||||
|
@cat perf/tmp/benchmark.txt
|
||||||
|
|
||||||
|
scripts/nvim-typecheck-action:
|
||||||
|
git clone https://github.com/stevearc/nvim-typecheck-action scripts/nvim-typecheck-action
|
||||||
|
|
||||||
|
scripts/benchmark.nvim:
|
||||||
|
git clone https://github.com/stevearc/benchmark.nvim scripts/benchmark.nvim
|
||||||
|
|
||||||
|
## clean: reset the repository to a clean state
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -rf scripts/nvim-typecheck-action .testenv perf/tmp profile.json
|
||||||
160
README.md
160
README.md
|
|
@ -1,105 +1,145 @@
|
||||||
# canola.nvim
|
# oil.nvim
|
||||||
|
|
||||||
A refined [oil.nvim](https://github.com/stevearc/oil.nvim) — edit your
|
A [vim-vinegar](https://github.com/tpope/vim-vinegar) like file explorer that lets you edit your filesystem like a normal Neovim buffer.
|
||||||
filesystem like a buffer, with bug fixes and community PRs that haven't landed
|
|
||||||
upstream.
|
|
||||||
|
|
||||||
[Upstream tracker](doc/upstream.md) — full PR and issue triage against
|
|
||||||
[oil.nvim](https://github.com/stevearc/oil.nvim)
|
|
||||||
|
|
||||||
https://user-images.githubusercontent.com/506791/209727111-6b4a11f4-634a-4efa-9461-80e9717cea94.mp4
|
https://user-images.githubusercontent.com/506791/209727111-6b4a11f4-634a-4efa-9461-80e9717cea94.mp4
|
||||||
|
|
||||||
## Features
|
This is a maintained fork of [stevearc/oil.nvim](https://github.com/stevearc/oil.nvim)
|
||||||
|
with cherry-picked upstream PRs and original bug fixes that haven't landed
|
||||||
|
upstream yet.
|
||||||
|
|
||||||
- Edit directory listings as normal buffers — mutations are derived by diffing
|
<details>
|
||||||
- Cross-directory move, copy, and rename across any adapter
|
<summary>Changes from upstream</summary>
|
||||||
- Adapters for local filesystem, SSH, S3, and OS trash
|
|
||||||
- File preview in split or floating window
|
### PRs
|
||||||
- Configurable columns (icon, size, permissions, timestamps)
|
|
||||||
- Executable file highlighting and filetype-aware icons
|
Upstream PRs cherry-picked or adapted into this fork.
|
||||||
- Floating window and split layouts
|
|
||||||
|
| PR | Description | Commit |
|
||||||
|
|---|---|---|
|
||||||
|
| [#495](https://github.com/stevearc/oil.nvim/pull/495) | Cancel visual/operator-pending mode on close instead of closing buffer | [`16f3d7b`](https://github.com/barrettruth/oil.nvim/commit/16f3d7b) |
|
||||||
|
| [#537](https://github.com/stevearc/oil.nvim/pull/537) | Configurable file and directory creation permissions (`new_file_mode`, `new_dir_mode`) | [`c6b4a7a`](https://github.com/barrettruth/oil.nvim/commit/c6b4a7a) |
|
||||||
|
| [#578](https://github.com/stevearc/oil.nvim/issues/578) | Recipe to disable hidden file dimming by relinking `Oil*Hidden` groups | [`38db6cf`](https://github.com/barrettruth/oil.nvim/commit/38db6cf) |
|
||||||
|
| [#618](https://github.com/stevearc/oil.nvim/pull/618) | Opt-in filetype detection for icons via `use_slow_filetype_detection` | [`ded1725`](https://github.com/barrettruth/oil.nvim/commit/ded1725) |
|
||||||
|
| [#644](https://github.com/stevearc/oil.nvim/pull/644) | Pass full entry to `is_hidden_file` and `is_always_hidden` callbacks | [`4ab4765`](https://github.com/barrettruth/oil.nvim/commit/4ab4765) |
|
||||||
|
| [#645](https://github.com/stevearc/oil.nvim/pull/645) | Add `close_float` action (close only floating oil windows) | [`f6bcdda`](https://github.com/barrettruth/oil.nvim/commit/f6bcdda) |
|
||||||
|
| [#690](https://github.com/stevearc/oil.nvim/pull/690) | Add `OilFileIcon` highlight group as fallback for unrecognized icons | [`ce64ae1`](https://github.com/barrettruth/oil.nvim/commit/ce64ae1) |
|
||||||
|
| [#697](https://github.com/stevearc/oil.nvim/pull/697) | Recipe for custom file extension column with sorting | [`dcb3a08`](https://github.com/barrettruth/oil.nvim/commit/dcb3a08) |
|
||||||
|
| [#698](https://github.com/stevearc/oil.nvim/pull/698) | Executable file highlighting (`OilExecutable`, `OilExecutableHidden`) | [`41556ec`](https://github.com/barrettruth/oil.nvim/commit/41556ec), [`85ed9b8`](https://github.com/barrettruth/oil.nvim/commit/85ed9b8) |
|
||||||
|
| [#717](https://github.com/stevearc/oil.nvim/pull/717) | Add malewicz1337/oil-git.nvim to third-party extensions | [`582d9fc`](https://github.com/barrettruth/oil.nvim/commit/582d9fc) |
|
||||||
|
| [#720](https://github.com/stevearc/oil.nvim/pull/720) | Gate `BufAdd` autocmd behind `default_file_explorer` check | [`2228f80`](https://github.com/barrettruth/oil.nvim/commit/2228f80) |
|
||||||
|
| [#722](https://github.com/stevearc/oil.nvim/pull/722) | Fix dead freedesktop trash specification URL | [`b92ecb0`](https://github.com/barrettruth/oil.nvim/commit/b92ecb0) |
|
||||||
|
| [#723](https://github.com/stevearc/oil.nvim/pull/723) | Emit `OilReadPost` user event after every buffer render | [`29239d5`](https://github.com/barrettruth/oil.nvim/commit/29239d5) |
|
||||||
|
| [#725](https://github.com/stevearc/oil.nvim/pull/725) | Normalize keymap keys before config merge (`<c-t>` = `<C-t>`) | [`723145c`](https://github.com/barrettruth/oil.nvim/commit/723145c) |
|
||||||
|
| [#727](https://github.com/stevearc/oil.nvim/pull/727) | Clarify `get_current_dir` nil return and add Telescope recipe | [`eed6697`](https://github.com/barrettruth/oil.nvim/commit/eed6697) |
|
||||||
|
|
||||||
|
### Issues
|
||||||
|
|
||||||
|
Upstream issues triaged against this fork.
|
||||||
|
|
||||||
|
| Issue | Status | Resolution |
|
||||||
|
|---|---|---|
|
||||||
|
| [#446](https://github.com/stevearc/oil.nvim/issues/446) | resolved | Executable highlighting — implemented by PR [#698](https://github.com/stevearc/oil.nvim/pull/698) |
|
||||||
|
| [#483](https://github.com/stevearc/oil.nvim/issues/483) | not actionable | Spell downloads depend on netrw — fixed in neovim ([neovim#34940](https://github.com/neovim/neovim/pull/34940)) |
|
||||||
|
| [#492](https://github.com/stevearc/oil.nvim/issues/492) | not actionable | Question — j/k remapping, answered in comments |
|
||||||
|
| [#533](https://github.com/stevearc/oil.nvim/issues/533) | not actionable | `constrain_cursor` — needs repro from reporter |
|
||||||
|
| [#587](https://github.com/stevearc/oil.nvim/issues/587) | not actionable | Alt+h keymap — user config issue |
|
||||||
|
| [#623](https://github.com/stevearc/oil.nvim/issues/623) | not actionable | bufferline.nvim interaction — cross-plugin issue |
|
||||||
|
| [#624](https://github.com/stevearc/oil.nvim/issues/624) | not actionable | Mutation-in-progress race — no reliable repro |
|
||||||
|
| [#632](https://github.com/stevearc/oil.nvim/issues/632) | fixed | Preview + move = copy — [`fe16993`](https://github.com/barrettruth/oil.nvim/commit/fe16993) |
|
||||||
|
| [#642](https://github.com/stevearc/oil.nvim/issues/642) | fixed | W10 warning under `nvim -R` — [`ca834cf`](https://github.com/barrettruth/oil.nvim/commit/ca834cf) |
|
||||||
|
| [#664](https://github.com/stevearc/oil.nvim/issues/664) | not actionable | Extra buffer on session reload — no repro |
|
||||||
|
| [#670](https://github.com/stevearc/oil.nvim/issues/670) | fixed | Multi-directory cmdline — [`70861e5`](https://github.com/barrettruth/oil.nvim/commit/70861e5) |
|
||||||
|
| [#673](https://github.com/stevearc/oil.nvim/issues/673) | fixed | Symlink newlines crash — [`9110a1a`](https://github.com/barrettruth/oil.nvim/commit/9110a1a) |
|
||||||
|
| [#679](https://github.com/stevearc/oil.nvim/issues/679) | resolved | Executable file sign — implemented by PR [#698](https://github.com/stevearc/oil.nvim/pull/698) |
|
||||||
|
| [#692](https://github.com/stevearc/oil.nvim/issues/692) | resolved | Keymap normalization — fixed by PR [#725](https://github.com/stevearc/oil.nvim/pull/725) |
|
||||||
|
| [#710](https://github.com/stevearc/oil.nvim/issues/710) | fixed | buftype empty on BufEnter — [`01b860e`](https://github.com/barrettruth/oil.nvim/commit/01b860e) |
|
||||||
|
| [#714](https://github.com/stevearc/oil.nvim/issues/714) | not actionable | Support question — already answered |
|
||||||
|
| [#719](https://github.com/stevearc/oil.nvim/issues/719) | not actionable | Neovim crash on node_modules delete — libuv/neovim bug |
|
||||||
|
| [#726](https://github.com/stevearc/oil.nvim/issues/726) | not actionable | Meta discussion/roadmap |
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Neovim 0.10+
|
Neovim 0.8+ and optionally [mini.icons](https://github.com/nvim-mini/mini.nvim/blob/main/readmes/mini-icons.md) or [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) for file icons.
|
||||||
- (Optionally) any of the following icon providers:
|
|
||||||
- [mini.icons](https://github.com/nvim-mini/mini.nvim/blob/main/readmes/mini-icons.md)
|
|
||||||
- [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons)
|
|
||||||
- [nonicons.nvim](https://github.com/barrettruth/nonicons.nvim)
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Install with your package manager of choice or via
|
Install with your favorite package manager or with luarocks:
|
||||||
[luarocks](https://luarocks.org/modules/barrettruth/canola.nvim):
|
|
||||||
|
|
||||||
```
|
```console
|
||||||
luarocks install canola.nvim
|
luarocks install oil.nvim
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
```vim
|
```vim
|
||||||
:help canola.nvim
|
:help oil.nvim
|
||||||
```
|
```
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
**Q: How do I migrate from `stevearc/oil.nvim`?**
|
**Q: How do I migrate from `stevearc/oil.nvim` to `barrettruth/oil.nvim`?**
|
||||||
|
|
||||||
Change the plugin source and replace `setup()` with `vim.g.oil` in `init`. The
|
Replace your `setup()` call with a `vim.g.oil` assignment. For example, with
|
||||||
configuration table is identical — only the entry point changes. For example,
|
[lazy.nvim](https://github.com/folke/lazy.nvim):
|
||||||
with [lazy.nvim](https://github.com/folke/lazy.nvim):
|
|
||||||
|
|
||||||
Before (`stevearc/oil.nvim`):
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
-- before
|
||||||
{
|
{
|
||||||
'stevearc/oil.nvim',
|
'stevearc/oil.nvim',
|
||||||
opts = { ... },
|
|
||||||
config = function(_, opts)
|
config = function(_, opts)
|
||||||
require('oil').setup(opts)
|
require('oil').setup(opts)
|
||||||
end,
|
end,
|
||||||
|
opts = {
|
||||||
|
...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
After (`barrettruth/canola.nvim`):
|
-- after
|
||||||
|
|
||||||
```lua
|
|
||||||
{
|
{
|
||||||
'barrettruth/canola.nvim',
|
'barrettruth/oil.nvim',
|
||||||
init = function()
|
init = function()
|
||||||
vim.g.oil = { ... }
|
vim.g.oil = {
|
||||||
|
columns = { "icon", "size" },
|
||||||
|
delete_to_trash = true,
|
||||||
|
}
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`init` runs before the plugin loads; `config` runs after. oil.nvim reads
|
|
||||||
`vim.g.oil` at load time, so `init` is the correct hook. Do not use `config`,
|
|
||||||
`opts`, or `lazy` — oil.nvim loads itself when you open a directory.
|
|
||||||
|
|
||||||
**Q: Why "oil"?**
|
**Q: Why "oil"?**
|
||||||
|
|
||||||
From the [vim-vinegar](https://github.com/tpope/vim-vinegar) README, a quote by
|
**A:** From the [vim-vinegar](https://github.com/tpope/vim-vinegar) README, a quote by Drew Neil:
|
||||||
Drew Neil:
|
|
||||||
|
|
||||||
> Split windows and the project drawer go together like oil and vinegar
|
> Split windows and the project drawer go together like oil and vinegar
|
||||||
|
|
||||||
|
Vinegar was taken. Let's be oil.
|
||||||
|
Plus, I think it's pretty slick ;)
|
||||||
|
|
||||||
|
**Q: Why would I want to use oil vs any other plugin?**
|
||||||
|
|
||||||
|
**A:**
|
||||||
|
|
||||||
|
- You like to use a netrw-like view to browse directories (as opposed to a file tree)
|
||||||
|
- AND you want to be able to edit your filesystem like a buffer
|
||||||
|
- AND you want to perform cross-directory actions. AFAIK there is no other plugin that does this. (update: [mini.files](https://github.com/nvim-mini/mini.nvim/blob/main/readmes/mini-files.md) also offers this functionality)
|
||||||
|
|
||||||
|
If you don't need those features specifically, check out the alternatives listed below
|
||||||
|
|
||||||
|
**Q: Can oil display files as a tree view?**
|
||||||
|
|
||||||
|
**A:** No. A tree view would require a completely different methodology, necessitating a complete rewrite.
|
||||||
|
|
||||||
**Q: What are some alternatives?**
|
**Q: What are some alternatives?**
|
||||||
|
|
||||||
- [stevearc/oil.nvim](https://github.com/stevearc/oil.nvim): the original
|
**A:**
|
||||||
- [mini.files](https://github.com/nvim-mini/mini.nvim/blob/main/readmes/mini-files.md):
|
|
||||||
cross-directory filesystem-as-buffer with a column view
|
|
||||||
- [vim-vinegar](https://github.com/tpope/vim-vinegar): the granddaddy of
|
|
||||||
single-directory file browsing
|
|
||||||
- [dirbuf.nvim](https://github.com/elihunter173/dirbuf.nvim): filesystem as
|
|
||||||
buffer without cross-directory edits
|
|
||||||
- [lir.nvim](https://github.com/tamago324/lir.nvim): vim-vinegar style with
|
|
||||||
Neovim integration
|
|
||||||
- [vim-dirvish](https://github.com/justinmk/vim-dirvish): stable, simple
|
|
||||||
directory browser
|
|
||||||
|
|
||||||
## Acknowledgements
|
- [the original](https://github.com/stevearc/oil.nvim): the lesser-maintained but
|
||||||
|
official `oil.nvim`
|
||||||
canola.nvim is a fork of [oil.nvim](https://github.com/stevearc/oil.nvim),
|
- [mini.files](https://github.com/nvim-mini/mini.nvim/blob/main/readmes/mini-files.md): Also supports cross-directory filesystem-as-buffer edits with a column view.
|
||||||
created by [Steven Arcangeli](https://github.com/stevearc). Maintained by
|
- [vim-vinegar](https://github.com/tpope/vim-vinegar): The granddaddy of single-directory file browsing.
|
||||||
[Barrett Ruth](https://github.com/barrettruth).
|
- [dirbuf.nvim](https://github.com/elihunter173/dirbuf.nvim): Edit filesystem like a buffer, but no cross-directory edits.
|
||||||
|
- [lir.nvim](https://github.com/tamago324/lir.nvim): Similar to vim-vinegar with better Neovim integration.
|
||||||
|
- [vim-dirvish](https://github.com/justinmk/vim-dirvish): Stable, simple directory browser.
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
rockspec_format = '3.0'
|
|
||||||
package = 'canola.nvim'
|
|
||||||
version = 'scm-1'
|
|
||||||
|
|
||||||
source = {
|
|
||||||
url = 'git+https://github.com/barrettruth/canola.nvim.git',
|
|
||||||
}
|
|
||||||
|
|
||||||
description = {
|
|
||||||
summary = 'Neovim file explorer: edit your filesystem like a buffer',
|
|
||||||
homepage = 'https://github.com/barrettruth/canola.nvim',
|
|
||||||
license = 'MIT',
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies = {
|
|
||||||
'lua >= 5.1',
|
|
||||||
}
|
|
||||||
|
|
||||||
test_dependencies = {
|
|
||||||
'nlua',
|
|
||||||
'busted >= 2.1.1',
|
|
||||||
}
|
|
||||||
|
|
||||||
test = {
|
|
||||||
type = 'busted',
|
|
||||||
}
|
|
||||||
|
|
||||||
build = {
|
|
||||||
type = 'builtin',
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
*canola.txt*
|
*oil.txt*
|
||||||
*Canola* *canola* *canola.nvim* *Oil* *oil* *oil.nvim*
|
*Oil* *oil* *oil.nvim*
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
CONTENTS *oil-contents*
|
CONTENTS *oil-contents*
|
||||||
|
|
||||||
165
doc/upstream.md
165
doc/upstream.md
|
|
@ -1,165 +0,0 @@
|
||||||
# Upstream Tracker
|
|
||||||
|
|
||||||
Triage of [stevearc/oil.nvim](https://github.com/stevearc/oil.nvim) PRs and
|
|
||||||
issues against this fork.
|
|
||||||
|
|
||||||
## Cherry-picked PRs
|
|
||||||
|
|
||||||
Upstream PRs cherry-picked or adapted into this fork.
|
|
||||||
|
|
||||||
| PR | Description | Commit |
|
|
||||||
| ----------------------------------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| [#495](https://github.com/stevearc/oil.nvim/pull/495) | Cancel visual/operator-pending mode on close | [`16f3d7b`](https://github.com/barrettruth/canola.nvim/commit/16f3d7b) |
|
|
||||||
| [#537](https://github.com/stevearc/oil.nvim/pull/537) | Configurable file/directory creation permissions | [`c6b4a7a`](https://github.com/barrettruth/canola.nvim/commit/c6b4a7a) |
|
|
||||||
| [#618](https://github.com/stevearc/oil.nvim/pull/618) | Opt-in filetype detection for icons | [`ded1725`](https://github.com/barrettruth/canola.nvim/commit/ded1725) |
|
|
||||||
| [#644](https://github.com/stevearc/oil.nvim/pull/644) | Pass entry to `is_hidden_file`/`is_always_hidden` | [`4ab4765`](https://github.com/barrettruth/canola.nvim/commit/4ab4765) |
|
|
||||||
| [#697](https://github.com/stevearc/oil.nvim/pull/697) | Recipe for file extension column | [`dcb3a08`](https://github.com/barrettruth/canola.nvim/commit/dcb3a08) |
|
|
||||||
| [#698](https://github.com/stevearc/oil.nvim/pull/698) | Executable file highlighting | [`41556ec`](https://github.com/barrettruth/canola.nvim/commit/41556ec), [`85ed9b8`](https://github.com/barrettruth/canola.nvim/commit/85ed9b8) |
|
|
||||||
| [#717](https://github.com/stevearc/oil.nvim/pull/717) | Add oil-git.nvim to extensions | [`582d9fc`](https://github.com/barrettruth/canola.nvim/commit/582d9fc) |
|
|
||||||
| [#720](https://github.com/stevearc/oil.nvim/pull/720) | Gate `BufAdd` autocmd behind config check | [`2228f80`](https://github.com/barrettruth/canola.nvim/commit/2228f80) |
|
|
||||||
| [#722](https://github.com/stevearc/oil.nvim/pull/722) | Fix freedesktop trash URL | [`b92ecb0`](https://github.com/barrettruth/canola.nvim/commit/b92ecb0) |
|
|
||||||
| [#723](https://github.com/stevearc/oil.nvim/pull/723) | Emit `OilReadPost` event after render | [`29239d5`](https://github.com/barrettruth/canola.nvim/commit/29239d5) |
|
|
||||||
| [#725](https://github.com/stevearc/oil.nvim/pull/725) | Normalize keymap keys before config merge | [`723145c`](https://github.com/barrettruth/canola.nvim/commit/723145c) |
|
|
||||||
| [#727](https://github.com/stevearc/oil.nvim/pull/727) | Clarify `get_current_dir` nil + Telescope recipe | [`eed6697`](https://github.com/barrettruth/canola.nvim/commit/eed6697) |
|
|
||||||
|
|
||||||
## Original fixes
|
|
||||||
|
|
||||||
Bugs fixed in this fork that remain open upstream.
|
|
||||||
|
|
||||||
| Upstream issue | Description | PR |
|
|
||||||
| ------------------------------------------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| [#632](https://github.com/stevearc/oil.nvim/issues/632) | Preview + move = copy | [#12](https://github.com/barrettruth/canola.nvim/pull/12) ([`fe16993`](https://github.com/barrettruth/canola.nvim/commit/fe16993)) |
|
|
||||||
| [#642](https://github.com/stevearc/oil.nvim/issues/642) | W10 warning under `nvim -R` | [`ca834cf`](https://github.com/barrettruth/canola.nvim/commit/ca834cf) |
|
|
||||||
| [#670](https://github.com/stevearc/oil.nvim/issues/670) | Multi-directory cmdline args ignored | [#11](https://github.com/barrettruth/canola.nvim/pull/11) ([`70861e5`](https://github.com/barrettruth/canola.nvim/commit/70861e5)) |
|
|
||||||
| [#673](https://github.com/stevearc/oil.nvim/issues/673) | Symlink newlines crash | [`9110a1a`](https://github.com/barrettruth/canola.nvim/commit/9110a1a) |
|
|
||||||
| [#710](https://github.com/stevearc/oil.nvim/issues/710) | buftype empty on BufEnter | [#10](https://github.com/barrettruth/canola.nvim/pull/10) ([`01b860e`](https://github.com/barrettruth/canola.nvim/commit/01b860e)) |
|
|
||||||
|
|
||||||
## Open upstream PRs
|
|
||||||
|
|
||||||
| PR | Description | Status |
|
|
||||||
| ----------------------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
||||||
| [#488](https://github.com/stevearc/oil.nvim/pull/488) | Parent directory in a split | not actionable — empty PR |
|
|
||||||
| [#493](https://github.com/stevearc/oil.nvim/pull/493) | UNC paths on Windows | not actionable — superseded by [#686](https://github.com/stevearc/oil.nvim/pull/686) |
|
|
||||||
| [#591](https://github.com/stevearc/oil.nvim/pull/591) | release-please changelog | not applicable |
|
|
||||||
| [#667](https://github.com/stevearc/oil.nvim/pull/667) | Virtual text columns + headers | deferred — WIP, conflicting |
|
|
||||||
| [#686](https://github.com/stevearc/oil.nvim/pull/686) | Windows path conversion fix | not actionable — Windows-only |
|
|
||||||
| [#708](https://github.com/stevearc/oil.nvim/pull/708) | Move file into new dir by renaming | deferred — needs rewrite |
|
|
||||||
| [#721](https://github.com/stevearc/oil.nvim/pull/721) | `create_hook` to populate file contents | open |
|
|
||||||
| [#728](https://github.com/stevearc/oil.nvim/pull/728) | `open_split` for opening oil in a split | tracked — [#2](https://github.com/barrettruth/canola.nvim/issues/2) |
|
|
||||||
| [#735](https://github.com/stevearc/oil.nvim/pull/735) | gX opens external program with a selection. | open |
|
|
||||||
|
|
||||||
## Upstream issues
|
|
||||||
|
|
||||||
**Status key:** `fixed` = original fix in fork, `resolved` = addressed by
|
|
||||||
cherry-picked PR, `not actionable` = can't/won't fix, `tracking` = known/not yet
|
|
||||||
addressed, `open` = not yet triaged.
|
|
||||||
|
|
||||||
| Issue | Status | Notes |
|
|
||||||
| ------------------------------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| [#85](https://github.com/stevearc/oil.nvim/issues/85) | open | Git status column (P2) |
|
|
||||||
| [#95](https://github.com/stevearc/oil.nvim/issues/95) | open | Undo after renaming files (P1) |
|
|
||||||
| [#117](https://github.com/stevearc/oil.nvim/issues/117) | open | Move file into new dir via slash in name (P1, related to [#708](https://github.com/stevearc/oil.nvim/pull/708)) |
|
|
||||||
| [#156](https://github.com/stevearc/oil.nvim/issues/156) | open | Paste path of files into oil buffer (P2) |
|
|
||||||
| [#200](https://github.com/stevearc/oil.nvim/issues/200) | open | Highlights not working when opening a file (P2) |
|
|
||||||
| [#207](https://github.com/stevearc/oil.nvim/issues/207) | open | Suppress "no longer available" message (P1) |
|
|
||||||
| [#210](https://github.com/stevearc/oil.nvim/issues/210) | open | FTP support (P2) |
|
|
||||||
| [#213](https://github.com/stevearc/oil.nvim/issues/213) | open | Disable preview for large files (P1) |
|
|
||||||
| [#226](https://github.com/stevearc/oil.nvim/issues/226) | open | K8s/Docker adapter (P2) |
|
|
||||||
| [#232](https://github.com/stevearc/oil.nvim/issues/232) | open | Cannot close last window (P2) |
|
|
||||||
| [#254](https://github.com/stevearc/oil.nvim/issues/254) | open | Buffer modified highlight group (P2) |
|
|
||||||
| [#263](https://github.com/stevearc/oil.nvim/issues/263) | open | Diff mode (P2) |
|
|
||||||
| [#276](https://github.com/stevearc/oil.nvim/issues/276) | open | Archives manipulation (P2) |
|
|
||||||
| [#280](https://github.com/stevearc/oil.nvim/issues/280) | open | vim-projectionist support (P2) |
|
|
||||||
| [#288](https://github.com/stevearc/oil.nvim/issues/288) | open | Oil failing to load (P2) |
|
|
||||||
| [#289](https://github.com/stevearc/oil.nvim/issues/289) | open | Show absolute path toggle (P2) |
|
|
||||||
| [#294](https://github.com/stevearc/oil.nvim/issues/294) | open | Can't handle emojis in filenames (P2) |
|
|
||||||
| [#298](https://github.com/stevearc/oil.nvim/issues/298) | open | Open float on neovim directory startup (P2) |
|
|
||||||
| [#302](https://github.com/stevearc/oil.nvim/issues/302) | open | C-o parent nav makes buffer buflisted (P0) |
|
|
||||||
| [#303](https://github.com/stevearc/oil.nvim/issues/303) | open | Preview in float window mode (P2) |
|
|
||||||
| [#325](https://github.com/stevearc/oil.nvim/issues/325) | open | oil-ssh error from command line (P0) |
|
|
||||||
| [#330](https://github.com/stevearc/oil.nvim/issues/330) | open | File opens in floating modal |
|
|
||||||
| [#332](https://github.com/stevearc/oil.nvim/issues/332) | open | Buffer not fixed to floating window (P2) |
|
|
||||||
| [#335](https://github.com/stevearc/oil.nvim/issues/335) | open | Disable editing outside root dir |
|
|
||||||
| [#349](https://github.com/stevearc/oil.nvim/issues/349) | open | Parent directory as column/vsplit (P2) |
|
|
||||||
| [#351](https://github.com/stevearc/oil.nvim/issues/351) | open | Paste deleted file from register |
|
|
||||||
| [#359](https://github.com/stevearc/oil.nvim/issues/359) | open | Parse error on filenames differing by space (P1) |
|
|
||||||
| [#360](https://github.com/stevearc/oil.nvim/issues/360) | open | Pick window to open file into |
|
|
||||||
| [#362](https://github.com/stevearc/oil.nvim/issues/362) | open | "Could not find oil adapter for scheme" error |
|
|
||||||
| [#363](https://github.com/stevearc/oil.nvim/issues/363) | open | `prompt_save_on_select_new_entry` uses wrong prompt |
|
|
||||||
| [#371](https://github.com/stevearc/oil.nvim/issues/371) | open | Constrain cursor in insert mode |
|
|
||||||
| [#373](https://github.com/stevearc/oil.nvim/issues/373) | open | Dir from quickfix with bqf/trouble broken (P1) |
|
|
||||||
| [#375](https://github.com/stevearc/oil.nvim/issues/375) | open | Highlights for file types and permissions (P2) |
|
|
||||||
| [#380](https://github.com/stevearc/oil.nvim/issues/380) | open | Show file in oil when editing hidden file |
|
|
||||||
| [#382](https://github.com/stevearc/oil.nvim/issues/382) | open | Relative path in window title (P2) |
|
|
||||||
| [#392](https://github.com/stevearc/oil.nvim/issues/392) | open | Option to skip delete prompt |
|
|
||||||
| [#393](https://github.com/stevearc/oil.nvim/issues/393) | open | Auto-save new buffer on entry |
|
|
||||||
| [#396](https://github.com/stevearc/oil.nvim/issues/396) | open | Customize preview content (P2) |
|
|
||||||
| [#399](https://github.com/stevearc/oil.nvim/issues/399) | open | Open file without closing Oil (P1) |
|
|
||||||
| [#404](https://github.com/stevearc/oil.nvim/issues/404) | not actionable | Restricted UNC paths — Windows-only (P2) |
|
|
||||||
| [#416](https://github.com/stevearc/oil.nvim/issues/416) | open | Cannot remap key to open split |
|
|
||||||
| [#431](https://github.com/stevearc/oil.nvim/issues/431) | open | More SSH adapter documentation |
|
|
||||||
| [#435](https://github.com/stevearc/oil.nvim/issues/435) | open | Error previewing with semantic tokens LSP |
|
|
||||||
| [#436](https://github.com/stevearc/oil.nvim/issues/436) | open | Owner and group columns (P2) |
|
|
||||||
| [#444](https://github.com/stevearc/oil.nvim/issues/444) | open | Opening behaviour customization |
|
|
||||||
| [#446](https://github.com/stevearc/oil.nvim/issues/446) | resolved | Executable highlighting — PR [#698](https://github.com/stevearc/oil.nvim/pull/698) |
|
|
||||||
| [#449](https://github.com/stevearc/oil.nvim/issues/449) | open | Renaming TypeScript files stopped working |
|
|
||||||
| [#450](https://github.com/stevearc/oil.nvim/issues/450) | open | Highlight opened file in directory listing |
|
|
||||||
| [#457](https://github.com/stevearc/oil.nvim/issues/457) | open | Custom column API |
|
|
||||||
| [#466](https://github.com/stevearc/oil.nvim/issues/466) | open | Select into window on right |
|
|
||||||
| [#473](https://github.com/stevearc/oil.nvim/issues/473) | open | Show all hidden files if dir only has hidden |
|
|
||||||
| [#479](https://github.com/stevearc/oil.nvim/issues/479) | open | Harpoon integration recipe |
|
|
||||||
| [#483](https://github.com/stevearc/oil.nvim/issues/483) | not actionable | Spell downloads depend on netrw — fixed in [neovim#34940](https://github.com/neovim/neovim/pull/34940) |
|
|
||||||
| [#486](https://github.com/stevearc/oil.nvim/issues/486) | open | All directory sizes show 4.1k |
|
|
||||||
| [#492](https://github.com/stevearc/oil.nvim/issues/492) | not actionable | j/k remapping question — answered in comments |
|
|
||||||
| [#507](https://github.com/stevearc/oil.nvim/issues/507) | open | lacasitos.nvim conflict (P1) |
|
|
||||||
| [#521](https://github.com/stevearc/oil.nvim/issues/521) | open | oil-ssh connection issues |
|
|
||||||
| [#525](https://github.com/stevearc/oil.nvim/issues/525) | open | SSH adapter documentation (P2) |
|
|
||||||
| [#531](https://github.com/stevearc/oil.nvim/issues/531) | not actionable | Windows — incomplete drive letters (P1) |
|
|
||||||
| [#533](https://github.com/stevearc/oil.nvim/issues/533) | not actionable | `constrain_cursor` — needs repro |
|
|
||||||
| [#570](https://github.com/stevearc/oil.nvim/issues/570) | open | Improve c0/d0 for renaming |
|
|
||||||
| [#571](https://github.com/stevearc/oil.nvim/issues/571) | open | Callback before `highlight_filename` (P2) |
|
|
||||||
| [#578](https://github.com/stevearc/oil.nvim/issues/578) | resolved | Hidden file dimming recipe — [`38db6cf`](https://github.com/barrettruth/canola.nvim/commit/38db6cf) |
|
|
||||||
| [#587](https://github.com/stevearc/oil.nvim/issues/587) | not actionable | Alt+h keymap — user config issue |
|
|
||||||
| [#599](https://github.com/stevearc/oil.nvim/issues/599) | open | user:group display and manipulation (P2) |
|
|
||||||
| [#607](https://github.com/stevearc/oil.nvim/issues/607) | open | Per-host SCP args (P2) |
|
|
||||||
| [#609](https://github.com/stevearc/oil.nvim/issues/609) | open | Cursor placement via Snacks picker |
|
|
||||||
| [#612](https://github.com/stevearc/oil.nvim/issues/612) | open | Delete buffers on file delete (P2) |
|
|
||||||
| [#615](https://github.com/stevearc/oil.nvim/issues/615) | open | Cursor at name column on o/O (P2) |
|
|
||||||
| [#617](https://github.com/stevearc/oil.nvim/issues/617) | open | Filetype by actual filetype (P2) |
|
|
||||||
| [#621](https://github.com/stevearc/oil.nvim/issues/621) | open | Toggle function for regular windows (P2) |
|
|
||||||
| [#623](https://github.com/stevearc/oil.nvim/issues/623) | not actionable | bufferline.nvim interaction — cross-plugin |
|
|
||||||
| [#624](https://github.com/stevearc/oil.nvim/issues/624) | not actionable | Mutation race — no reliable repro |
|
|
||||||
| [#625](https://github.com/stevearc/oil.nvim/issues/625) | not actionable | E19 mark invalid line — intractable without neovim API changes |
|
|
||||||
| [#632](https://github.com/stevearc/oil.nvim/issues/632) | fixed | Preview + move = copy — [#12](https://github.com/barrettruth/canola.nvim/pull/12) ([`fe16993`](https://github.com/barrettruth/canola.nvim/commit/fe16993)) |
|
|
||||||
| [#636](https://github.com/stevearc/oil.nvim/issues/636) | open | Telescope picker opens in active buffer |
|
|
||||||
| [#637](https://github.com/stevearc/oil.nvim/issues/637) | open | Inconsistent symlink resolution |
|
|
||||||
| [#641](https://github.com/stevearc/oil.nvim/issues/641) | open | Flicker on `actions.parent` |
|
|
||||||
| [#642](https://github.com/stevearc/oil.nvim/issues/642) | fixed | W10 warning under `nvim -R` — [`ca834cf`](https://github.com/barrettruth/canola.nvim/commit/ca834cf) |
|
|
||||||
| [#645](https://github.com/stevearc/oil.nvim/issues/645) | resolved | `close_float` action — [`f6bcdda`](https://github.com/barrettruth/canola.nvim/commit/f6bcdda) |
|
|
||||||
| [#646](https://github.com/stevearc/oil.nvim/issues/646) | open | `get_current_dir` nil on SSH |
|
|
||||||
| [#650](https://github.com/stevearc/oil.nvim/issues/650) | open | Emit LSP `workspace.fileOperations` events |
|
|
||||||
| [#655](https://github.com/stevearc/oil.nvim/issues/655) | open | File statistics as virtual text |
|
|
||||||
| [#659](https://github.com/stevearc/oil.nvim/issues/659) | open | Mark and diff files in buffer |
|
|
||||||
| [#664](https://github.com/stevearc/oil.nvim/issues/664) | not actionable | Session reload extra buffer — no repro |
|
|
||||||
| [#665](https://github.com/stevearc/oil.nvim/issues/665) | open | Hot load preview fast-scratch buffers |
|
|
||||||
| [#668](https://github.com/stevearc/oil.nvim/issues/668) | open | Custom yes/no confirmation |
|
|
||||||
| [#670](https://github.com/stevearc/oil.nvim/issues/670) | fixed | Multi-directory cmdline — [#11](https://github.com/barrettruth/canola.nvim/pull/11) ([`70861e5`](https://github.com/barrettruth/canola.nvim/commit/70861e5)) |
|
|
||||||
| [#671](https://github.com/stevearc/oil.nvim/issues/671) | open | Yanking between nvim instances |
|
|
||||||
| [#673](https://github.com/stevearc/oil.nvim/issues/673) | fixed | Symlink newlines crash — [`9110a1a`](https://github.com/barrettruth/canola.nvim/commit/9110a1a) |
|
|
||||||
| [#675](https://github.com/stevearc/oil.nvim/issues/675) | open | Move file into folder by renaming (related to [#708](https://github.com/stevearc/oil.nvim/pull/708)) |
|
|
||||||
| [#676](https://github.com/stevearc/oil.nvim/issues/676) | not actionable | Windows — path conversion |
|
|
||||||
| [#678](https://github.com/stevearc/oil.nvim/issues/678) | tracking | `buftype='acwrite'` causes `mksession` to skip oil windows |
|
|
||||||
| [#679](https://github.com/stevearc/oil.nvim/issues/679) | resolved | Executable file sign — PR [#698](https://github.com/stevearc/oil.nvim/pull/698) |
|
|
||||||
| [#682](https://github.com/stevearc/oil.nvim/issues/682) | open | `get_current_dir()` nil in non-telescope context |
|
|
||||||
| [#683](https://github.com/stevearc/oil.nvim/issues/683) | open | Path not shown in floating mode |
|
|
||||||
| [#684](https://github.com/stevearc/oil.nvim/issues/684) | open | User and group columns |
|
|
||||||
| [#685](https://github.com/stevearc/oil.nvim/issues/685) | open | Plain directory paths in buffer names |
|
|
||||||
| [#690](https://github.com/stevearc/oil.nvim/issues/690) | resolved | `OilFileIcon` highlight group — [`ce64ae1`](https://github.com/barrettruth/canola.nvim/commit/ce64ae1) |
|
|
||||||
| [#692](https://github.com/stevearc/oil.nvim/issues/692) | resolved | Keymap normalization — PR [#725](https://github.com/stevearc/oil.nvim/pull/725) |
|
|
||||||
| [#699](https://github.com/stevearc/oil.nvim/issues/699) | open | `select` blocks UI with slow FileType autocmd |
|
|
||||||
| [#707](https://github.com/stevearc/oil.nvim/issues/707) | open | Move file/dir into new dir by renaming (related to [#708](https://github.com/stevearc/oil.nvim/pull/708)) |
|
|
||||||
| [#710](https://github.com/stevearc/oil.nvim/issues/710) | fixed | buftype empty on BufEnter — [#10](https://github.com/barrettruth/canola.nvim/pull/10) ([`01b860e`](https://github.com/barrettruth/canola.nvim/commit/01b860e)) |
|
|
||||||
| [#714](https://github.com/stevearc/oil.nvim/issues/714) | not actionable | Support question — answered |
|
|
||||||
| [#719](https://github.com/stevearc/oil.nvim/issues/719) | not actionable | Neovim crash on node_modules — libuv/neovim bug |
|
|
||||||
| [#726](https://github.com/stevearc/oil.nvim/issues/726) | not actionable | Meta discussion/roadmap |
|
|
||||||
| [#736](https://github.com/stevearc/oil.nvim/issues/736) | open | feature request: make icons virtual text |
|
|
||||||
|
|
@ -13,22 +13,15 @@
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
forEachSystem =
|
forEachSystem = f: nixpkgs.lib.genAttrs (import systems) (system: f nixpkgs.legacyPackages.${system});
|
||||||
f: nixpkgs.lib.genAttrs (import systems) (system: f nixpkgs.legacyPackages.${system});
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
formatter = forEachSystem (pkgs: pkgs.nixfmt-tree);
|
|
||||||
|
|
||||||
devShells = forEachSystem (pkgs: {
|
devShells = forEachSystem (pkgs: {
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
packages = [
|
packages = [
|
||||||
pkgs.prettier
|
pkgs.prettier
|
||||||
pkgs.stylua
|
pkgs.stylua
|
||||||
pkgs.selene
|
pkgs.selene
|
||||||
(pkgs.luajit.withPackages (ps: [
|
|
||||||
ps.busted
|
|
||||||
ps.nlua
|
|
||||||
]))
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
local oil = require('oil')
|
local oil = require("oil")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
M.show_help = {
|
M.show_help = {
|
||||||
callback = function()
|
callback = function()
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
require('oil.keymap_util').show_help(config.keymaps)
|
require("oil.keymap_util").show_help(config.keymaps)
|
||||||
end,
|
end,
|
||||||
desc = 'Show default keymaps',
|
desc = "Show default keymaps",
|
||||||
}
|
}
|
||||||
|
|
||||||
M.select = {
|
M.select = {
|
||||||
desc = 'Open the entry under the cursor',
|
desc = "Open the entry under the cursor",
|
||||||
callback = function(opts)
|
callback = function(opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local callback = opts.callback
|
local callback = opts.callback
|
||||||
|
|
@ -21,30 +21,30 @@ M.select = {
|
||||||
end,
|
end,
|
||||||
parameters = {
|
parameters = {
|
||||||
vertical = {
|
vertical = {
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
desc = 'Open the buffer in a vertical split',
|
desc = "Open the buffer in a vertical split",
|
||||||
},
|
},
|
||||||
horizontal = {
|
horizontal = {
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
desc = 'Open the buffer in a horizontal split',
|
desc = "Open the buffer in a horizontal split",
|
||||||
},
|
},
|
||||||
split = {
|
split = {
|
||||||
type = '"aboveleft"|"belowright"|"topleft"|"botright"',
|
type = '"aboveleft"|"belowright"|"topleft"|"botright"',
|
||||||
desc = 'Split modifier',
|
desc = "Split modifier",
|
||||||
},
|
},
|
||||||
tab = {
|
tab = {
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
desc = 'Open the buffer in a new tab',
|
desc = "Open the buffer in a new tab",
|
||||||
},
|
},
|
||||||
close = {
|
close = {
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
desc = 'Close the original oil buffer once selection is made',
|
desc = "Close the original oil buffer once selection is made",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
M.select_vsplit = {
|
M.select_vsplit = {
|
||||||
desc = 'Open the entry under the cursor in a vertical split',
|
desc = "Open the entry under the cursor in a vertical split",
|
||||||
deprecated = true,
|
deprecated = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
oil.select({ vertical = true })
|
oil.select({ vertical = true })
|
||||||
|
|
@ -52,7 +52,7 @@ M.select_vsplit = {
|
||||||
}
|
}
|
||||||
|
|
||||||
M.select_split = {
|
M.select_split = {
|
||||||
desc = 'Open the entry under the cursor in a horizontal split',
|
desc = "Open the entry under the cursor in a horizontal split",
|
||||||
deprecated = true,
|
deprecated = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
oil.select({ horizontal = true })
|
oil.select({ horizontal = true })
|
||||||
|
|
@ -60,7 +60,7 @@ M.select_split = {
|
||||||
}
|
}
|
||||||
|
|
||||||
M.select_tab = {
|
M.select_tab = {
|
||||||
desc = 'Open the entry under the cursor in a new tab',
|
desc = "Open the entry under the cursor in a new tab",
|
||||||
deprecated = true,
|
deprecated = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
oil.select({ tab = true })
|
oil.select({ tab = true })
|
||||||
|
|
@ -68,25 +68,25 @@ M.select_tab = {
|
||||||
}
|
}
|
||||||
|
|
||||||
M.preview = {
|
M.preview = {
|
||||||
desc = 'Open the entry under the cursor in a preview window, or close the preview window if already open',
|
desc = "Open the entry under the cursor in a preview window, or close the preview window if already open",
|
||||||
parameters = {
|
parameters = {
|
||||||
vertical = {
|
vertical = {
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
desc = 'Open the buffer in a vertical split',
|
desc = "Open the buffer in a vertical split",
|
||||||
},
|
},
|
||||||
horizontal = {
|
horizontal = {
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
desc = 'Open the buffer in a horizontal split',
|
desc = "Open the buffer in a horizontal split",
|
||||||
},
|
},
|
||||||
split = {
|
split = {
|
||||||
type = '"aboveleft"|"belowright"|"topleft"|"botright"',
|
type = '"aboveleft"|"belowright"|"topleft"|"botright"',
|
||||||
desc = 'Split modifier',
|
desc = "Split modifier",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
callback = function(opts)
|
callback = function(opts)
|
||||||
local entry = oil.get_cursor_entry()
|
local entry = oil.get_cursor_entry()
|
||||||
if not entry then
|
if not entry then
|
||||||
vim.notify('Could not find entry under cursor', vim.log.levels.ERROR)
|
vim.notify("Could not find entry under cursor", vim.log.levels.ERROR)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local winid = util.get_preview_win()
|
local winid = util.get_preview_win()
|
||||||
|
|
@ -95,7 +95,7 @@ M.preview = {
|
||||||
if entry.id == cur_id then
|
if entry.id == cur_id then
|
||||||
vim.api.nvim_win_close(winid, true)
|
vim.api.nvim_win_close(winid, true)
|
||||||
if util.is_floating_win() then
|
if util.is_floating_win() then
|
||||||
local layout = require('oil.layout')
|
local layout = require("oil.layout")
|
||||||
local win_opts = layout.get_fullscreen_win_opts()
|
local win_opts = layout.get_fullscreen_win_opts()
|
||||||
vim.api.nvim_win_set_config(0, win_opts)
|
vim.api.nvim_win_set_config(0, win_opts)
|
||||||
end
|
end
|
||||||
|
|
@ -107,13 +107,13 @@ M.preview = {
|
||||||
}
|
}
|
||||||
|
|
||||||
M.preview_scroll_down = {
|
M.preview_scroll_down = {
|
||||||
desc = 'Scroll down in the preview window',
|
desc = "Scroll down in the preview window",
|
||||||
callback = function()
|
callback = function()
|
||||||
local winid = util.get_preview_win()
|
local winid = util.get_preview_win()
|
||||||
if winid then
|
if winid then
|
||||||
vim.api.nvim_win_call(winid, function()
|
vim.api.nvim_win_call(winid, function()
|
||||||
vim.cmd.normal({
|
vim.cmd.normal({
|
||||||
args = { vim.api.nvim_replace_termcodes('<C-d>', true, true, true) },
|
args = { vim.api.nvim_replace_termcodes("<C-d>", true, true, true) },
|
||||||
bang = true,
|
bang = true,
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
@ -122,13 +122,13 @@ M.preview_scroll_down = {
|
||||||
}
|
}
|
||||||
|
|
||||||
M.preview_scroll_up = {
|
M.preview_scroll_up = {
|
||||||
desc = 'Scroll up in the preview window',
|
desc = "Scroll up in the preview window",
|
||||||
callback = function()
|
callback = function()
|
||||||
local winid = util.get_preview_win()
|
local winid = util.get_preview_win()
|
||||||
if winid then
|
if winid then
|
||||||
vim.api.nvim_win_call(winid, function()
|
vim.api.nvim_win_call(winid, function()
|
||||||
vim.cmd.normal({
|
vim.cmd.normal({
|
||||||
args = { vim.api.nvim_replace_termcodes('<C-u>', true, true, true) },
|
args = { vim.api.nvim_replace_termcodes("<C-u>", true, true, true) },
|
||||||
bang = true,
|
bang = true,
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
@ -137,50 +137,50 @@ M.preview_scroll_up = {
|
||||||
}
|
}
|
||||||
|
|
||||||
M.preview_scroll_left = {
|
M.preview_scroll_left = {
|
||||||
desc = 'Scroll left in the preview window',
|
desc = "Scroll left in the preview window",
|
||||||
callback = function()
|
callback = function()
|
||||||
local winid = util.get_preview_win()
|
local winid = util.get_preview_win()
|
||||||
if winid then
|
if winid then
|
||||||
vim.api.nvim_win_call(winid, function()
|
vim.api.nvim_win_call(winid, function()
|
||||||
vim.cmd.normal({ 'zH', bang = true })
|
vim.cmd.normal({ "zH", bang = true })
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
M.preview_scroll_right = {
|
M.preview_scroll_right = {
|
||||||
desc = 'Scroll right in the preview window',
|
desc = "Scroll right in the preview window",
|
||||||
callback = function()
|
callback = function()
|
||||||
local winid = util.get_preview_win()
|
local winid = util.get_preview_win()
|
||||||
if winid then
|
if winid then
|
||||||
vim.api.nvim_win_call(winid, function()
|
vim.api.nvim_win_call(winid, function()
|
||||||
vim.cmd.normal({ 'zL', bang = true })
|
vim.cmd.normal({ "zL", bang = true })
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
M.parent = {
|
M.parent = {
|
||||||
desc = 'Navigate to the parent path',
|
desc = "Navigate to the parent path",
|
||||||
callback = oil.open,
|
callback = oil.open,
|
||||||
}
|
}
|
||||||
|
|
||||||
M.close = {
|
M.close = {
|
||||||
desc = 'Close oil and restore original buffer',
|
desc = "Close oil and restore original buffer",
|
||||||
callback = function(opts)
|
callback = function(opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
oil.close(opts)
|
oil.close(opts)
|
||||||
end,
|
end,
|
||||||
parameters = {
|
parameters = {
|
||||||
exit_if_last_buf = {
|
exit_if_last_buf = {
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
desc = 'Exit vim if oil is closed as the last buffer',
|
desc = "Exit vim if oil is closed as the last buffer",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
M.close_float = {
|
M.close_float = {
|
||||||
desc = 'Close oil if the window is floating, otherwise do nothing',
|
desc = "Close oil if the window is floating, otherwise do nothing",
|
||||||
callback = function(opts)
|
callback = function(opts)
|
||||||
if vim.w.is_oil_win then
|
if vim.w.is_oil_win then
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
|
|
@ -189,8 +189,8 @@ M.close_float = {
|
||||||
end,
|
end,
|
||||||
parameters = {
|
parameters = {
|
||||||
exit_if_last_buf = {
|
exit_if_last_buf = {
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
desc = 'Exit vim if oil is closed as the last buffer',
|
desc = "Exit vim if oil is closed as the last buffer",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -202,42 +202,42 @@ local function cd(cmd, silent)
|
||||||
if dir then
|
if dir then
|
||||||
vim.cmd({ cmd = cmd, args = { dir } })
|
vim.cmd({ cmd = cmd, args = { dir } })
|
||||||
if not silent then
|
if not silent then
|
||||||
vim.notify(string.format('CWD: %s', dir), vim.log.levels.INFO)
|
vim.notify(string.format("CWD: %s", dir), vim.log.levels.INFO)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
vim.notify('Cannot :cd; not in a directory', vim.log.levels.WARN)
|
vim.notify("Cannot :cd; not in a directory", vim.log.levels.WARN)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
M.cd = {
|
M.cd = {
|
||||||
desc = ':cd to the current oil directory',
|
desc = ":cd to the current oil directory",
|
||||||
callback = function(opts)
|
callback = function(opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local cmd = 'cd'
|
local cmd = "cd"
|
||||||
if opts.scope == 'tab' then
|
if opts.scope == "tab" then
|
||||||
cmd = 'tcd'
|
cmd = "tcd"
|
||||||
elseif opts.scope == 'win' then
|
elseif opts.scope == "win" then
|
||||||
cmd = 'lcd'
|
cmd = "lcd"
|
||||||
end
|
end
|
||||||
cd(cmd, opts.silent)
|
cd(cmd, opts.silent)
|
||||||
end,
|
end,
|
||||||
parameters = {
|
parameters = {
|
||||||
scope = {
|
scope = {
|
||||||
type = 'nil|"tab"|"win"',
|
type = 'nil|"tab"|"win"',
|
||||||
desc = 'Scope of the directory change (e.g. use |:tcd| or |:lcd|)',
|
desc = "Scope of the directory change (e.g. use |:tcd| or |:lcd|)",
|
||||||
},
|
},
|
||||||
silent = {
|
silent = {
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
desc = 'Do not show a message when changing directories',
|
desc = "Do not show a message when changing directories",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
M.tcd = {
|
M.tcd = {
|
||||||
desc = ':tcd to the current oil directory',
|
desc = ":tcd to the current oil directory",
|
||||||
deprecated = true,
|
deprecated = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
cd('tcd')
|
cd("tcd")
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,46 +249,46 @@ M.open_cwd = {
|
||||||
}
|
}
|
||||||
|
|
||||||
M.toggle_hidden = {
|
M.toggle_hidden = {
|
||||||
desc = 'Toggle hidden files and directories',
|
desc = "Toggle hidden files and directories",
|
||||||
callback = function()
|
callback = function()
|
||||||
require('oil.view').toggle_hidden()
|
require("oil.view").toggle_hidden()
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
M.open_terminal = {
|
M.open_terminal = {
|
||||||
desc = 'Open a terminal in the current directory',
|
desc = "Open a terminal in the current directory",
|
||||||
callback = function()
|
callback = function()
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local bufname = vim.api.nvim_buf_get_name(0)
|
local bufname = vim.api.nvim_buf_get_name(0)
|
||||||
local adapter = config.get_adapter_by_scheme(bufname)
|
local adapter = config.get_adapter_by_scheme(bufname)
|
||||||
if not adapter then
|
if not adapter then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if adapter.name == 'files' then
|
if adapter.name == "files" then
|
||||||
local dir = oil.get_current_dir()
|
local dir = oil.get_current_dir()
|
||||||
assert(dir, 'Oil buffer with files adapter must have current directory')
|
assert(dir, "Oil buffer with files adapter must have current directory")
|
||||||
local bufnr = vim.api.nvim_create_buf(false, true)
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
vim.api.nvim_set_current_buf(bufnr)
|
vim.api.nvim_set_current_buf(bufnr)
|
||||||
if vim.fn.has('nvim-0.11') == 1 then
|
if vim.fn.has("nvim-0.11") == 1 then
|
||||||
vim.fn.jobstart(vim.o.shell, { cwd = dir, term = true })
|
vim.fn.jobstart(vim.o.shell, { cwd = dir, term = true })
|
||||||
else
|
else
|
||||||
---@diagnostic disable-next-line: deprecated
|
---@diagnostic disable-next-line: deprecated
|
||||||
vim.fn.termopen(vim.o.shell, { cwd = dir })
|
vim.fn.termopen(vim.o.shell, { cwd = dir })
|
||||||
end
|
end
|
||||||
elseif adapter.name == 'ssh' then
|
elseif adapter.name == "ssh" then
|
||||||
local bufnr = vim.api.nvim_create_buf(false, true)
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
vim.api.nvim_set_current_buf(bufnr)
|
vim.api.nvim_set_current_buf(bufnr)
|
||||||
local url = require('oil.adapters.ssh').parse_url(bufname)
|
local url = require("oil.adapters.ssh").parse_url(bufname)
|
||||||
local cmd = require('oil.adapters.ssh.connection').create_ssh_command(url)
|
local cmd = require("oil.adapters.ssh.connection").create_ssh_command(url)
|
||||||
local term_id
|
local term_id
|
||||||
if vim.fn.has('nvim-0.11') == 1 then
|
if vim.fn.has("nvim-0.11") == 1 then
|
||||||
term_id = vim.fn.jobstart(cmd, { term = true })
|
term_id = vim.fn.jobstart(cmd, { term = true })
|
||||||
else
|
else
|
||||||
---@diagnostic disable-next-line: deprecated
|
---@diagnostic disable-next-line: deprecated
|
||||||
term_id = vim.fn.termopen(cmd)
|
term_id = vim.fn.termopen(cmd)
|
||||||
end
|
end
|
||||||
if term_id then
|
if term_id then
|
||||||
vim.api.nvim_chan_send(term_id, string.format('cd %s\n', url.path))
|
vim.api.nvim_chan_send(term_id, string.format("cd %s\n", url.path))
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
vim.notify(
|
vim.notify(
|
||||||
|
|
@ -304,25 +304,25 @@ M.open_terminal = {
|
||||||
---@return nil|string[] cmd
|
---@return nil|string[] cmd
|
||||||
---@return nil|string error
|
---@return nil|string error
|
||||||
local function get_open_cmd(path)
|
local function get_open_cmd(path)
|
||||||
if vim.fn.has('mac') == 1 then
|
if vim.fn.has("mac") == 1 then
|
||||||
return { 'open', path }
|
return { "open", path }
|
||||||
elseif vim.fn.has('win32') == 1 then
|
elseif vim.fn.has("win32") == 1 then
|
||||||
if vim.fn.executable('rundll32') == 1 then
|
if vim.fn.executable("rundll32") == 1 then
|
||||||
return { 'rundll32', 'url.dll,FileProtocolHandler', path }
|
return { "rundll32", "url.dll,FileProtocolHandler", path }
|
||||||
else
|
else
|
||||||
return nil, 'rundll32 not found'
|
return nil, "rundll32 not found"
|
||||||
end
|
end
|
||||||
elseif vim.fn.executable('explorer.exe') == 1 then
|
elseif vim.fn.executable("explorer.exe") == 1 then
|
||||||
return { 'explorer.exe', path }
|
return { "explorer.exe", path }
|
||||||
elseif vim.fn.executable('xdg-open') == 1 then
|
elseif vim.fn.executable("xdg-open") == 1 then
|
||||||
return { 'xdg-open', path }
|
return { "xdg-open", path }
|
||||||
else
|
else
|
||||||
return nil, 'no handler found'
|
return nil, "no handler found"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
M.open_external = {
|
M.open_external = {
|
||||||
desc = 'Open the entry under the cursor in an external program',
|
desc = "Open the entry under the cursor in an external program",
|
||||||
callback = function()
|
callback = function()
|
||||||
local entry = oil.get_cursor_entry()
|
local entry = oil.get_cursor_entry()
|
||||||
local dir = oil.get_current_dir()
|
local dir = oil.get_current_dir()
|
||||||
|
|
@ -338,20 +338,20 @@ M.open_external = {
|
||||||
|
|
||||||
local cmd, err = get_open_cmd(path)
|
local cmd, err = get_open_cmd(path)
|
||||||
if not cmd then
|
if not cmd then
|
||||||
vim.notify(string.format('Could not open %s: %s', path, err), vim.log.levels.ERROR)
|
vim.notify(string.format("Could not open %s: %s", path, err), vim.log.levels.ERROR)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local jid = vim.fn.jobstart(cmd, { detach = true })
|
local jid = vim.fn.jobstart(cmd, { detach = true })
|
||||||
assert(jid > 0, 'Failed to start job')
|
assert(jid > 0, "Failed to start job")
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
M.refresh = {
|
M.refresh = {
|
||||||
desc = 'Refresh current directory list',
|
desc = "Refresh current directory list",
|
||||||
callback = function(opts)
|
callback = function(opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
if vim.bo.modified and not opts.force then
|
if vim.bo.modified and not opts.force then
|
||||||
local ok, choice = pcall(vim.fn.confirm, 'Discard changes?', 'No\nYes')
|
local ok, choice = pcall(vim.fn.confirm, "Discard changes?", "No\nYes")
|
||||||
if not ok or choice ~= 2 then
|
if not ok or choice ~= 2 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -363,26 +363,26 @@ M.refresh = {
|
||||||
end,
|
end,
|
||||||
parameters = {
|
parameters = {
|
||||||
force = {
|
force = {
|
||||||
desc = 'When true, do not prompt user if they will be discarding changes',
|
desc = "When true, do not prompt user if they will be discarding changes",
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local function open_cmdline_with_path(path)
|
local function open_cmdline_with_path(path)
|
||||||
local escaped =
|
local escaped =
|
||||||
vim.api.nvim_replace_termcodes(': ' .. vim.fn.fnameescape(path) .. '<Home>', true, false, true)
|
vim.api.nvim_replace_termcodes(": " .. vim.fn.fnameescape(path) .. "<Home>", true, false, true)
|
||||||
vim.api.nvim_feedkeys(escaped, 'n', false)
|
vim.api.nvim_feedkeys(escaped, "n", false)
|
||||||
end
|
end
|
||||||
|
|
||||||
M.open_cmdline = {
|
M.open_cmdline = {
|
||||||
desc = 'Open vim cmdline with current entry as an argument',
|
desc = "Open vim cmdline with current entry as an argument",
|
||||||
callback = function(opts)
|
callback = function(opts)
|
||||||
opts = vim.tbl_deep_extend('keep', opts or {}, {
|
opts = vim.tbl_deep_extend("keep", opts or {}, {
|
||||||
shorten_path = true,
|
shorten_path = true,
|
||||||
})
|
})
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local entry = oil.get_cursor_entry()
|
local entry = oil.get_cursor_entry()
|
||||||
if not entry then
|
if not entry then
|
||||||
return
|
return
|
||||||
|
|
@ -393,7 +393,7 @@ M.open_cmdline = {
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local adapter = config.get_adapter_by_scheme(scheme)
|
local adapter = config.get_adapter_by_scheme(scheme)
|
||||||
if not adapter or not path or adapter.name ~= 'files' then
|
if not adapter or not path or adapter.name ~= "files" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local fullpath = fs.posix_to_os_path(path) .. entry.name
|
local fullpath = fs.posix_to_os_path(path) .. entry.name
|
||||||
|
|
@ -407,18 +407,18 @@ M.open_cmdline = {
|
||||||
end,
|
end,
|
||||||
parameters = {
|
parameters = {
|
||||||
modify = {
|
modify = {
|
||||||
desc = 'Modify the path with |fnamemodify()| using this as the mods argument',
|
desc = "Modify the path with |fnamemodify()| using this as the mods argument",
|
||||||
type = 'string',
|
type = "string",
|
||||||
},
|
},
|
||||||
shorten_path = {
|
shorten_path = {
|
||||||
desc = 'Use relative paths when possible',
|
desc = "Use relative paths when possible",
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
M.yank_entry = {
|
M.yank_entry = {
|
||||||
desc = 'Yank the filepath of the entry under the cursor to a register',
|
desc = "Yank the filepath of the entry under the cursor to a register",
|
||||||
callback = function(opts)
|
callback = function(opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local entry = oil.get_cursor_entry()
|
local entry = oil.get_cursor_entry()
|
||||||
|
|
@ -427,8 +427,8 @@ M.yank_entry = {
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local name = entry.name
|
local name = entry.name
|
||||||
if entry.type == 'directory' then
|
if entry.type == "directory" then
|
||||||
name = name .. '/'
|
name = name .. "/"
|
||||||
end
|
end
|
||||||
local path = dir .. name
|
local path = dir .. name
|
||||||
if opts.modify then
|
if opts.modify then
|
||||||
|
|
@ -438,14 +438,14 @@ M.yank_entry = {
|
||||||
end,
|
end,
|
||||||
parameters = {
|
parameters = {
|
||||||
modify = {
|
modify = {
|
||||||
desc = 'Modify the path with |fnamemodify()| using this as the mods argument',
|
desc = "Modify the path with |fnamemodify()| using this as the mods argument",
|
||||||
type = 'string',
|
type = "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
M.copy_entry_path = {
|
M.copy_entry_path = {
|
||||||
desc = 'Yank the filepath of the entry under the cursor to a register',
|
desc = "Yank the filepath of the entry under the cursor to a register",
|
||||||
deprecated = true,
|
deprecated = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
local entry = oil.get_cursor_entry()
|
local entry = oil.get_cursor_entry()
|
||||||
|
|
@ -458,7 +458,7 @@ M.copy_entry_path = {
|
||||||
}
|
}
|
||||||
|
|
||||||
M.copy_entry_filename = {
|
M.copy_entry_filename = {
|
||||||
desc = 'Yank the filename of the entry under the cursor to a register',
|
desc = "Yank the filename of the entry under the cursor to a register",
|
||||||
deprecated = true,
|
deprecated = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
local entry = oil.get_cursor_entry()
|
local entry = oil.get_cursor_entry()
|
||||||
|
|
@ -470,30 +470,30 @@ M.copy_entry_filename = {
|
||||||
}
|
}
|
||||||
|
|
||||||
M.copy_to_system_clipboard = {
|
M.copy_to_system_clipboard = {
|
||||||
desc = 'Copy the entry under the cursor to the system clipboard',
|
desc = "Copy the entry under the cursor to the system clipboard",
|
||||||
callback = function()
|
callback = function()
|
||||||
require('oil.clipboard').copy_to_system_clipboard()
|
require("oil.clipboard").copy_to_system_clipboard()
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
M.paste_from_system_clipboard = {
|
M.paste_from_system_clipboard = {
|
||||||
desc = 'Paste the system clipboard into the current oil directory',
|
desc = "Paste the system clipboard into the current oil directory",
|
||||||
callback = function(opts)
|
callback = function(opts)
|
||||||
require('oil.clipboard').paste_from_system_clipboard(opts and opts.delete_original)
|
require("oil.clipboard").paste_from_system_clipboard(opts and opts.delete_original)
|
||||||
end,
|
end,
|
||||||
parameters = {
|
parameters = {
|
||||||
delete_original = {
|
delete_original = {
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
desc = 'Delete the original file after copying',
|
desc = "Delete the original file after copying",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
M.open_cmdline_dir = {
|
M.open_cmdline_dir = {
|
||||||
desc = 'Open vim cmdline with current directory as an argument',
|
desc = "Open vim cmdline with current directory as an argument",
|
||||||
deprecated = true,
|
deprecated = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local dir = oil.get_current_dir()
|
local dir = oil.get_current_dir()
|
||||||
if dir then
|
if dir then
|
||||||
open_cmdline_with_path(fs.shorten_path(dir))
|
open_cmdline_with_path(fs.shorten_path(dir))
|
||||||
|
|
@ -502,7 +502,7 @@ M.open_cmdline_dir = {
|
||||||
}
|
}
|
||||||
|
|
||||||
M.change_sort = {
|
M.change_sort = {
|
||||||
desc = 'Change the sort order',
|
desc = "Change the sort order",
|
||||||
callback = function(opts)
|
callback = function(opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
|
|
||||||
|
|
@ -511,21 +511,21 @@ M.change_sort = {
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local sort_cols = { 'name', 'size', 'atime', 'mtime', 'ctime', 'birthtime' }
|
local sort_cols = { "name", "size", "atime", "mtime", "ctime", "birthtime" }
|
||||||
vim.ui.select(sort_cols, { prompt = 'Sort by', kind = 'oil_sort_col' }, function(col)
|
vim.ui.select(sort_cols, { prompt = "Sort by", kind = "oil_sort_col" }, function(col)
|
||||||
if not col then
|
if not col then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
vim.ui.select(
|
vim.ui.select(
|
||||||
{ 'ascending', 'descending' },
|
{ "ascending", "descending" },
|
||||||
{ prompt = 'Sort order', kind = 'oil_sort_order' },
|
{ prompt = "Sort order", kind = "oil_sort_order" },
|
||||||
function(order)
|
function(order)
|
||||||
if not order then
|
if not order then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
order = order == 'ascending' and 'asc' or 'desc'
|
order = order == "ascending" and "asc" or "desc"
|
||||||
oil.set_sort({
|
oil.set_sort({
|
||||||
{ 'type', 'asc' },
|
{ "type", "asc" },
|
||||||
{ col, order },
|
{ col, order },
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
@ -534,24 +534,24 @@ M.change_sort = {
|
||||||
end,
|
end,
|
||||||
parameters = {
|
parameters = {
|
||||||
sort = {
|
sort = {
|
||||||
type = 'oil.SortSpec[]',
|
type = "oil.SortSpec[]",
|
||||||
desc = 'List of columns plus direction (see |oil.set_sort|) instead of interactive selection',
|
desc = "List of columns plus direction (see |oil.set_sort|) instead of interactive selection",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
M.toggle_trash = {
|
M.toggle_trash = {
|
||||||
desc = 'Jump to and from the trash for the current directory',
|
desc = "Jump to and from the trash for the current directory",
|
||||||
callback = function()
|
callback = function()
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local bufname = vim.api.nvim_buf_get_name(0)
|
local bufname = vim.api.nvim_buf_get_name(0)
|
||||||
local scheme, path = util.parse_url(bufname)
|
local scheme, path = util.parse_url(bufname)
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
local url
|
local url
|
||||||
if scheme == 'oil://' then
|
if scheme == "oil://" then
|
||||||
url = 'oil-trash://' .. path
|
url = "oil-trash://" .. path
|
||||||
elseif scheme == 'oil-trash://' then
|
elseif scheme == "oil-trash://" then
|
||||||
url = 'oil://' .. path
|
url = "oil://" .. path
|
||||||
-- The non-linux trash implementations don't support per-directory trash,
|
-- The non-linux trash implementations don't support per-directory trash,
|
||||||
-- so jump back to the stored source buffer.
|
-- so jump back to the stored source buffer.
|
||||||
if not fs.is_linux then
|
if not fs.is_linux then
|
||||||
|
|
@ -561,7 +561,7 @@ M.toggle_trash = {
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
vim.notify('No trash found for buffer', vim.log.levels.WARN)
|
vim.notify("No trash found for buffer", vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
vim.cmd.edit({ args = { url } })
|
vim.cmd.edit({ args = { url } })
|
||||||
|
|
@ -570,11 +570,11 @@ M.toggle_trash = {
|
||||||
}
|
}
|
||||||
|
|
||||||
M.send_to_qflist = {
|
M.send_to_qflist = {
|
||||||
desc = 'Sends files in the current oil directory to the quickfix list, replacing the previous entries.',
|
desc = "Sends files in the current oil directory to the quickfix list, replacing the previous entries.",
|
||||||
callback = function(opts)
|
callback = function(opts)
|
||||||
opts = vim.tbl_deep_extend('keep', opts or {}, {
|
opts = vim.tbl_deep_extend("keep", opts or {}, {
|
||||||
target = 'qflist',
|
target = "qflist",
|
||||||
action = 'r',
|
action = "r",
|
||||||
only_matching_search = false,
|
only_matching_search = false,
|
||||||
})
|
})
|
||||||
util.send_to_quickfix({
|
util.send_to_quickfix({
|
||||||
|
|
@ -586,48 +586,48 @@ M.send_to_qflist = {
|
||||||
parameters = {
|
parameters = {
|
||||||
target = {
|
target = {
|
||||||
type = '"qflist"|"loclist"',
|
type = '"qflist"|"loclist"',
|
||||||
desc = 'The target list to send files to',
|
desc = "The target list to send files to",
|
||||||
},
|
},
|
||||||
action = {
|
action = {
|
||||||
type = '"r"|"a"',
|
type = '"r"|"a"',
|
||||||
desc = 'Replace or add to current quickfix list (see |setqflist-action|)',
|
desc = "Replace or add to current quickfix list (see |setqflist-action|)",
|
||||||
},
|
},
|
||||||
only_matching_search = {
|
only_matching_search = {
|
||||||
type = 'boolean',
|
type = "boolean",
|
||||||
desc = 'Whether to only add the files that matches the last search. This option only applies when search highlighting is active',
|
desc = "Whether to only add the files that matches the last search. This option only applies when search highlighting is active",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
M.add_to_qflist = {
|
M.add_to_qflist = {
|
||||||
desc = 'Adds files in the current oil directory to the quickfix list, keeping the previous entries.',
|
desc = "Adds files in the current oil directory to the quickfix list, keeping the previous entries.",
|
||||||
deprecated = true,
|
deprecated = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
util.send_to_quickfix({
|
util.send_to_quickfix({
|
||||||
target = 'qflist',
|
target = "qflist",
|
||||||
mode = 'a',
|
mode = "a",
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
M.send_to_loclist = {
|
M.send_to_loclist = {
|
||||||
desc = 'Sends files in the current oil directory to the location list, replacing the previous entries.',
|
desc = "Sends files in the current oil directory to the location list, replacing the previous entries.",
|
||||||
deprecated = true,
|
deprecated = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
util.send_to_quickfix({
|
util.send_to_quickfix({
|
||||||
target = 'loclist',
|
target = "loclist",
|
||||||
mode = 'r',
|
mode = "r",
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
M.add_to_loclist = {
|
M.add_to_loclist = {
|
||||||
desc = 'Adds files in the current oil directory to the location list, keeping the previous entries.',
|
desc = "Adds files in the current oil directory to the location list, keeping the previous entries.",
|
||||||
deprecated = true,
|
deprecated = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
util.send_to_quickfix({
|
util.send_to_quickfix({
|
||||||
target = 'loclist',
|
target = "loclist",
|
||||||
mode = 'a',
|
mode = "a",
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
@ -637,7 +637,7 @@ M.add_to_loclist = {
|
||||||
M._get_actions = function()
|
M._get_actions = function()
|
||||||
local ret = {}
|
local ret = {}
|
||||||
for name, action in pairs(M) do
|
for name, action in pairs(M) do
|
||||||
if type(action) == 'table' and action.desc then
|
if type(action) == "table" and action.desc then
|
||||||
table.insert(ret, {
|
table.insert(ret, {
|
||||||
name = name,
|
name = name,
|
||||||
desc = action.desc,
|
desc = action.desc,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
local cache = require('oil.cache')
|
local cache = require("oil.cache")
|
||||||
local columns = require('oil.columns')
|
local columns = require("oil.columns")
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local git = require('oil.git')
|
local git = require("oil.git")
|
||||||
local log = require('oil.log')
|
local log = require("oil.log")
|
||||||
local permissions = require('oil.adapters.files.permissions')
|
local permissions = require("oil.adapters.files.permissions")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local uv = vim.uv or vim.loop
|
local uv = vim.uv or vim.loop
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
@ -25,7 +25,7 @@ local function read_link_data(path, cb)
|
||||||
assert(link)
|
assert(link)
|
||||||
local stat_path = link
|
local stat_path = link
|
||||||
if not fs.is_absolute(link) then
|
if not fs.is_absolute(link) then
|
||||||
stat_path = fs.join(vim.fn.fnamemodify(path, ':h'), link)
|
stat_path = fs.join(vim.fn.fnamemodify(path, ":h"), link)
|
||||||
end
|
end
|
||||||
uv.fs_stat(stat_path, function(stat_err, stat)
|
uv.fs_stat(stat_path, function(stat_err, stat)
|
||||||
cb(nil, link, stat)
|
cb(nil, link, stat)
|
||||||
|
|
@ -43,7 +43,7 @@ end
|
||||||
---@return string
|
---@return string
|
||||||
M.to_short_os_path = function(path, entry_type)
|
M.to_short_os_path = function(path, entry_type)
|
||||||
local shortpath = fs.shorten_path(fs.posix_to_os_path(path))
|
local shortpath = fs.shorten_path(fs.posix_to_os_path(path))
|
||||||
if entry_type == 'directory' then
|
if entry_type == "directory" then
|
||||||
shortpath = util.addslash(shortpath, true)
|
shortpath = util.addslash(shortpath, true)
|
||||||
end
|
end
|
||||||
return shortpath
|
return shortpath
|
||||||
|
|
@ -61,13 +61,13 @@ file_columns.size = {
|
||||||
return columns.EMPTY
|
return columns.EMPTY
|
||||||
end
|
end
|
||||||
if stat.size >= 1e9 then
|
if stat.size >= 1e9 then
|
||||||
return string.format('%.1fG', stat.size / 1e9)
|
return string.format("%.1fG", stat.size / 1e9)
|
||||||
elseif stat.size >= 1e6 then
|
elseif stat.size >= 1e6 then
|
||||||
return string.format('%.1fM', stat.size / 1e6)
|
return string.format("%.1fM", stat.size / 1e6)
|
||||||
elseif stat.size >= 1e3 then
|
elseif stat.size >= 1e3 then
|
||||||
return string.format('%.1fk', stat.size / 1e3)
|
return string.format("%.1fk", stat.size / 1e3)
|
||||||
else
|
else
|
||||||
return string.format('%d', stat.size)
|
return string.format("%d", stat.size)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
@ -82,7 +82,7 @@ file_columns.size = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
parse = function(line, conf)
|
parse = function(line, conf)
|
||||||
return line:match('^(%d+%S*)%s+(.*)$')
|
return line:match("^(%d+%S*)%s+(.*)$")
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,7 +120,7 @@ if not fs.is_windows then
|
||||||
local _, path = util.parse_url(action.url)
|
local _, path = util.parse_url(action.url)
|
||||||
assert(path)
|
assert(path)
|
||||||
return string.format(
|
return string.format(
|
||||||
'CHMOD %s %s',
|
"CHMOD %s %s",
|
||||||
permissions.mode_to_octal_str(action.value),
|
permissions.mode_to_octal_str(action.value),
|
||||||
M.to_short_os_path(path, action.entry_type)
|
M.to_short_os_path(path, action.entry_type)
|
||||||
)
|
)
|
||||||
|
|
@ -147,10 +147,10 @@ end
|
||||||
local current_year
|
local current_year
|
||||||
-- Make sure we run this import-time effect in the main loop (mostly for tests)
|
-- Make sure we run this import-time effect in the main loop (mostly for tests)
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
current_year = vim.fn.strftime('%Y')
|
current_year = vim.fn.strftime("%Y")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
for _, time_key in ipairs({ 'ctime', 'mtime', 'atime', 'birthtime' }) do
|
for _, time_key in ipairs({ "ctime", "mtime", "atime", "birthtime" }) do
|
||||||
file_columns[time_key] = {
|
file_columns[time_key] = {
|
||||||
require_stat = true,
|
require_stat = true,
|
||||||
|
|
||||||
|
|
@ -165,11 +165,11 @@ for _, time_key in ipairs({ 'ctime', 'mtime', 'atime', 'birthtime' }) do
|
||||||
if fmt then
|
if fmt then
|
||||||
ret = vim.fn.strftime(fmt, stat[time_key].sec)
|
ret = vim.fn.strftime(fmt, stat[time_key].sec)
|
||||||
else
|
else
|
||||||
local year = vim.fn.strftime('%Y', stat[time_key].sec)
|
local year = vim.fn.strftime("%Y", stat[time_key].sec)
|
||||||
if year ~= current_year then
|
if year ~= current_year then
|
||||||
ret = vim.fn.strftime('%b %d %Y', stat[time_key].sec)
|
ret = vim.fn.strftime("%b %d %Y", stat[time_key].sec)
|
||||||
else
|
else
|
||||||
ret = vim.fn.strftime('%b %d %H:%M', stat[time_key].sec)
|
ret = vim.fn.strftime("%b %d %H:%M", stat[time_key].sec)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
|
|
@ -183,20 +183,20 @@ for _, time_key in ipairs({ 'ctime', 'mtime', 'atime', 'birthtime' }) do
|
||||||
-- and whitespace with a pattern that matches any amount of whitespace
|
-- and whitespace with a pattern that matches any amount of whitespace
|
||||||
-- e.g. "%b %d %Y" -> "%S+%s+%S+%s+%S+"
|
-- e.g. "%b %d %Y" -> "%S+%s+%S+%s+%S+"
|
||||||
pattern = fmt
|
pattern = fmt
|
||||||
:gsub('%%.', '%%S+')
|
:gsub("%%.", "%%S+")
|
||||||
:gsub('%s+', '%%s+')
|
:gsub("%s+", "%%s+")
|
||||||
-- escape `()[]` because those are special characters in Lua patterns
|
-- escape `()[]` because those are special characters in Lua patterns
|
||||||
:gsub(
|
:gsub(
|
||||||
'%(',
|
"%(",
|
||||||
'%%('
|
"%%("
|
||||||
)
|
)
|
||||||
:gsub('%)', '%%)')
|
:gsub("%)", "%%)")
|
||||||
:gsub('%[', '%%[')
|
:gsub("%[", "%%[")
|
||||||
:gsub('%]', '%%]')
|
:gsub("%]", "%%]")
|
||||||
else
|
else
|
||||||
pattern = '%S+%s+%d+%s+%d%d:?%d%d'
|
pattern = "%S+%s+%d+%s+%d%d:?%d%d"
|
||||||
end
|
end
|
||||||
return line:match('^(' .. pattern .. ')%s+(.+)$')
|
return line:match("^(" .. pattern .. ")%s+(.+)$")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_sort_value = function(entry)
|
get_sort_value = function(entry)
|
||||||
|
|
@ -238,17 +238,17 @@ M.normalize_url = function(url, callback)
|
||||||
assert(path)
|
assert(path)
|
||||||
|
|
||||||
if fs.is_windows then
|
if fs.is_windows then
|
||||||
if path == '/' then
|
if path == "/" then
|
||||||
return callback(url)
|
return callback(url)
|
||||||
else
|
else
|
||||||
local is_root_drive = path:match('^/%u$')
|
local is_root_drive = path:match("^/%u$")
|
||||||
if is_root_drive then
|
if is_root_drive then
|
||||||
return callback(url .. '/')
|
return callback(url .. "/")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(path), ':p')
|
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(path), ":p")
|
||||||
uv.fs_realpath(os_path, function(err, new_os_path)
|
uv.fs_realpath(os_path, function(err, new_os_path)
|
||||||
local realpath
|
local realpath
|
||||||
if fs.is_windows then
|
if fs.is_windows then
|
||||||
|
|
@ -264,8 +264,8 @@ M.normalize_url = function(url, callback)
|
||||||
vim.schedule_wrap(function(stat_err, stat)
|
vim.schedule_wrap(function(stat_err, stat)
|
||||||
local is_directory
|
local is_directory
|
||||||
if stat then
|
if stat then
|
||||||
is_directory = stat.type == 'directory'
|
is_directory = stat.type == "directory"
|
||||||
elseif vim.endswith(realpath, '/') or (fs.is_windows and vim.endswith(realpath, '\\')) then
|
elseif vim.endswith(realpath, "/") or (fs.is_windows and vim.endswith(realpath, "\\")) then
|
||||||
is_directory = true
|
is_directory = true
|
||||||
else
|
else
|
||||||
local filetype = vim.filetype.match({ filename = vim.fs.basename(realpath) })
|
local filetype = vim.filetype.match({ filename = vim.fs.basename(realpath) })
|
||||||
|
|
@ -276,7 +276,7 @@ M.normalize_url = function(url, callback)
|
||||||
local norm_path = util.addslash(fs.os_to_posix_path(realpath))
|
local norm_path = util.addslash(fs.os_to_posix_path(realpath))
|
||||||
callback(scheme .. norm_path)
|
callback(scheme .. norm_path)
|
||||||
else
|
else
|
||||||
callback(vim.fn.fnamemodify(realpath, ':.'))
|
callback(vim.fn.fnamemodify(realpath, ":."))
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
)
|
)
|
||||||
|
|
@ -292,11 +292,11 @@ M.get_entry_path = function(url, entry, cb)
|
||||||
local scheme, path = util.parse_url(parent_url)
|
local scheme, path = util.parse_url(parent_url)
|
||||||
M.normalize_url(scheme .. path .. entry.name, cb)
|
M.normalize_url(scheme .. path .. entry.name, cb)
|
||||||
else
|
else
|
||||||
if entry.type == 'directory' then
|
if entry.type == "directory" then
|
||||||
cb(url)
|
cb(url)
|
||||||
else
|
else
|
||||||
local _, path = util.parse_url(url)
|
local _, path = util.parse_url(url)
|
||||||
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(assert(path)), ':p')
|
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(assert(path)), ":p")
|
||||||
cb(os_path)
|
cb(os_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -321,10 +321,10 @@ local function fetch_entry_metadata(parent_dir, entry, require_stat, cb)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Make sure we always get fs_stat info for links
|
-- Make sure we always get fs_stat info for links
|
||||||
if entry[FIELD_TYPE] == 'link' then
|
if entry[FIELD_TYPE] == "link" then
|
||||||
read_link_data(entry_path, function(link_err, link, link_stat)
|
read_link_data(entry_path, function(link_err, link, link_stat)
|
||||||
if link_err then
|
if link_err then
|
||||||
log.warn('Error reading link data %s: %s', entry_path, link_err)
|
log.warn("Error reading link data %s: %s", entry_path, link_err)
|
||||||
return cb()
|
return cb()
|
||||||
end
|
end
|
||||||
meta.link = link
|
meta.link = link
|
||||||
|
|
@ -336,7 +336,7 @@ local function fetch_entry_metadata(parent_dir, entry, require_stat, cb)
|
||||||
-- The link is broken, so let's use the stat of the link itself
|
-- The link is broken, so let's use the stat of the link itself
|
||||||
uv.fs_lstat(entry_path, function(stat_err, stat)
|
uv.fs_lstat(entry_path, function(stat_err, stat)
|
||||||
if stat_err then
|
if stat_err then
|
||||||
log.warn('Error lstat link file %s: %s', entry_path, stat_err)
|
log.warn("Error lstat link file %s: %s", entry_path, stat_err)
|
||||||
return cb()
|
return cb()
|
||||||
end
|
end
|
||||||
meta.stat = stat
|
meta.stat = stat
|
||||||
|
|
@ -350,7 +350,7 @@ local function fetch_entry_metadata(parent_dir, entry, require_stat, cb)
|
||||||
elseif require_stat then
|
elseif require_stat then
|
||||||
uv.fs_stat(entry_path, function(stat_err, stat)
|
uv.fs_stat(entry_path, function(stat_err, stat)
|
||||||
if stat_err then
|
if stat_err then
|
||||||
log.warn('Error stat file %s: %s', entry_path, stat_err)
|
log.warn("Error stat file %s: %s", entry_path, stat_err)
|
||||||
return cb()
|
return cb()
|
||||||
end
|
end
|
||||||
assert(stat)
|
assert(stat)
|
||||||
|
|
@ -368,11 +368,11 @@ end
|
||||||
if fs.is_windows then
|
if fs.is_windows then
|
||||||
local old_fetch_metadata = fetch_entry_metadata
|
local old_fetch_metadata = fetch_entry_metadata
|
||||||
fetch_entry_metadata = function(parent_dir, entry, require_stat, cb)
|
fetch_entry_metadata = function(parent_dir, entry, require_stat, cb)
|
||||||
if entry[FIELD_TYPE] == 'link' then
|
if entry[FIELD_TYPE] == "link" then
|
||||||
local entry_path = fs.posix_to_os_path(parent_dir .. entry[FIELD_NAME])
|
local entry_path = fs.posix_to_os_path(parent_dir .. entry[FIELD_NAME])
|
||||||
uv.fs_lstat(entry_path, function(stat_err, stat)
|
uv.fs_lstat(entry_path, function(stat_err, stat)
|
||||||
if stat_err then
|
if stat_err then
|
||||||
log.warn('Error lstat link file %s: %s', entry_path, stat_err)
|
log.warn("Error lstat link file %s: %s", entry_path, stat_err)
|
||||||
return old_fetch_metadata(parent_dir, entry, require_stat, cb)
|
return old_fetch_metadata(parent_dir, entry, require_stat, cb)
|
||||||
end
|
end
|
||||||
assert(stat)
|
assert(stat)
|
||||||
|
|
@ -398,17 +398,17 @@ local function list_windows_drives(url, column_defs, cb)
|
||||||
local _, path = util.parse_url(url)
|
local _, path = util.parse_url(url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local require_stat = columns_require_stat(column_defs)
|
local require_stat = columns_require_stat(column_defs)
|
||||||
local stdout = ''
|
local stdout = ""
|
||||||
local jid = vim.fn.jobstart({ 'wmic', 'logicaldisk', 'get', 'name' }, {
|
local jid = vim.fn.jobstart({ "wmic", "logicaldisk", "get", "name" }, {
|
||||||
stdout_buffered = true,
|
stdout_buffered = true,
|
||||||
on_stdout = function(_, data)
|
on_stdout = function(_, data)
|
||||||
stdout = table.concat(data, '\n')
|
stdout = table.concat(data, "\n")
|
||||||
end,
|
end,
|
||||||
on_exit = function(_, code)
|
on_exit = function(_, code)
|
||||||
if code ~= 0 then
|
if code ~= 0 then
|
||||||
return cb('Error listing windows devices')
|
return cb("Error listing windows devices")
|
||||||
end
|
end
|
||||||
local lines = vim.split(stdout, '\n', { plain = true, trimempty = true })
|
local lines = vim.split(stdout, "\n", { plain = true, trimempty = true })
|
||||||
-- Remove the "Name" header
|
-- Remove the "Name" header
|
||||||
table.remove(lines, 1)
|
table.remove(lines, 1)
|
||||||
local internal_entries = {}
|
local internal_entries = {}
|
||||||
|
|
@ -421,12 +421,12 @@ local function list_windows_drives(url, column_defs, cb)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
for _, disk in ipairs(lines) do
|
for _, disk in ipairs(lines) do
|
||||||
if disk:match('^%s*$') then
|
if disk:match("^%s*$") then
|
||||||
-- Skip empty line
|
-- Skip empty line
|
||||||
complete_disk_cb()
|
complete_disk_cb()
|
||||||
else
|
else
|
||||||
disk = disk:gsub(':%s*$', '')
|
disk = disk:gsub(":%s*$", "")
|
||||||
local cache_entry = cache.create_entry(url, disk, 'directory')
|
local cache_entry = cache.create_entry(url, disk, "directory")
|
||||||
table.insert(internal_entries, cache_entry)
|
table.insert(internal_entries, cache_entry)
|
||||||
fetch_entry_metadata(path, cache_entry, require_stat, complete_disk_cb)
|
fetch_entry_metadata(path, cache_entry, require_stat, complete_disk_cb)
|
||||||
end
|
end
|
||||||
|
|
@ -434,7 +434,7 @@ local function list_windows_drives(url, column_defs, cb)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
if jid <= 0 then
|
if jid <= 0 then
|
||||||
cb('Could not list windows devices')
|
cb("Could not list windows devices")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -444,7 +444,7 @@ end
|
||||||
M.list = function(url, column_defs, cb)
|
M.list = function(url, column_defs, cb)
|
||||||
local _, path = util.parse_url(url)
|
local _, path = util.parse_url(url)
|
||||||
assert(path)
|
assert(path)
|
||||||
if fs.is_windows and path == '/' then
|
if fs.is_windows and path == "/" then
|
||||||
return list_windows_drives(url, column_defs, cb)
|
return list_windows_drives(url, column_defs, cb)
|
||||||
end
|
end
|
||||||
local dir = fs.posix_to_os_path(path)
|
local dir = fs.posix_to_os_path(path)
|
||||||
|
|
@ -453,7 +453,7 @@ M.list = function(url, column_defs, cb)
|
||||||
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
|
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
|
||||||
uv.fs_opendir(dir, function(open_err, fd)
|
uv.fs_opendir(dir, function(open_err, fd)
|
||||||
if open_err then
|
if open_err then
|
||||||
if open_err:match('^ENOENT: no such file or directory') then
|
if open_err:match("^ENOENT: no such file or directory") then
|
||||||
-- If the directory doesn't exist, treat the list as a success. We will be able to traverse
|
-- If the directory doesn't exist, treat the list as a success. We will be able to traverse
|
||||||
-- and edit a not-yet-existing directory.
|
-- and edit a not-yet-existing directory.
|
||||||
return cb()
|
return cb()
|
||||||
|
|
@ -505,7 +505,7 @@ M.is_modifiable = function(bufnr)
|
||||||
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
||||||
local _, path = util.parse_url(bufname)
|
local _, path = util.parse_url(bufname)
|
||||||
assert(path)
|
assert(path)
|
||||||
if fs.is_windows and path == '/' then
|
if fs.is_windows and path == "/" then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local dir = fs.posix_to_os_path(path)
|
local dir = fs.posix_to_os_path(path)
|
||||||
|
|
@ -515,30 +515,30 @@ M.is_modifiable = function(bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- fs_access can return nil, force boolean return
|
-- fs_access can return nil, force boolean return
|
||||||
return uv.fs_access(dir, 'W') == true
|
return uv.fs_access(dir, "W") == true
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@return string
|
---@return string
|
||||||
M.render_action = function(action)
|
M.render_action = function(action)
|
||||||
if action.type == 'create' then
|
if action.type == "create" then
|
||||||
local _, path = util.parse_url(action.url)
|
local _, path = util.parse_url(action.url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local ret = string.format('CREATE %s', M.to_short_os_path(path, action.entry_type))
|
local ret = string.format("CREATE %s", M.to_short_os_path(path, action.entry_type))
|
||||||
if action.link then
|
if action.link then
|
||||||
ret = ret .. ' -> ' .. fs.posix_to_os_path(action.link)
|
ret = ret .. " -> " .. fs.posix_to_os_path(action.link)
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
elseif action.type == 'delete' then
|
elseif action.type == "delete" then
|
||||||
local _, path = util.parse_url(action.url)
|
local _, path = util.parse_url(action.url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local short_path = M.to_short_os_path(path, action.entry_type)
|
local short_path = M.to_short_os_path(path, action.entry_type)
|
||||||
if config.delete_to_trash then
|
if config.delete_to_trash then
|
||||||
return string.format(' TRASH %s', short_path)
|
return string.format(" TRASH %s", short_path)
|
||||||
else
|
else
|
||||||
return string.format('DELETE %s', short_path)
|
return string.format("DELETE %s", short_path)
|
||||||
end
|
end
|
||||||
elseif action.type == 'move' or action.type == 'copy' then
|
elseif action.type == "move" or action.type == "copy" then
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if dest_adapter == M then
|
if dest_adapter == M then
|
||||||
local _, src_path = util.parse_url(action.src_url)
|
local _, src_path = util.parse_url(action.src_url)
|
||||||
|
|
@ -546,7 +546,7 @@ M.render_action = function(action)
|
||||||
local _, dest_path = util.parse_url(action.dest_url)
|
local _, dest_path = util.parse_url(action.dest_url)
|
||||||
assert(dest_path)
|
assert(dest_path)
|
||||||
return string.format(
|
return string.format(
|
||||||
' %s %s -> %s',
|
" %s %s -> %s",
|
||||||
action.type:upper(),
|
action.type:upper(),
|
||||||
M.to_short_os_path(src_path, action.entry_type),
|
M.to_short_os_path(src_path, action.entry_type),
|
||||||
M.to_short_os_path(dest_path, action.entry_type)
|
M.to_short_os_path(dest_path, action.entry_type)
|
||||||
|
|
@ -563,7 +563,7 @@ end
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@param cb fun(err: nil|string)
|
---@param cb fun(err: nil|string)
|
||||||
M.perform_action = function(action, cb)
|
M.perform_action = function(action, cb)
|
||||||
if action.type == 'create' then
|
if action.type == "create" then
|
||||||
local _, path = util.parse_url(action.url)
|
local _, path = util.parse_url(action.url)
|
||||||
assert(path)
|
assert(path)
|
||||||
path = fs.posix_to_os_path(path)
|
path = fs.posix_to_os_path(path)
|
||||||
|
|
@ -579,16 +579,16 @@ M.perform_action = function(action, cb)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
if action.entry_type == 'directory' then
|
if action.entry_type == "directory" then
|
||||||
uv.fs_mkdir(path, config.new_dir_mode, function(err)
|
uv.fs_mkdir(path, config.new_dir_mode, function(err)
|
||||||
-- Ignore if the directory already exists
|
-- Ignore if the directory already exists
|
||||||
if not err or err:match('^EEXIST:') then
|
if not err or err:match("^EEXIST:") then
|
||||||
cb()
|
cb()
|
||||||
else
|
else
|
||||||
cb(err)
|
cb(err)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
elseif action.entry_type == 'link' and action.link then
|
elseif action.entry_type == "link" and action.link then
|
||||||
local flags = nil
|
local flags = nil
|
||||||
local target = fs.posix_to_os_path(action.link)
|
local target = fs.posix_to_os_path(action.link)
|
||||||
if fs.is_windows then
|
if fs.is_windows then
|
||||||
|
|
@ -602,7 +602,7 @@ M.perform_action = function(action, cb)
|
||||||
else
|
else
|
||||||
fs.touch(path, config.new_file_mode, cb)
|
fs.touch(path, config.new_file_mode, cb)
|
||||||
end
|
end
|
||||||
elseif action.type == 'delete' then
|
elseif action.type == "delete" then
|
||||||
local _, path = util.parse_url(action.url)
|
local _, path = util.parse_url(action.url)
|
||||||
assert(path)
|
assert(path)
|
||||||
path = fs.posix_to_os_path(path)
|
path = fs.posix_to_os_path(path)
|
||||||
|
|
@ -619,11 +619,11 @@ M.perform_action = function(action, cb)
|
||||||
end
|
end
|
||||||
|
|
||||||
if config.delete_to_trash then
|
if config.delete_to_trash then
|
||||||
require('oil.adapters.trash').delete_to_trash(path, cb)
|
require("oil.adapters.trash").delete_to_trash(path, cb)
|
||||||
else
|
else
|
||||||
fs.recursive_delete(action.entry_type, path, cb)
|
fs.recursive_delete(action.entry_type, path, cb)
|
||||||
end
|
end
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if dest_adapter == M then
|
if dest_adapter == M then
|
||||||
local _, src_path = util.parse_url(action.src_url)
|
local _, src_path = util.parse_url(action.src_url)
|
||||||
|
|
@ -641,7 +641,7 @@ M.perform_action = function(action, cb)
|
||||||
-- We should never hit this because we don't implement supported_cross_adapter_actions
|
-- We should never hit this because we don't implement supported_cross_adapter_actions
|
||||||
cb("files adapter doesn't support cross-adapter move")
|
cb("files adapter doesn't support cross-adapter move")
|
||||||
end
|
end
|
||||||
elseif action.type == 'copy' then
|
elseif action.type == "copy" then
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if dest_adapter == M then
|
if dest_adapter == M then
|
||||||
local _, src_path = util.parse_url(action.src_url)
|
local _, src_path = util.parse_url(action.src_url)
|
||||||
|
|
@ -656,7 +656,7 @@ M.perform_action = function(action, cb)
|
||||||
cb("files adapter doesn't support cross-adapter copy")
|
cb("files adapter doesn't support cross-adapter copy")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
cb(string.format('Bad action type: %s', action.type))
|
cb(string.format("Bad action type: %s", action.type))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ local M = {}
|
||||||
---@param num integer
|
---@param num integer
|
||||||
---@return string
|
---@return string
|
||||||
local function perm_to_str(exe_modifier, num)
|
local function perm_to_str(exe_modifier, num)
|
||||||
local str = (bit.band(num, 4) ~= 0 and 'r' or '-') .. (bit.band(num, 2) ~= 0 and 'w' or '-')
|
local str = (bit.band(num, 4) ~= 0 and "r" or "-") .. (bit.band(num, 2) ~= 0 and "w" or "-")
|
||||||
if exe_modifier then
|
if exe_modifier then
|
||||||
if bit.band(num, 1) ~= 0 then
|
if bit.band(num, 1) ~= 0 then
|
||||||
return str .. exe_modifier
|
return str .. exe_modifier
|
||||||
|
|
@ -12,7 +12,7 @@ local function perm_to_str(exe_modifier, num)
|
||||||
return str .. exe_modifier:upper()
|
return str .. exe_modifier:upper()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return str .. (bit.band(num, 1) ~= 0 and 'x' or '-')
|
return str .. (bit.band(num, 1) ~= 0 and "x" or "-")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -20,9 +20,9 @@ end
|
||||||
---@return string
|
---@return string
|
||||||
M.mode_to_str = function(mode)
|
M.mode_to_str = function(mode)
|
||||||
local extra = bit.rshift(mode, 9)
|
local extra = bit.rshift(mode, 9)
|
||||||
return perm_to_str(bit.band(extra, 4) ~= 0 and 's', bit.rshift(mode, 6))
|
return perm_to_str(bit.band(extra, 4) ~= 0 and "s", bit.rshift(mode, 6))
|
||||||
.. perm_to_str(bit.band(extra, 2) ~= 0 and 's', bit.rshift(mode, 3))
|
.. perm_to_str(bit.band(extra, 2) ~= 0 and "s", bit.rshift(mode, 3))
|
||||||
.. perm_to_str(bit.band(extra, 1) ~= 0 and 't', mode)
|
.. perm_to_str(bit.band(extra, 1) ~= 0 and "t", mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param mode integer
|
---@param mode integer
|
||||||
|
|
@ -38,25 +38,25 @@ end
|
||||||
---@param str string String of 3 characters
|
---@param str string String of 3 characters
|
||||||
---@return nil|integer
|
---@return nil|integer
|
||||||
local function str_to_mode(str)
|
local function str_to_mode(str)
|
||||||
local r, w, x = unpack(vim.split(str, '', {}))
|
local r, w, x = unpack(vim.split(str, "", {}))
|
||||||
local mode = 0
|
local mode = 0
|
||||||
if r == 'r' then
|
if r == "r" then
|
||||||
mode = bit.bor(mode, 4)
|
mode = bit.bor(mode, 4)
|
||||||
elseif r ~= '-' then
|
elseif r ~= "-" then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
if w == 'w' then
|
if w == "w" then
|
||||||
mode = bit.bor(mode, 2)
|
mode = bit.bor(mode, 2)
|
||||||
elseif w ~= '-' then
|
elseif w ~= "-" then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
-- t means sticky and executable
|
-- t means sticky and executable
|
||||||
-- T means sticky, not executable
|
-- T means sticky, not executable
|
||||||
-- s means setuid/setgid and executable
|
-- s means setuid/setgid and executable
|
||||||
-- S means setuid/setgid and not executable
|
-- S means setuid/setgid and not executable
|
||||||
if x == 'x' or x == 't' or x == 's' then
|
if x == "x" or x == "t" or x == "s" then
|
||||||
mode = bit.bor(mode, 1)
|
mode = bit.bor(mode, 1)
|
||||||
elseif x ~= '-' and x ~= 'T' and x ~= 'S' then
|
elseif x ~= "-" and x ~= "T" and x ~= "S" then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
return mode
|
return mode
|
||||||
|
|
@ -67,13 +67,13 @@ end
|
||||||
local function parse_extra_bits(perm)
|
local function parse_extra_bits(perm)
|
||||||
perm = perm:lower()
|
perm = perm:lower()
|
||||||
local mode = 0
|
local mode = 0
|
||||||
if perm:sub(3, 3) == 's' then
|
if perm:sub(3, 3) == "s" then
|
||||||
mode = bit.bor(mode, 4)
|
mode = bit.bor(mode, 4)
|
||||||
end
|
end
|
||||||
if perm:sub(6, 6) == 's' then
|
if perm:sub(6, 6) == "s" then
|
||||||
mode = bit.bor(mode, 2)
|
mode = bit.bor(mode, 2)
|
||||||
end
|
end
|
||||||
if perm:sub(9, 9) == 't' then
|
if perm:sub(9, 9) == "t" then
|
||||||
mode = bit.bor(mode, 1)
|
mode = bit.bor(mode, 1)
|
||||||
end
|
end
|
||||||
return mode
|
return mode
|
||||||
|
|
@ -83,7 +83,7 @@ end
|
||||||
---@return nil|integer
|
---@return nil|integer
|
||||||
---@return nil|string
|
---@return nil|string
|
||||||
M.parse = function(line)
|
M.parse = function(line)
|
||||||
local strval, rem = line:match('^([r%-][w%-][xsS%-][r%-][w%-][xsS%-][r%-][w%-][xtT%-])%s*(.*)$')
|
local strval, rem = line:match("^([r%-][w%-][xsS%-][r%-][w%-][xsS%-][r%-][w%-][xtT%-])%s*(.*)$")
|
||||||
if not strval then
|
if not strval then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
local files = require('oil.adapters.files')
|
local files = require("oil.adapters.files")
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local loading = require('oil.loading')
|
local loading = require("oil.loading")
|
||||||
local pathutil = require('oil.pathutil')
|
local pathutil = require("oil.pathutil")
|
||||||
local s3fs = require('oil.adapters.s3.s3fs')
|
local s3fs = require("oil.adapters.s3.s3fs")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local FIELD_META = constants.FIELD_META
|
local FIELD_META = constants.FIELD_META
|
||||||
|
|
@ -21,11 +21,11 @@ M.parse_url = function(oil_url)
|
||||||
local scheme, url = util.parse_url(oil_url)
|
local scheme, url = util.parse_url(oil_url)
|
||||||
assert(scheme and url, string.format("Malformed input url '%s'", oil_url))
|
assert(scheme and url, string.format("Malformed input url '%s'", oil_url))
|
||||||
local ret = { scheme = scheme }
|
local ret = { scheme = scheme }
|
||||||
local bucket, path = url:match('^([^/]+)/?(.*)$')
|
local bucket, path = url:match("^([^/]+)/?(.*)$")
|
||||||
ret.bucket = bucket
|
ret.bucket = bucket
|
||||||
ret.path = path ~= '' and path or nil
|
ret.path = path ~= "" and path or nil
|
||||||
if not ret.bucket and ret.path then
|
if not ret.bucket and ret.path then
|
||||||
error(string.format('Parsing error for s3 url: %s', oil_url))
|
error(string.format("Parsing error for s3 url: %s", oil_url))
|
||||||
end
|
end
|
||||||
---@cast ret oil.s3Url
|
---@cast ret oil.s3Url
|
||||||
return ret
|
return ret
|
||||||
|
|
@ -36,43 +36,43 @@ end
|
||||||
local function url_to_str(url)
|
local function url_to_str(url)
|
||||||
local pieces = { url.scheme }
|
local pieces = { url.scheme }
|
||||||
if url.bucket then
|
if url.bucket then
|
||||||
assert(url.bucket ~= '')
|
assert(url.bucket ~= "")
|
||||||
table.insert(pieces, url.bucket)
|
table.insert(pieces, url.bucket)
|
||||||
table.insert(pieces, '/')
|
table.insert(pieces, "/")
|
||||||
end
|
end
|
||||||
if url.path then
|
if url.path then
|
||||||
assert(url.path ~= '')
|
assert(url.path ~= "")
|
||||||
table.insert(pieces, url.path)
|
table.insert(pieces, url.path)
|
||||||
end
|
end
|
||||||
return table.concat(pieces, '')
|
return table.concat(pieces, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param url oil.s3Url
|
---@param url oil.s3Url
|
||||||
---@param is_folder boolean
|
---@param is_folder boolean
|
||||||
---@return string
|
---@return string
|
||||||
local function url_to_s3(url, is_folder)
|
local function url_to_s3(url, is_folder)
|
||||||
local pieces = { 's3://' }
|
local pieces = { "s3://" }
|
||||||
if url.bucket then
|
if url.bucket then
|
||||||
assert(url.bucket ~= '')
|
assert(url.bucket ~= "")
|
||||||
table.insert(pieces, url.bucket)
|
table.insert(pieces, url.bucket)
|
||||||
table.insert(pieces, '/')
|
table.insert(pieces, "/")
|
||||||
end
|
end
|
||||||
if url.path then
|
if url.path then
|
||||||
assert(url.path ~= '')
|
assert(url.path ~= "")
|
||||||
table.insert(pieces, url.path)
|
table.insert(pieces, url.path)
|
||||||
if is_folder and not vim.endswith(url.path, '/') then
|
if is_folder and not vim.endswith(url.path, "/") then
|
||||||
table.insert(pieces, '/')
|
table.insert(pieces, "/")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return table.concat(pieces, '')
|
return table.concat(pieces, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param url oil.s3Url
|
---@param url oil.s3Url
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function is_bucket(url)
|
local function is_bucket(url)
|
||||||
assert(url.bucket and url.bucket ~= '')
|
assert(url.bucket and url.bucket ~= "")
|
||||||
if url.path then
|
if url.path then
|
||||||
assert(url.path ~= '')
|
assert(url.path ~= "")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
|
|
@ -83,20 +83,20 @@ s3_columns.size = {
|
||||||
render = function(entry, conf)
|
render = function(entry, conf)
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
if not meta or not meta.size then
|
if not meta or not meta.size then
|
||||||
return ''
|
return ""
|
||||||
elseif meta.size >= 1e9 then
|
elseif meta.size >= 1e9 then
|
||||||
return string.format('%.1fG', meta.size / 1e9)
|
return string.format("%.1fG", meta.size / 1e9)
|
||||||
elseif meta.size >= 1e6 then
|
elseif meta.size >= 1e6 then
|
||||||
return string.format('%.1fM', meta.size / 1e6)
|
return string.format("%.1fM", meta.size / 1e6)
|
||||||
elseif meta.size >= 1e3 then
|
elseif meta.size >= 1e3 then
|
||||||
return string.format('%.1fk', meta.size / 1e3)
|
return string.format("%.1fk", meta.size / 1e3)
|
||||||
else
|
else
|
||||||
return string.format('%d', meta.size)
|
return string.format("%d", meta.size)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
parse = function(line, conf)
|
parse = function(line, conf)
|
||||||
return line:match('^(%d+%S*)%s+(.*)$')
|
return line:match("^(%d+%S*)%s+(.*)$")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_sort_value = function(entry)
|
get_sort_value = function(entry)
|
||||||
|
|
@ -113,21 +113,21 @@ s3_columns.birthtime = {
|
||||||
render = function(entry, conf)
|
render = function(entry, conf)
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
if not meta or not meta.date then
|
if not meta or not meta.date then
|
||||||
return ''
|
return ""
|
||||||
else
|
else
|
||||||
return meta.date
|
return meta.date
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
parse = function(line, conf)
|
parse = function(line, conf)
|
||||||
return line:match('^(%d+%-%d+%-%d+%s%d+:%d+:%d+)%s+(.*)$')
|
return line:match("^(%d+%-%d+%-%d+%s%d+:%d+:%d+)%s+(.*)$")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_sort_value = function(entry)
|
get_sort_value = function(entry)
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
if meta and meta.date then
|
if meta and meta.date then
|
||||||
local year, month, day, hour, min, sec =
|
local year, month, day, hour, min, sec =
|
||||||
meta.date:match('^(%d+)%-(%d+)%-(%d+)%s(%d+):(%d+):(%d+)$')
|
meta.date:match("^(%d+)%-(%d+)%-(%d+)%s(%d+):(%d+):(%d+)$")
|
||||||
local time =
|
local time =
|
||||||
os.time({ year = year, month = month, day = day, hour = hour, min = min, sec = sec })
|
os.time({ year = year, month = month, day = day, hour = hour, min = min, sec = sec })
|
||||||
return time
|
return time
|
||||||
|
|
@ -148,9 +148,9 @@ end
|
||||||
M.get_parent = function(bufname)
|
M.get_parent = function(bufname)
|
||||||
local res = M.parse_url(bufname)
|
local res = M.parse_url(bufname)
|
||||||
if res.path then
|
if res.path then
|
||||||
assert(res.path ~= '')
|
assert(res.path ~= "")
|
||||||
local path = pathutil.parent(res.path)
|
local path = pathutil.parent(res.path)
|
||||||
res.path = path ~= '' and path or nil
|
res.path = path ~= "" and path or nil
|
||||||
else
|
else
|
||||||
res.bucket = nil
|
res.bucket = nil
|
||||||
end
|
end
|
||||||
|
|
@ -168,8 +168,8 @@ end
|
||||||
---@param column_defs string[]
|
---@param column_defs string[]
|
||||||
---@param callback fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun())
|
---@param callback fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun())
|
||||||
M.list = function(url, column_defs, callback)
|
M.list = function(url, column_defs, callback)
|
||||||
if vim.fn.executable('aws') ~= 1 then
|
if vim.fn.executable("aws") ~= 1 then
|
||||||
callback('`aws` is not executable. Can you run `aws s3 ls`?')
|
callback("`aws` is not executable. Can you run `aws s3 ls`?")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -187,16 +187,16 @@ end
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@return string
|
---@return string
|
||||||
M.render_action = function(action)
|
M.render_action = function(action)
|
||||||
local is_folder = action.entry_type == 'directory'
|
local is_folder = action.entry_type == "directory"
|
||||||
if action.type == 'create' then
|
if action.type == "create" then
|
||||||
local res = M.parse_url(action.url)
|
local res = M.parse_url(action.url)
|
||||||
local extra = is_bucket(res) and 'BUCKET ' or ''
|
local extra = is_bucket(res) and "BUCKET " or ""
|
||||||
return string.format('CREATE %s%s', extra, url_to_s3(res, is_folder))
|
return string.format("CREATE %s%s", extra, url_to_s3(res, is_folder))
|
||||||
elseif action.type == 'delete' then
|
elseif action.type == "delete" then
|
||||||
local res = M.parse_url(action.url)
|
local res = M.parse_url(action.url)
|
||||||
local extra = is_bucket(res) and 'BUCKET ' or ''
|
local extra = is_bucket(res) and "BUCKET " or ""
|
||||||
return string.format('DELETE %s%s', extra, url_to_s3(res, is_folder))
|
return string.format("DELETE %s%s", extra, url_to_s3(res, is_folder))
|
||||||
elseif action.type == 'move' or action.type == 'copy' then
|
elseif action.type == "move" or action.type == "copy" then
|
||||||
local src = action.src_url
|
local src = action.src_url
|
||||||
local dest = action.dest_url
|
local dest = action.dest_url
|
||||||
if config.get_adapter_by_scheme(src) ~= M then
|
if config.get_adapter_by_scheme(src) ~= M then
|
||||||
|
|
@ -210,7 +210,7 @@ M.render_action = function(action)
|
||||||
dest = files.to_short_os_path(path, action.entry_type)
|
dest = files.to_short_os_path(path, action.entry_type)
|
||||||
src = url_to_s3(M.parse_url(src), is_folder)
|
src = url_to_s3(M.parse_url(src), is_folder)
|
||||||
end
|
end
|
||||||
return string.format(' %s %s -> %s', action.type:upper(), src, dest)
|
return string.format(" %s %s -> %s", action.type:upper(), src, dest)
|
||||||
else
|
else
|
||||||
error(string.format("Bad action type: '%s'", action.type))
|
error(string.format("Bad action type: '%s'", action.type))
|
||||||
end
|
end
|
||||||
|
|
@ -219,30 +219,30 @@ end
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@param cb fun(err: nil|string)
|
---@param cb fun(err: nil|string)
|
||||||
M.perform_action = function(action, cb)
|
M.perform_action = function(action, cb)
|
||||||
local is_folder = action.entry_type == 'directory'
|
local is_folder = action.entry_type == "directory"
|
||||||
if action.type == 'create' then
|
if action.type == "create" then
|
||||||
local res = M.parse_url(action.url)
|
local res = M.parse_url(action.url)
|
||||||
local bucket = is_bucket(res)
|
local bucket = is_bucket(res)
|
||||||
|
|
||||||
if action.entry_type == 'directory' and bucket then
|
if action.entry_type == "directory" and bucket then
|
||||||
s3fs.mb(url_to_s3(res, true), cb)
|
s3fs.mb(url_to_s3(res, true), cb)
|
||||||
elseif action.entry_type == 'directory' or action.entry_type == 'file' then
|
elseif action.entry_type == "directory" or action.entry_type == "file" then
|
||||||
s3fs.touch(url_to_s3(res, is_folder), cb)
|
s3fs.touch(url_to_s3(res, is_folder), cb)
|
||||||
else
|
else
|
||||||
cb(string.format('Bad entry type on s3 create action: %s', action.entry_type))
|
cb(string.format("Bad entry type on s3 create action: %s", action.entry_type))
|
||||||
end
|
end
|
||||||
elseif action.type == 'delete' then
|
elseif action.type == "delete" then
|
||||||
local res = M.parse_url(action.url)
|
local res = M.parse_url(action.url)
|
||||||
local bucket = is_bucket(res)
|
local bucket = is_bucket(res)
|
||||||
|
|
||||||
if action.entry_type == 'directory' and bucket then
|
if action.entry_type == "directory" and bucket then
|
||||||
s3fs.rb(url_to_s3(res, true), cb)
|
s3fs.rb(url_to_s3(res, true), cb)
|
||||||
elseif action.entry_type == 'directory' or action.entry_type == 'file' then
|
elseif action.entry_type == "directory" or action.entry_type == "file" then
|
||||||
s3fs.rm(url_to_s3(res, is_folder), is_folder, cb)
|
s3fs.rm(url_to_s3(res, is_folder), is_folder, cb)
|
||||||
else
|
else
|
||||||
cb(string.format('Bad entry type on s3 delete action: %s', action.entry_type))
|
cb(string.format("Bad entry type on s3 delete action: %s", action.entry_type))
|
||||||
end
|
end
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if
|
if
|
||||||
|
|
@ -250,7 +250,7 @@ M.perform_action = function(action, cb)
|
||||||
then
|
then
|
||||||
cb(
|
cb(
|
||||||
string.format(
|
string.format(
|
||||||
'We should never attempt to move from the %s adapter to the %s adapter.',
|
"We should never attempt to move from the %s adapter to the %s adapter.",
|
||||||
src_adapter.name,
|
src_adapter.name,
|
||||||
dest_adapter.name
|
dest_adapter.name
|
||||||
)
|
)
|
||||||
|
|
@ -276,7 +276,7 @@ M.perform_action = function(action, cb)
|
||||||
assert(dest)
|
assert(dest)
|
||||||
|
|
||||||
s3fs.mv(src, dest, is_folder, cb)
|
s3fs.mv(src, dest, is_folder, cb)
|
||||||
elseif action.type == 'copy' then
|
elseif action.type == "copy" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if
|
if
|
||||||
|
|
@ -284,7 +284,7 @@ M.perform_action = function(action, cb)
|
||||||
then
|
then
|
||||||
cb(
|
cb(
|
||||||
string.format(
|
string.format(
|
||||||
'We should never attempt to copy from the %s adapter to the %s adapter.',
|
"We should never attempt to copy from the %s adapter to the %s adapter.",
|
||||||
src_adapter.name,
|
src_adapter.name,
|
||||||
dest_adapter.name
|
dest_adapter.name
|
||||||
)
|
)
|
||||||
|
|
@ -311,11 +311,11 @@ M.perform_action = function(action, cb)
|
||||||
|
|
||||||
s3fs.cp(src, dest, is_folder, cb)
|
s3fs.cp(src, dest, is_folder, cb)
|
||||||
else
|
else
|
||||||
cb(string.format('Bad action type: %s', action.type))
|
cb(string.format("Bad action type: %s", action.type))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
M.supported_cross_adapter_actions = { files = 'move' }
|
M.supported_cross_adapter_actions = { files = "move" }
|
||||||
|
|
||||||
---@param bufnr integer
|
---@param bufnr integer
|
||||||
M.read_file = function(bufnr)
|
M.read_file = function(bufnr)
|
||||||
|
|
@ -323,11 +323,11 @@ M.read_file = function(bufnr)
|
||||||
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
||||||
local url = M.parse_url(bufname)
|
local url = M.parse_url(bufname)
|
||||||
local basename = pathutil.basename(bufname)
|
local basename = pathutil.basename(bufname)
|
||||||
local cache_dir = vim.fn.stdpath('cache')
|
local cache_dir = vim.fn.stdpath("cache")
|
||||||
assert(type(cache_dir) == 'string')
|
assert(type(cache_dir) == "string")
|
||||||
local tmpdir = fs.join(cache_dir, 'oil')
|
local tmpdir = fs.join(cache_dir, "oil")
|
||||||
fs.mkdirp(tmpdir)
|
fs.mkdirp(tmpdir)
|
||||||
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, 's3_XXXXXX'))
|
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, "s3_XXXXXX"))
|
||||||
if fd then
|
if fd then
|
||||||
vim.loop.fs_close(fd)
|
vim.loop.fs_close(fd)
|
||||||
end
|
end
|
||||||
|
|
@ -336,9 +336,9 @@ M.read_file = function(bufnr)
|
||||||
s3fs.cp(url_to_s3(url, false), tmpfile, false, function(err)
|
s3fs.cp(url_to_s3(url, false), tmpfile, false, function(err)
|
||||||
loading.set_loading(bufnr, false)
|
loading.set_loading(bufnr, false)
|
||||||
vim.bo[bufnr].modifiable = true
|
vim.bo[bufnr].modifiable = true
|
||||||
vim.cmd.doautocmd({ args = { 'BufReadPre', bufname }, mods = { silent = true } })
|
vim.cmd.doautocmd({ args = { "BufReadPre", bufname }, mods = { silent = true } })
|
||||||
if err then
|
if err then
|
||||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, vim.split(err, '\n'))
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, vim.split(err, "\n"))
|
||||||
else
|
else
|
||||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {})
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {})
|
||||||
vim.api.nvim_buf_call(bufnr, function()
|
vim.api.nvim_buf_call(bufnr, function()
|
||||||
|
|
@ -352,7 +352,7 @@ M.read_file = function(bufnr)
|
||||||
if filetype then
|
if filetype then
|
||||||
vim.bo[bufnr].filetype = filetype
|
vim.bo[bufnr].filetype = filetype
|
||||||
end
|
end
|
||||||
vim.cmd.doautocmd({ args = { 'BufReadPost', bufname }, mods = { silent = true } })
|
vim.cmd.doautocmd({ args = { "BufReadPost", bufname }, mods = { silent = true } })
|
||||||
vim.api.nvim_buf_delete(tmp_bufnr, { force = true })
|
vim.api.nvim_buf_delete(tmp_bufnr, { force = true })
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
@ -361,14 +361,14 @@ end
|
||||||
M.write_file = function(bufnr)
|
M.write_file = function(bufnr)
|
||||||
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
||||||
local url = M.parse_url(bufname)
|
local url = M.parse_url(bufname)
|
||||||
local cache_dir = vim.fn.stdpath('cache')
|
local cache_dir = vim.fn.stdpath("cache")
|
||||||
assert(type(cache_dir) == 'string')
|
assert(type(cache_dir) == "string")
|
||||||
local tmpdir = fs.join(cache_dir, 'oil')
|
local tmpdir = fs.join(cache_dir, "oil")
|
||||||
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, 's3_XXXXXXXX'))
|
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, "s3_XXXXXXXX"))
|
||||||
if fd then
|
if fd then
|
||||||
vim.loop.fs_close(fd)
|
vim.loop.fs_close(fd)
|
||||||
end
|
end
|
||||||
vim.cmd.doautocmd({ args = { 'BufWritePre', bufname }, mods = { silent = true } })
|
vim.cmd.doautocmd({ args = { "BufWritePre", bufname }, mods = { silent = true } })
|
||||||
vim.bo[bufnr].modifiable = false
|
vim.bo[bufnr].modifiable = false
|
||||||
vim.cmd.write({ args = { tmpfile }, bang = true, mods = { silent = true, noautocmd = true } })
|
vim.cmd.write({ args = { tmpfile }, bang = true, mods = { silent = true, noautocmd = true } })
|
||||||
local tmp_bufnr = vim.fn.bufadd(tmpfile)
|
local tmp_bufnr = vim.fn.bufadd(tmpfile)
|
||||||
|
|
@ -376,10 +376,10 @@ M.write_file = function(bufnr)
|
||||||
s3fs.cp(tmpfile, url_to_s3(url, false), false, function(err)
|
s3fs.cp(tmpfile, url_to_s3(url, false), false, function(err)
|
||||||
vim.bo[bufnr].modifiable = true
|
vim.bo[bufnr].modifiable = true
|
||||||
if err then
|
if err then
|
||||||
vim.notify(string.format('Error writing file: %s', err), vim.log.levels.ERROR)
|
vim.notify(string.format("Error writing file: %s", err), vim.log.levels.ERROR)
|
||||||
else
|
else
|
||||||
vim.bo[bufnr].modified = false
|
vim.bo[bufnr].modified = false
|
||||||
vim.cmd.doautocmd({ args = { 'BufWritePost', bufname }, mods = { silent = true } })
|
vim.cmd.doautocmd({ args = { "BufWritePost", bufname }, mods = { silent = true } })
|
||||||
end
|
end
|
||||||
vim.loop.fs_unlink(tmpfile)
|
vim.loop.fs_unlink(tmpfile)
|
||||||
vim.api.nvim_buf_delete(tmp_bufnr, { force = true })
|
vim.api.nvim_buf_delete(tmp_bufnr, { force = true })
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
local cache = require('oil.cache')
|
local cache = require("oil.cache")
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
local shell = require('oil.shell')
|
local shell = require("oil.shell")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
|
@ -13,11 +13,11 @@ local FIELD_META = constants.FIELD_META
|
||||||
---@return oil.EntryType
|
---@return oil.EntryType
|
||||||
---@return table Metadata for entry
|
---@return table Metadata for entry
|
||||||
local function parse_ls_line_bucket(line)
|
local function parse_ls_line_bucket(line)
|
||||||
local date, name = line:match('^(%d+%-%d+%-%d+%s%d+:%d+:%d+)%s+(.*)$')
|
local date, name = line:match("^(%d+%-%d+%-%d+%s%d+:%d+:%d+)%s+(.*)$")
|
||||||
if not date or not name then
|
if not date or not name then
|
||||||
error(string.format("Could not parse '%s'", line))
|
error(string.format("Could not parse '%s'", line))
|
||||||
end
|
end
|
||||||
local type = 'directory'
|
local type = "directory"
|
||||||
local meta = { date = date }
|
local meta = { date = date }
|
||||||
return name, type, meta
|
return name, type, meta
|
||||||
end
|
end
|
||||||
|
|
@ -27,18 +27,18 @@ end
|
||||||
---@return oil.EntryType
|
---@return oil.EntryType
|
||||||
---@return table Metadata for entry
|
---@return table Metadata for entry
|
||||||
local function parse_ls_line_file(line)
|
local function parse_ls_line_file(line)
|
||||||
local name = line:match('^%s+PRE%s+(.*)/$')
|
local name = line:match("^%s+PRE%s+(.*)/$")
|
||||||
local type = 'directory'
|
local type = "directory"
|
||||||
local meta = {}
|
local meta = {}
|
||||||
if name then
|
if name then
|
||||||
return name, type, meta
|
return name, type, meta
|
||||||
end
|
end
|
||||||
local date, size
|
local date, size
|
||||||
date, size, name = line:match('^(%d+%-%d+%-%d+%s%d+:%d+:%d+)%s+(%d+)%s+(.*)$')
|
date, size, name = line:match("^(%d+%-%d+%-%d+%s%d+:%d+:%d+)%s+(%d+)%s+(.*)$")
|
||||||
if not name then
|
if not name then
|
||||||
error(string.format("Could not parse '%s'", line))
|
error(string.format("Could not parse '%s'", line))
|
||||||
end
|
end
|
||||||
type = 'file'
|
type = "file"
|
||||||
meta = { date = date, size = tonumber(size) }
|
meta = { date = date, size = tonumber(size) }
|
||||||
return name, type, meta
|
return name, type, meta
|
||||||
end
|
end
|
||||||
|
|
@ -46,7 +46,7 @@ end
|
||||||
---@param cmd string[] cmd and flags
|
---@param cmd string[] cmd and flags
|
||||||
---@return string[] Shell command to run
|
---@return string[] Shell command to run
|
||||||
local function create_s3_command(cmd)
|
local function create_s3_command(cmd)
|
||||||
local full_cmd = vim.list_extend({ 'aws', 's3' }, cmd)
|
local full_cmd = vim.list_extend({ "aws", "s3" }, cmd)
|
||||||
return vim.list_extend(full_cmd, config.extra_s3_args)
|
return vim.list_extend(full_cmd, config.extra_s3_args)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ end
|
||||||
---@param path string
|
---@param path string
|
||||||
---@param callback fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun())
|
---@param callback fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun())
|
||||||
function M.list_dir(url, path, callback)
|
function M.list_dir(url, path, callback)
|
||||||
local cmd = create_s3_command({ 'ls', path, '--color=off', '--no-cli-pager' })
|
local cmd = create_s3_command({ "ls", path, "--color=off", "--no-cli-pager" })
|
||||||
shell.run(cmd, function(err, lines)
|
shell.run(cmd, function(err, lines)
|
||||||
if err then
|
if err then
|
||||||
return callback(err)
|
return callback(err)
|
||||||
|
|
@ -63,13 +63,13 @@ function M.list_dir(url, path, callback)
|
||||||
local cache_entries = {}
|
local cache_entries = {}
|
||||||
local url_path, _
|
local url_path, _
|
||||||
_, url_path = util.parse_url(url)
|
_, url_path = util.parse_url(url)
|
||||||
local is_top_level = url_path == nil or url_path:match('/') == nil
|
local is_top_level = url_path == nil or url_path:match("/") == nil
|
||||||
local parse_ls_line = is_top_level and parse_ls_line_bucket or parse_ls_line_file
|
local parse_ls_line = is_top_level and parse_ls_line_bucket or parse_ls_line_file
|
||||||
for _, line in ipairs(lines) do
|
for _, line in ipairs(lines) do
|
||||||
if line ~= '' then
|
if line ~= "" then
|
||||||
local name, type, meta = parse_ls_line(line)
|
local name, type, meta = parse_ls_line(line)
|
||||||
-- in s3 '-' can be used to create an "empty folder"
|
-- in s3 '-' can be used to create an "empty folder"
|
||||||
if name ~= '-' then
|
if name ~= "-" then
|
||||||
local cache_entry = cache.create_entry(url, name, type)
|
local cache_entry = cache.create_entry(url, name, type)
|
||||||
table.insert(cache_entries, cache_entry)
|
table.insert(cache_entries, cache_entry)
|
||||||
cache_entry[FIELD_META] = meta
|
cache_entry[FIELD_META] = meta
|
||||||
|
|
@ -85,8 +85,8 @@ end
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function M.touch(path, callback)
|
function M.touch(path, callback)
|
||||||
-- here "-" means that we copy from stdin
|
-- here "-" means that we copy from stdin
|
||||||
local cmd = create_s3_command({ 'cp', '-', path })
|
local cmd = create_s3_command({ "cp", "-", path })
|
||||||
shell.run(cmd, { stdin = 'null' }, callback)
|
shell.run(cmd, { stdin = "null" }, callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Remove files
|
--- Remove files
|
||||||
|
|
@ -94,9 +94,9 @@ end
|
||||||
---@param is_folder boolean
|
---@param is_folder boolean
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function M.rm(path, is_folder, callback)
|
function M.rm(path, is_folder, callback)
|
||||||
local main_cmd = { 'rm', path }
|
local main_cmd = { "rm", path }
|
||||||
if is_folder then
|
if is_folder then
|
||||||
table.insert(main_cmd, '--recursive')
|
table.insert(main_cmd, "--recursive")
|
||||||
end
|
end
|
||||||
local cmd = create_s3_command(main_cmd)
|
local cmd = create_s3_command(main_cmd)
|
||||||
shell.run(cmd, callback)
|
shell.run(cmd, callback)
|
||||||
|
|
@ -106,7 +106,7 @@ end
|
||||||
---@param bucket string
|
---@param bucket string
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function M.rb(bucket, callback)
|
function M.rb(bucket, callback)
|
||||||
local cmd = create_s3_command({ 'rb', bucket })
|
local cmd = create_s3_command({ "rb", bucket })
|
||||||
shell.run(cmd, callback)
|
shell.run(cmd, callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -114,7 +114,7 @@ end
|
||||||
---@param bucket string
|
---@param bucket string
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function M.mb(bucket, callback)
|
function M.mb(bucket, callback)
|
||||||
local cmd = create_s3_command({ 'mb', bucket })
|
local cmd = create_s3_command({ "mb", bucket })
|
||||||
shell.run(cmd, callback)
|
shell.run(cmd, callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -124,9 +124,9 @@ end
|
||||||
---@param is_folder boolean
|
---@param is_folder boolean
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function M.mv(src, dest, is_folder, callback)
|
function M.mv(src, dest, is_folder, callback)
|
||||||
local main_cmd = { 'mv', src, dest }
|
local main_cmd = { "mv", src, dest }
|
||||||
if is_folder then
|
if is_folder then
|
||||||
table.insert(main_cmd, '--recursive')
|
table.insert(main_cmd, "--recursive")
|
||||||
end
|
end
|
||||||
local cmd = create_s3_command(main_cmd)
|
local cmd = create_s3_command(main_cmd)
|
||||||
shell.run(cmd, callback)
|
shell.run(cmd, callback)
|
||||||
|
|
@ -138,9 +138,9 @@ end
|
||||||
---@param is_folder boolean
|
---@param is_folder boolean
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function M.cp(src, dest, is_folder, callback)
|
function M.cp(src, dest, is_folder, callback)
|
||||||
local main_cmd = { 'cp', src, dest }
|
local main_cmd = { "cp", src, dest }
|
||||||
if is_folder then
|
if is_folder then
|
||||||
table.insert(main_cmd, '--recursive')
|
table.insert(main_cmd, "--recursive")
|
||||||
end
|
end
|
||||||
local cmd = create_s3_command(main_cmd)
|
local cmd = create_s3_command(main_cmd)
|
||||||
shell.run(cmd, callback)
|
shell.run(cmd, callback)
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
local files = require('oil.adapters.files')
|
local files = require("oil.adapters.files")
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local loading = require('oil.loading')
|
local loading = require("oil.loading")
|
||||||
local pathutil = require('oil.pathutil')
|
local pathutil = require("oil.pathutil")
|
||||||
local permissions = require('oil.adapters.files.permissions')
|
local permissions = require("oil.adapters.files.permissions")
|
||||||
local shell = require('oil.shell')
|
local shell = require("oil.shell")
|
||||||
local sshfs = require('oil.adapters.ssh.sshfs')
|
local sshfs = require("oil.adapters.ssh.sshfs")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local FIELD_NAME = constants.FIELD_NAME
|
local FIELD_NAME = constants.FIELD_NAME
|
||||||
|
|
@ -22,7 +22,7 @@ local FIELD_META = constants.FIELD_META
|
||||||
|
|
||||||
---@param args string[]
|
---@param args string[]
|
||||||
local function scp(args, ...)
|
local function scp(args, ...)
|
||||||
local cmd = vim.list_extend({ 'scp', '-C' }, config.extra_scp_args)
|
local cmd = vim.list_extend({ "scp", "-C" }, config.extra_scp_args)
|
||||||
vim.list_extend(cmd, args)
|
vim.list_extend(cmd, args)
|
||||||
shell.run(cmd, ...)
|
shell.run(cmd, ...)
|
||||||
end
|
end
|
||||||
|
|
@ -33,21 +33,21 @@ M.parse_url = function(oil_url)
|
||||||
local scheme, url = util.parse_url(oil_url)
|
local scheme, url = util.parse_url(oil_url)
|
||||||
assert(scheme and url, string.format("Malformed input url '%s'", oil_url))
|
assert(scheme and url, string.format("Malformed input url '%s'", oil_url))
|
||||||
local ret = { scheme = scheme }
|
local ret = { scheme = scheme }
|
||||||
local username, rem = url:match('^([^@%s]+)@(.*)$')
|
local username, rem = url:match("^([^@%s]+)@(.*)$")
|
||||||
ret.user = username
|
ret.user = username
|
||||||
url = rem or url
|
url = rem or url
|
||||||
local host, port, path = url:match('^([^:]+):(%d+)/(.*)$')
|
local host, port, path = url:match("^([^:]+):(%d+)/(.*)$")
|
||||||
if host then
|
if host then
|
||||||
ret.host = host
|
ret.host = host
|
||||||
ret.port = tonumber(port)
|
ret.port = tonumber(port)
|
||||||
ret.path = path
|
ret.path = path
|
||||||
else
|
else
|
||||||
host, path = url:match('^([^/]+)/(.*)$')
|
host, path = url:match("^([^/]+)/(.*)$")
|
||||||
ret.host = host
|
ret.host = host
|
||||||
ret.path = path
|
ret.path = path
|
||||||
end
|
end
|
||||||
if not ret.host or not ret.path then
|
if not ret.host or not ret.path then
|
||||||
error(string.format('Malformed SSH url: %s', oil_url))
|
error(string.format("Malformed SSH url: %s", oil_url))
|
||||||
end
|
end
|
||||||
|
|
||||||
---@cast ret oil.sshUrl
|
---@cast ret oil.sshUrl
|
||||||
|
|
@ -60,33 +60,33 @@ local function url_to_str(url)
|
||||||
local pieces = { url.scheme }
|
local pieces = { url.scheme }
|
||||||
if url.user then
|
if url.user then
|
||||||
table.insert(pieces, url.user)
|
table.insert(pieces, url.user)
|
||||||
table.insert(pieces, '@')
|
table.insert(pieces, "@")
|
||||||
end
|
end
|
||||||
table.insert(pieces, url.host)
|
table.insert(pieces, url.host)
|
||||||
if url.port then
|
if url.port then
|
||||||
table.insert(pieces, string.format(':%d', url.port))
|
table.insert(pieces, string.format(":%d", url.port))
|
||||||
end
|
end
|
||||||
table.insert(pieces, '/')
|
table.insert(pieces, "/")
|
||||||
table.insert(pieces, url.path)
|
table.insert(pieces, url.path)
|
||||||
return table.concat(pieces, '')
|
return table.concat(pieces, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param url oil.sshUrl
|
---@param url oil.sshUrl
|
||||||
---@return string
|
---@return string
|
||||||
local function url_to_scp(url)
|
local function url_to_scp(url)
|
||||||
local pieces = { 'scp://' }
|
local pieces = { "scp://" }
|
||||||
if url.user then
|
if url.user then
|
||||||
table.insert(pieces, url.user)
|
table.insert(pieces, url.user)
|
||||||
table.insert(pieces, '@')
|
table.insert(pieces, "@")
|
||||||
end
|
end
|
||||||
table.insert(pieces, url.host)
|
table.insert(pieces, url.host)
|
||||||
if url.port then
|
if url.port then
|
||||||
table.insert(pieces, string.format(':%d', url.port))
|
table.insert(pieces, string.format(":%d", url.port))
|
||||||
end
|
end
|
||||||
table.insert(pieces, '/')
|
table.insert(pieces, "/")
|
||||||
local escaped_path = util.url_escape(url.path)
|
local escaped_path = util.url_escape(url.path)
|
||||||
table.insert(pieces, escaped_path)
|
table.insert(pieces, escaped_path)
|
||||||
return table.concat(pieces, '')
|
return table.concat(pieces, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param url1 oil.sshUrl
|
---@param url1 oil.sshUrl
|
||||||
|
|
@ -103,7 +103,7 @@ local _connections = {}
|
||||||
local function get_connection(url, allow_retry)
|
local function get_connection(url, allow_retry)
|
||||||
local res = M.parse_url(url)
|
local res = M.parse_url(url)
|
||||||
res.scheme = config.adapter_to_scheme.ssh
|
res.scheme = config.adapter_to_scheme.ssh
|
||||||
res.path = ''
|
res.path = ""
|
||||||
local key = url_to_str(res)
|
local key = url_to_str(res)
|
||||||
local conn = _connections[key]
|
local conn = _connections[key]
|
||||||
if not conn or (allow_retry and conn:get_connection_error()) then
|
if not conn or (allow_retry and conn:get_connection_error()) then
|
||||||
|
|
@ -137,7 +137,7 @@ ssh_columns.permissions = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
render_action = function(action)
|
render_action = function(action)
|
||||||
return string.format('CHMOD %s %s', permissions.mode_to_octal_str(action.value), action.url)
|
return string.format("CHMOD %s %s", permissions.mode_to_octal_str(action.value), action.url)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
perform_action = function(action, callback)
|
perform_action = function(action, callback)
|
||||||
|
|
@ -151,20 +151,20 @@ ssh_columns.size = {
|
||||||
render = function(entry, conf)
|
render = function(entry, conf)
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
if not meta or not meta.size then
|
if not meta or not meta.size then
|
||||||
return ''
|
return ""
|
||||||
elseif meta.size >= 1e9 then
|
elseif meta.size >= 1e9 then
|
||||||
return string.format('%.1fG', meta.size / 1e9)
|
return string.format("%.1fG", meta.size / 1e9)
|
||||||
elseif meta.size >= 1e6 then
|
elseif meta.size >= 1e6 then
|
||||||
return string.format('%.1fM', meta.size / 1e6)
|
return string.format("%.1fM", meta.size / 1e6)
|
||||||
elseif meta.size >= 1e3 then
|
elseif meta.size >= 1e3 then
|
||||||
return string.format('%.1fk', meta.size / 1e3)
|
return string.format("%.1fk", meta.size / 1e3)
|
||||||
else
|
else
|
||||||
return string.format('%d', meta.size)
|
return string.format("%d", meta.size)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
parse = function(line, conf)
|
parse = function(line, conf)
|
||||||
return line:match('^(%d+%S*)%s+(.*)$')
|
return line:match("^(%d+%S*)%s+(.*)$")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_sort_value = function(entry)
|
get_sort_value = function(entry)
|
||||||
|
|
@ -206,13 +206,13 @@ M.normalize_url = function(url, callback)
|
||||||
local conn = get_connection(url, true)
|
local conn = get_connection(url, true)
|
||||||
|
|
||||||
local path = res.path
|
local path = res.path
|
||||||
if path == '' then
|
if path == "" then
|
||||||
path = '.'
|
path = "."
|
||||||
end
|
end
|
||||||
|
|
||||||
conn:realpath(path, function(err, abspath)
|
conn:realpath(path, function(err, abspath)
|
||||||
if err then
|
if err then
|
||||||
vim.notify(string.format('Error normalizing url %s: %s', url, err), vim.log.levels.WARN)
|
vim.notify(string.format("Error normalizing url %s: %s", url, err), vim.log.levels.WARN)
|
||||||
callback(url)
|
callback(url)
|
||||||
else
|
else
|
||||||
res.path = abspath
|
res.path = abspath
|
||||||
|
|
@ -259,15 +259,15 @@ end
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@return string
|
---@return string
|
||||||
M.render_action = function(action)
|
M.render_action = function(action)
|
||||||
if action.type == 'create' then
|
if action.type == "create" then
|
||||||
local ret = string.format('CREATE %s', action.url)
|
local ret = string.format("CREATE %s", action.url)
|
||||||
if action.link then
|
if action.link then
|
||||||
ret = ret .. ' -> ' .. action.link
|
ret = ret .. " -> " .. action.link
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
elseif action.type == 'delete' then
|
elseif action.type == "delete" then
|
||||||
return string.format('DELETE %s', action.url)
|
return string.format("DELETE %s", action.url)
|
||||||
elseif action.type == 'move' or action.type == 'copy' then
|
elseif action.type == "move" or action.type == "copy" then
|
||||||
local src = action.src_url
|
local src = action.src_url
|
||||||
local dest = action.dest_url
|
local dest = action.dest_url
|
||||||
if config.get_adapter_by_scheme(src) == M then
|
if config.get_adapter_by_scheme(src) == M then
|
||||||
|
|
@ -279,7 +279,7 @@ M.render_action = function(action)
|
||||||
assert(path)
|
assert(path)
|
||||||
src = files.to_short_os_path(path, action.entry_type)
|
src = files.to_short_os_path(path, action.entry_type)
|
||||||
end
|
end
|
||||||
return string.format(' %s %s -> %s', action.type:upper(), src, dest)
|
return string.format(" %s %s -> %s", action.type:upper(), src, dest)
|
||||||
else
|
else
|
||||||
error(string.format("Bad action type: '%s'", action.type))
|
error(string.format("Bad action type: '%s'", action.type))
|
||||||
end
|
end
|
||||||
|
|
@ -288,21 +288,21 @@ end
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@param cb fun(err: nil|string)
|
---@param cb fun(err: nil|string)
|
||||||
M.perform_action = function(action, cb)
|
M.perform_action = function(action, cb)
|
||||||
if action.type == 'create' then
|
if action.type == "create" then
|
||||||
local res = M.parse_url(action.url)
|
local res = M.parse_url(action.url)
|
||||||
local conn = get_connection(action.url)
|
local conn = get_connection(action.url)
|
||||||
if action.entry_type == 'directory' then
|
if action.entry_type == "directory" then
|
||||||
conn:mkdir(res.path, cb)
|
conn:mkdir(res.path, cb)
|
||||||
elseif action.entry_type == 'link' and action.link then
|
elseif action.entry_type == "link" and action.link then
|
||||||
conn:mklink(res.path, action.link, cb)
|
conn:mklink(res.path, action.link, cb)
|
||||||
else
|
else
|
||||||
conn:touch(res.path, cb)
|
conn:touch(res.path, cb)
|
||||||
end
|
end
|
||||||
elseif action.type == 'delete' then
|
elseif action.type == "delete" then
|
||||||
local res = M.parse_url(action.url)
|
local res = M.parse_url(action.url)
|
||||||
local conn = get_connection(action.url)
|
local conn = get_connection(action.url)
|
||||||
conn:rm(res.path, cb)
|
conn:rm(res.path, cb)
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if src_adapter == M and dest_adapter == M then
|
if src_adapter == M and dest_adapter == M then
|
||||||
|
|
@ -311,7 +311,7 @@ M.perform_action = function(action, cb)
|
||||||
local src_conn = get_connection(action.src_url)
|
local src_conn = get_connection(action.src_url)
|
||||||
local dest_conn = get_connection(action.dest_url)
|
local dest_conn = get_connection(action.dest_url)
|
||||||
if src_conn ~= dest_conn then
|
if src_conn ~= dest_conn then
|
||||||
scp({ '-r', url_to_scp(src_res), url_to_scp(dest_res) }, function(err)
|
scp({ "-r", url_to_scp(src_res), url_to_scp(dest_res) }, function(err)
|
||||||
if err then
|
if err then
|
||||||
return cb(err)
|
return cb(err)
|
||||||
end
|
end
|
||||||
|
|
@ -321,16 +321,16 @@ M.perform_action = function(action, cb)
|
||||||
src_conn:mv(src_res.path, dest_res.path, cb)
|
src_conn:mv(src_res.path, dest_res.path, cb)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
cb('We should never attempt to move across adapters')
|
cb("We should never attempt to move across adapters")
|
||||||
end
|
end
|
||||||
elseif action.type == 'copy' then
|
elseif action.type == "copy" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if src_adapter == M and dest_adapter == M then
|
if src_adapter == M and dest_adapter == M then
|
||||||
local src_res = M.parse_url(action.src_url)
|
local src_res = M.parse_url(action.src_url)
|
||||||
local dest_res = M.parse_url(action.dest_url)
|
local dest_res = M.parse_url(action.dest_url)
|
||||||
if not url_hosts_equal(src_res, dest_res) then
|
if not url_hosts_equal(src_res, dest_res) then
|
||||||
scp({ '-r', url_to_scp(src_res), url_to_scp(dest_res) }, cb)
|
scp({ "-r", url_to_scp(src_res), url_to_scp(dest_res) }, cb)
|
||||||
else
|
else
|
||||||
local src_conn = get_connection(action.src_url)
|
local src_conn = get_connection(action.src_url)
|
||||||
src_conn:cp(src_res.path, dest_res.path, cb)
|
src_conn:cp(src_res.path, dest_res.path, cb)
|
||||||
|
|
@ -349,14 +349,14 @@ M.perform_action = function(action, cb)
|
||||||
src_arg = fs.posix_to_os_path(path)
|
src_arg = fs.posix_to_os_path(path)
|
||||||
dest_arg = url_to_scp(M.parse_url(action.dest_url))
|
dest_arg = url_to_scp(M.parse_url(action.dest_url))
|
||||||
end
|
end
|
||||||
scp({ '-r', src_arg, dest_arg }, cb)
|
scp({ "-r", src_arg, dest_arg }, cb)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
cb(string.format('Bad action type: %s', action.type))
|
cb(string.format("Bad action type: %s", action.type))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
M.supported_cross_adapter_actions = { files = 'copy' }
|
M.supported_cross_adapter_actions = { files = "copy" }
|
||||||
|
|
||||||
---@param bufnr integer
|
---@param bufnr integer
|
||||||
M.read_file = function(bufnr)
|
M.read_file = function(bufnr)
|
||||||
|
|
@ -365,11 +365,11 @@ M.read_file = function(bufnr)
|
||||||
local url = M.parse_url(bufname)
|
local url = M.parse_url(bufname)
|
||||||
local scp_url = url_to_scp(url)
|
local scp_url = url_to_scp(url)
|
||||||
local basename = pathutil.basename(bufname)
|
local basename = pathutil.basename(bufname)
|
||||||
local cache_dir = vim.fn.stdpath('cache')
|
local cache_dir = vim.fn.stdpath("cache")
|
||||||
assert(type(cache_dir) == 'string')
|
assert(type(cache_dir) == "string")
|
||||||
local tmpdir = fs.join(cache_dir, 'oil')
|
local tmpdir = fs.join(cache_dir, "oil")
|
||||||
fs.mkdirp(tmpdir)
|
fs.mkdirp(tmpdir)
|
||||||
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, 'ssh_XXXXXX'))
|
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, "ssh_XXXXXX"))
|
||||||
if fd then
|
if fd then
|
||||||
vim.loop.fs_close(fd)
|
vim.loop.fs_close(fd)
|
||||||
end
|
end
|
||||||
|
|
@ -378,9 +378,9 @@ M.read_file = function(bufnr)
|
||||||
scp({ scp_url, tmpfile }, function(err)
|
scp({ scp_url, tmpfile }, function(err)
|
||||||
loading.set_loading(bufnr, false)
|
loading.set_loading(bufnr, false)
|
||||||
vim.bo[bufnr].modifiable = true
|
vim.bo[bufnr].modifiable = true
|
||||||
vim.cmd.doautocmd({ args = { 'BufReadPre', bufname }, mods = { silent = true } })
|
vim.cmd.doautocmd({ args = { "BufReadPre", bufname }, mods = { silent = true } })
|
||||||
if err then
|
if err then
|
||||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, vim.split(err, '\n'))
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, vim.split(err, "\n"))
|
||||||
else
|
else
|
||||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {})
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {})
|
||||||
vim.api.nvim_buf_call(bufnr, function()
|
vim.api.nvim_buf_call(bufnr, function()
|
||||||
|
|
@ -394,9 +394,9 @@ M.read_file = function(bufnr)
|
||||||
if filetype then
|
if filetype then
|
||||||
vim.bo[bufnr].filetype = filetype
|
vim.bo[bufnr].filetype = filetype
|
||||||
end
|
end
|
||||||
vim.cmd.doautocmd({ args = { 'BufReadPost', bufname }, mods = { silent = true } })
|
vim.cmd.doautocmd({ args = { "BufReadPost", bufname }, mods = { silent = true } })
|
||||||
vim.api.nvim_buf_delete(tmp_bufnr, { force = true })
|
vim.api.nvim_buf_delete(tmp_bufnr, { force = true })
|
||||||
vim.keymap.set('n', 'gf', M.goto_file, { buffer = bufnr })
|
vim.keymap.set("n", "gf", M.goto_file, { buffer = bufnr })
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -405,14 +405,14 @@ M.write_file = function(bufnr)
|
||||||
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
||||||
local url = M.parse_url(bufname)
|
local url = M.parse_url(bufname)
|
||||||
local scp_url = url_to_scp(url)
|
local scp_url = url_to_scp(url)
|
||||||
local cache_dir = vim.fn.stdpath('cache')
|
local cache_dir = vim.fn.stdpath("cache")
|
||||||
assert(type(cache_dir) == 'string')
|
assert(type(cache_dir) == "string")
|
||||||
local tmpdir = fs.join(cache_dir, 'oil')
|
local tmpdir = fs.join(cache_dir, "oil")
|
||||||
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, 'ssh_XXXXXXXX'))
|
local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, "ssh_XXXXXXXX"))
|
||||||
if fd then
|
if fd then
|
||||||
vim.loop.fs_close(fd)
|
vim.loop.fs_close(fd)
|
||||||
end
|
end
|
||||||
vim.cmd.doautocmd({ args = { 'BufWritePre', bufname }, mods = { silent = true } })
|
vim.cmd.doautocmd({ args = { "BufWritePre", bufname }, mods = { silent = true } })
|
||||||
vim.bo[bufnr].modifiable = false
|
vim.bo[bufnr].modifiable = false
|
||||||
vim.cmd.write({ args = { tmpfile }, bang = true, mods = { silent = true, noautocmd = true } })
|
vim.cmd.write({ args = { tmpfile }, bang = true, mods = { silent = true, noautocmd = true } })
|
||||||
local tmp_bufnr = vim.fn.bufadd(tmpfile)
|
local tmp_bufnr = vim.fn.bufadd(tmpfile)
|
||||||
|
|
@ -420,10 +420,10 @@ M.write_file = function(bufnr)
|
||||||
scp({ tmpfile, scp_url }, function(err)
|
scp({ tmpfile, scp_url }, function(err)
|
||||||
vim.bo[bufnr].modifiable = true
|
vim.bo[bufnr].modifiable = true
|
||||||
if err then
|
if err then
|
||||||
vim.notify(string.format('Error writing file: %s', err), vim.log.levels.ERROR)
|
vim.notify(string.format("Error writing file: %s", err), vim.log.levels.ERROR)
|
||||||
else
|
else
|
||||||
vim.bo[bufnr].modified = false
|
vim.bo[bufnr].modified = false
|
||||||
vim.cmd.doautocmd({ args = { 'BufWritePost', bufname }, mods = { silent = true } })
|
vim.cmd.doautocmd({ args = { "BufWritePost", bufname }, mods = { silent = true } })
|
||||||
end
|
end
|
||||||
vim.loop.fs_unlink(tmpfile)
|
vim.loop.fs_unlink(tmpfile)
|
||||||
vim.api.nvim_buf_delete(tmp_bufnr, { force = true })
|
vim.api.nvim_buf_delete(tmp_bufnr, { force = true })
|
||||||
|
|
@ -432,7 +432,7 @@ end
|
||||||
|
|
||||||
M.goto_file = function()
|
M.goto_file = function()
|
||||||
local url = M.parse_url(vim.api.nvim_buf_get_name(0))
|
local url = M.parse_url(vim.api.nvim_buf_get_name(0))
|
||||||
local fname = vim.fn.expand('<cfile>')
|
local fname = vim.fn.expand("<cfile>")
|
||||||
local fullpath = fname
|
local fullpath = fname
|
||||||
if not fs.is_absolute(fname) then
|
if not fs.is_absolute(fname) then
|
||||||
local pardir = vim.fs.dirname(url.path)
|
local pardir = vim.fs.dirname(url.path)
|
||||||
|
|
@ -459,7 +459,7 @@ M.goto_file = function()
|
||||||
vim.cmd.edit({ args = { url_to_str(url) } })
|
vim.cmd.edit({ args = { url_to_str(url) } })
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
for suffix in vim.gsplit(vim.o.suffixesadd, ',', { plain = true, trimempty = true }) do
|
for suffix in vim.gsplit(vim.o.suffixesadd, ",", { plain = true, trimempty = true }) do
|
||||||
local suffixname = basename .. suffix
|
local suffixname = basename .. suffix
|
||||||
if name_map[suffixname] then
|
if name_map[suffixname] then
|
||||||
url.path = fullpath .. suffix
|
url.path = fullpath .. suffix
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local layout = require('oil.layout')
|
local layout = require("oil.layout")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
|
|
||||||
---@class (exact) oil.sshCommand
|
---@class (exact) oil.sshCommand
|
||||||
---@field cmd string|string[]
|
---@field cmd string|string[]
|
||||||
|
|
@ -24,12 +24,12 @@ local function output_extend(agg, output)
|
||||||
local start = #agg
|
local start = #agg
|
||||||
if vim.tbl_isempty(agg) then
|
if vim.tbl_isempty(agg) then
|
||||||
for _, line in ipairs(output) do
|
for _, line in ipairs(output) do
|
||||||
line = line:gsub('\r', '')
|
line = line:gsub("\r", "")
|
||||||
table.insert(agg, line)
|
table.insert(agg, line)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
for i, v in ipairs(output) do
|
for i, v in ipairs(output) do
|
||||||
v = v:gsub('\r', '')
|
v = v:gsub("\r", "")
|
||||||
if i == 1 then
|
if i == 1 then
|
||||||
agg[#agg] = agg[#agg] .. v
|
agg[#agg] = agg[#agg] .. v
|
||||||
else
|
else
|
||||||
|
|
@ -53,7 +53,7 @@ local function get_last_lines(bufnr, num_lines)
|
||||||
vim.api.nvim_buf_get_lines(bufnr, end_line - need_lines, end_line, false),
|
vim.api.nvim_buf_get_lines(bufnr, end_line - need_lines, end_line, false),
|
||||||
lines
|
lines
|
||||||
)
|
)
|
||||||
while not vim.tbl_isempty(lines) and lines[#lines]:match('^%s*$') do
|
while not vim.tbl_isempty(lines) and lines[#lines]:match("^%s*$") do
|
||||||
table.remove(lines)
|
table.remove(lines)
|
||||||
end
|
end
|
||||||
end_line = end_line - need_lines
|
end_line = end_line - need_lines
|
||||||
|
|
@ -66,14 +66,14 @@ end
|
||||||
function SSHConnection.create_ssh_command(url)
|
function SSHConnection.create_ssh_command(url)
|
||||||
local host = url.host
|
local host = url.host
|
||||||
if url.user then
|
if url.user then
|
||||||
host = url.user .. '@' .. host
|
host = url.user .. "@" .. host
|
||||||
end
|
end
|
||||||
local command = {
|
local command = {
|
||||||
'ssh',
|
"ssh",
|
||||||
host,
|
host,
|
||||||
}
|
}
|
||||||
if url.port then
|
if url.port then
|
||||||
table.insert(command, '-p')
|
table.insert(command, "-p")
|
||||||
table.insert(command, url.port)
|
table.insert(command, url.port)
|
||||||
end
|
end
|
||||||
return command
|
return command
|
||||||
|
|
@ -84,8 +84,8 @@ end
|
||||||
function SSHConnection.new(url)
|
function SSHConnection.new(url)
|
||||||
local command = SSHConnection.create_ssh_command(url)
|
local command = SSHConnection.create_ssh_command(url)
|
||||||
vim.list_extend(command, {
|
vim.list_extend(command, {
|
||||||
'/bin/sh',
|
"/bin/sh",
|
||||||
'-c',
|
"-c",
|
||||||
-- HACK: For some reason in my testing if I just have "echo READY" it doesn't appear, but if I echo
|
-- HACK: For some reason in my testing if I just have "echo READY" it doesn't appear, but if I echo
|
||||||
-- anything prior to that, it *will* appear. The first line gets swallowed.
|
-- anything prior to that, it *will* appear. The first line gets swallowed.
|
||||||
"echo '_make_newline_'; echo '===READY==='; exec /bin/sh",
|
"echo '_make_newline_'; echo '===READY==='; exec /bin/sh",
|
||||||
|
|
@ -112,7 +112,7 @@ function SSHConnection.new(url)
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
self.term_id = term_id
|
self.term_id = term_id
|
||||||
vim.api.nvim_chan_send(term_id, string.format('ssh %s\r\n', url.host))
|
vim.api.nvim_chan_send(term_id, string.format("ssh %s\r\n", url.host))
|
||||||
util.hack_around_termopen_autocmd(mode)
|
util.hack_around_termopen_autocmd(mode)
|
||||||
|
|
||||||
-- If it takes more than 2 seconds to connect, pop open the terminal
|
-- If it takes more than 2 seconds to connect, pop open the terminal
|
||||||
|
|
@ -125,7 +125,7 @@ function SSHConnection.new(url)
|
||||||
local jid = vim.fn.jobstart(command, {
|
local jid = vim.fn.jobstart(command, {
|
||||||
pty = true, -- This is require for interactivity
|
pty = true, -- This is require for interactivity
|
||||||
on_stdout = function(j, output)
|
on_stdout = function(j, output)
|
||||||
pcall(vim.api.nvim_chan_send, self.term_id, table.concat(output, '\r\n'))
|
pcall(vim.api.nvim_chan_send, self.term_id, table.concat(output, "\r\n"))
|
||||||
---@diagnostic disable-next-line: invisible
|
---@diagnostic disable-next-line: invisible
|
||||||
local new_i_start = output_extend(self._stdout, output)
|
local new_i_start = output_extend(self._stdout, output)
|
||||||
self:_handle_output(new_i_start)
|
self:_handle_output(new_i_start)
|
||||||
|
|
@ -134,12 +134,12 @@ function SSHConnection.new(url)
|
||||||
pcall(
|
pcall(
|
||||||
vim.api.nvim_chan_send,
|
vim.api.nvim_chan_send,
|
||||||
self.term_id,
|
self.term_id,
|
||||||
string.format('\r\n[Process exited %d]\r\n', code)
|
string.format("\r\n[Process exited %d]\r\n", code)
|
||||||
)
|
)
|
||||||
-- Defer to allow the deferred terminal output handling to kick in first
|
-- Defer to allow the deferred terminal output handling to kick in first
|
||||||
vim.defer_fn(function()
|
vim.defer_fn(function()
|
||||||
if code == 0 then
|
if code == 0 then
|
||||||
self:_set_connection_error('SSH connection terminated gracefully')
|
self:_set_connection_error("SSH connection terminated gracefully")
|
||||||
else
|
else
|
||||||
self:_set_connection_error(
|
self:_set_connection_error(
|
||||||
'Unknown SSH error\nTo see more, run :lua require("oil.adapters.ssh").open_terminal()'
|
'Unknown SSH error\nTo see more, run :lua require("oil.adapters.ssh").open_terminal()'
|
||||||
|
|
@ -156,23 +156,23 @@ function SSHConnection.new(url)
|
||||||
else
|
else
|
||||||
self.jid = jid
|
self.jid = jid
|
||||||
end
|
end
|
||||||
self:run('id -u', function(err, lines)
|
self:run("id -u", function(err, lines)
|
||||||
if err then
|
if err then
|
||||||
vim.notify(string.format('Error fetching ssh connection user: %s', err), vim.log.levels.WARN)
|
vim.notify(string.format("Error fetching ssh connection user: %s", err), vim.log.levels.WARN)
|
||||||
else
|
else
|
||||||
assert(lines)
|
assert(lines)
|
||||||
self.meta.user = vim.trim(table.concat(lines, ''))
|
self.meta.user = vim.trim(table.concat(lines, ""))
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
self:run('id -G', function(err, lines)
|
self:run("id -G", function(err, lines)
|
||||||
if err then
|
if err then
|
||||||
vim.notify(
|
vim.notify(
|
||||||
string.format('Error fetching ssh connection user groups: %s', err),
|
string.format("Error fetching ssh connection user groups: %s", err),
|
||||||
vim.log.levels.WARN
|
vim.log.levels.WARN
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
assert(lines)
|
assert(lines)
|
||||||
self.meta.groups = vim.split(table.concat(lines, ''), '%s+', { trimempty = true })
|
self.meta.groups = vim.split(table.concat(lines, ""), "%s+", { trimempty = true })
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
@ -197,7 +197,7 @@ function SSHConnection:_handle_output(start_i)
|
||||||
if not self.connected then
|
if not self.connected then
|
||||||
for i = start_i, #self._stdout - 1 do
|
for i = start_i, #self._stdout - 1 do
|
||||||
local line = self._stdout[i]
|
local line = self._stdout[i]
|
||||||
if line == '===READY===' then
|
if line == "===READY===" then
|
||||||
if self.term_winid then
|
if self.term_winid then
|
||||||
if vim.api.nvim_win_is_valid(self.term_winid) then
|
if vim.api.nvim_win_is_valid(self.term_winid) then
|
||||||
vim.api.nvim_win_close(self.term_winid, true)
|
vim.api.nvim_win_close(self.term_winid, true)
|
||||||
|
|
@ -215,7 +215,7 @@ function SSHConnection:_handle_output(start_i)
|
||||||
for i = start_i, #self._stdout - 1 do
|
for i = start_i, #self._stdout - 1 do
|
||||||
---@type string
|
---@type string
|
||||||
local line = self._stdout[i]
|
local line = self._stdout[i]
|
||||||
if line:match('^===BEGIN===%s*$') then
|
if line:match("^===BEGIN===%s*$") then
|
||||||
self._stdout = util.tbl_slice(self._stdout, i + 1)
|
self._stdout = util.tbl_slice(self._stdout, i + 1)
|
||||||
self:_handle_output(1)
|
self:_handle_output(1)
|
||||||
return
|
return
|
||||||
|
|
@ -223,15 +223,15 @@ function SSHConnection:_handle_output(start_i)
|
||||||
-- We can't be as strict with the matching (^$) because since we're using a pty the stdout and
|
-- We can't be as strict with the matching (^$) because since we're using a pty the stdout and
|
||||||
-- stderr can be interleaved. If the command had an error, the stderr may interfere with a
|
-- stderr can be interleaved. If the command had an error, the stderr may interfere with a
|
||||||
-- clean print of the done line.
|
-- clean print of the done line.
|
||||||
local exit_code = line:match('===DONE%((%d+)%)===')
|
local exit_code = line:match("===DONE%((%d+)%)===")
|
||||||
if exit_code then
|
if exit_code then
|
||||||
local output = util.tbl_slice(self._stdout, 1, i - 1)
|
local output = util.tbl_slice(self._stdout, 1, i - 1)
|
||||||
local cb = self.commands[1].cb
|
local cb = self.commands[1].cb
|
||||||
self._stdout = util.tbl_slice(self._stdout, i + 1)
|
self._stdout = util.tbl_slice(self._stdout, i + 1)
|
||||||
if exit_code == '0' then
|
if exit_code == "0" then
|
||||||
cb(nil, output)
|
cb(nil, output)
|
||||||
else
|
else
|
||||||
cb(exit_code .. ': ' .. table.concat(output, '\n'), output)
|
cb(exit_code .. ": " .. table.concat(output, "\n"), output)
|
||||||
end
|
end
|
||||||
table.remove(self.commands, 1)
|
table.remove(self.commands, 1)
|
||||||
self:_handle_output(1)
|
self:_handle_output(1)
|
||||||
|
|
@ -244,17 +244,16 @@ function SSHConnection:_handle_output(start_i)
|
||||||
local function check_last_line()
|
local function check_last_line()
|
||||||
local last_lines = get_last_lines(self.term_bufnr, 1)
|
local last_lines = get_last_lines(self.term_bufnr, 1)
|
||||||
local last_line = last_lines[1]
|
local last_line = last_lines[1]
|
||||||
if last_line:match('^Are you sure you want to continue connecting') then
|
if last_line:match("^Are you sure you want to continue connecting") then
|
||||||
self:open_terminal()
|
self:open_terminal()
|
||||||
-- selene: allow(if_same_then_else)
|
elseif last_line:match("Password:%s*$") then
|
||||||
elseif last_line:match('Password:%s*$') then
|
|
||||||
self:open_terminal()
|
self:open_terminal()
|
||||||
elseif last_line:match(': Permission denied %(.+%)%.') then
|
elseif last_line:match(": Permission denied %(.+%)%.") then
|
||||||
self:_set_connection_error(last_line:match(': (Permission denied %(.+%).)'))
|
self:_set_connection_error(last_line:match(": (Permission denied %(.+%).)"))
|
||||||
elseif last_line:match('^ssh: .*Connection refused%s*$') then
|
elseif last_line:match("^ssh: .*Connection refused%s*$") then
|
||||||
self:_set_connection_error('Connection refused')
|
self:_set_connection_error("Connection refused")
|
||||||
elseif last_line:match('^Connection to .+ closed by remote host.%s*$') then
|
elseif last_line:match("^Connection to .+ closed by remote host.%s*$") then
|
||||||
self:_set_connection_error('Connection closed by remote host')
|
self:_set_connection_error("Connection closed by remote host")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- We have to defer this so the terminal buffer has time to update
|
-- We have to defer this so the terminal buffer has time to update
|
||||||
|
|
@ -274,12 +273,12 @@ function SSHConnection:open_terminal()
|
||||||
local row = math.floor((total_height - height) / 2)
|
local row = math.floor((total_height - height) / 2)
|
||||||
local col = math.floor((vim.o.columns - width) / 2)
|
local col = math.floor((vim.o.columns - width) / 2)
|
||||||
self.term_winid = vim.api.nvim_open_win(self.term_bufnr, true, {
|
self.term_winid = vim.api.nvim_open_win(self.term_bufnr, true, {
|
||||||
relative = 'editor',
|
relative = "editor",
|
||||||
width = width,
|
width = width,
|
||||||
height = height,
|
height = height,
|
||||||
row = row,
|
row = row,
|
||||||
col = col,
|
col = col,
|
||||||
style = 'minimal',
|
style = "minimal",
|
||||||
border = config.ssh.border,
|
border = config.ssh.border,
|
||||||
})
|
})
|
||||||
vim.cmd.startinsert()
|
vim.cmd.startinsert()
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
local SSHConnection = require('oil.adapters.ssh.connection')
|
local SSHConnection = require("oil.adapters.ssh.connection")
|
||||||
local cache = require('oil.cache')
|
local cache = require("oil.cache")
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
local permissions = require('oil.adapters.files.permissions')
|
local permissions = require("oil.adapters.files.permissions")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
|
|
||||||
---@class (exact) oil.sshFs
|
---@class (exact) oil.sshFs
|
||||||
---@field new fun(url: oil.sshUrl): oil.sshFs
|
---@field new fun(url: oil.sshUrl): oil.sshFs
|
||||||
|
|
@ -13,13 +13,13 @@ local FIELD_TYPE = constants.FIELD_TYPE
|
||||||
local FIELD_META = constants.FIELD_META
|
local FIELD_META = constants.FIELD_META
|
||||||
|
|
||||||
local typechar_map = {
|
local typechar_map = {
|
||||||
l = 'link',
|
l = "link",
|
||||||
d = 'directory',
|
d = "directory",
|
||||||
p = 'fifo',
|
p = "fifo",
|
||||||
s = 'socket',
|
s = "socket",
|
||||||
['-'] = 'file',
|
["-"] = "file",
|
||||||
c = 'file', -- character special file
|
c = "file", -- character special file
|
||||||
b = 'file', -- block special file
|
b = "file", -- block special file
|
||||||
}
|
}
|
||||||
---@param line string
|
---@param line string
|
||||||
---@return string Name of entry
|
---@return string Name of entry
|
||||||
|
|
@ -27,11 +27,11 @@ local typechar_map = {
|
||||||
---@return table Metadata for entry
|
---@return table Metadata for entry
|
||||||
local function parse_ls_line(line)
|
local function parse_ls_line(line)
|
||||||
local typechar, perms, refcount, user, group, rem =
|
local typechar, perms, refcount, user, group, rem =
|
||||||
line:match('^(.)(%S+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(.*)$')
|
line:match("^(.)(%S+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(.*)$")
|
||||||
if not typechar then
|
if not typechar then
|
||||||
error(string.format("Could not parse '%s'", line))
|
error(string.format("Could not parse '%s'", line))
|
||||||
end
|
end
|
||||||
local type = typechar_map[typechar] or 'file'
|
local type = typechar_map[typechar] or "file"
|
||||||
|
|
||||||
local meta = {
|
local meta = {
|
||||||
user = user,
|
user = user,
|
||||||
|
|
@ -40,26 +40,26 @@ local function parse_ls_line(line)
|
||||||
refcount = tonumber(refcount),
|
refcount = tonumber(refcount),
|
||||||
}
|
}
|
||||||
local name, size, date, major, minor
|
local name, size, date, major, minor
|
||||||
if typechar == 'c' or typechar == 'b' then
|
if typechar == "c" or typechar == "b" then
|
||||||
major, minor, date, name = rem:match('^(%d+)%s*,%s*(%d+)%s+(%S+%s+%d+%s+%d%d:?%d%d)%s+(.*)')
|
major, minor, date, name = rem:match("^(%d+)%s*,%s*(%d+)%s+(%S+%s+%d+%s+%d%d:?%d%d)%s+(.*)")
|
||||||
if name == nil then
|
if name == nil then
|
||||||
major, minor, date, name =
|
major, minor, date, name =
|
||||||
rem:match('^(%d+)%s*,%s*(%d+)%s+(%d+%-%d+%-%d+%s+%d%d:?%d%d)%s+(.*)')
|
rem:match("^(%d+)%s*,%s*(%d+)%s+(%d+%-%d+%-%d+%s+%d%d:?%d%d)%s+(.*)")
|
||||||
end
|
end
|
||||||
meta.major = tonumber(major)
|
meta.major = tonumber(major)
|
||||||
meta.minor = tonumber(minor)
|
meta.minor = tonumber(minor)
|
||||||
else
|
else
|
||||||
size, date, name = rem:match('^(%d+)%s+(%S+%s+%d+%s+%d%d:?%d%d)%s+(.*)')
|
size, date, name = rem:match("^(%d+)%s+(%S+%s+%d+%s+%d%d:?%d%d)%s+(.*)")
|
||||||
if name == nil then
|
if name == nil then
|
||||||
size, date, name = rem:match('^(%d+)%s+(%d+%-%d+%-%d+%s+%d%d:?%d%d)%s+(.*)')
|
size, date, name = rem:match("^(%d+)%s+(%d+%-%d+%-%d+%s+%d%d:?%d%d)%s+(.*)")
|
||||||
end
|
end
|
||||||
meta.size = tonumber(size)
|
meta.size = tonumber(size)
|
||||||
end
|
end
|
||||||
meta.iso_modified_date = date
|
meta.iso_modified_date = date
|
||||||
if type == 'link' then
|
if type == "link" then
|
||||||
local link
|
local link
|
||||||
name, link = unpack(vim.split(name, ' -> ', { plain = true }))
|
name, link = unpack(vim.split(name, " -> ", { plain = true }))
|
||||||
if vim.endswith(link, '/') then
|
if vim.endswith(link, "/") then
|
||||||
link = link:sub(1, #link - 1)
|
link = link:sub(1, #link - 1)
|
||||||
end
|
end
|
||||||
meta.link = link
|
meta.link = link
|
||||||
|
|
@ -94,7 +94,7 @@ end
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function SSHFS:chmod(value, path, callback)
|
function SSHFS:chmod(value, path, callback)
|
||||||
local octal = permissions.mode_to_octal_str(value)
|
local octal = permissions.mode_to_octal_str(value)
|
||||||
self.conn:run(string.format('chmod %s %s', octal, shellescape(path)), callback)
|
self.conn:run(string.format("chmod %s %s", octal, shellescape(path)), callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
function SSHFS:open_terminal()
|
function SSHFS:open_terminal()
|
||||||
|
|
@ -114,24 +114,24 @@ function SSHFS:realpath(path, callback)
|
||||||
return callback(err)
|
return callback(err)
|
||||||
end
|
end
|
||||||
assert(lines)
|
assert(lines)
|
||||||
local abspath = table.concat(lines, '')
|
local abspath = table.concat(lines, "")
|
||||||
-- If the path was "." then the abspath might be /path/to/., so we need to trim that final '.'
|
-- If the path was "." then the abspath might be /path/to/., so we need to trim that final '.'
|
||||||
if vim.endswith(abspath, '.') then
|
if vim.endswith(abspath, ".") then
|
||||||
abspath = abspath:sub(1, #abspath - 1)
|
abspath = abspath:sub(1, #abspath - 1)
|
||||||
end
|
end
|
||||||
self.conn:run(
|
self.conn:run(
|
||||||
string.format('LC_ALL=C ls -land --color=never %s', shellescape(abspath)),
|
string.format("LC_ALL=C ls -land --color=never %s", shellescape(abspath)),
|
||||||
function(ls_err, ls_lines)
|
function(ls_err, ls_lines)
|
||||||
local type
|
local type
|
||||||
if ls_err then
|
if ls_err then
|
||||||
-- If the file doesn't exist, treat it like a not-yet-existing directory
|
-- If the file doesn't exist, treat it like a not-yet-existing directory
|
||||||
type = 'directory'
|
type = "directory"
|
||||||
else
|
else
|
||||||
assert(ls_lines)
|
assert(ls_lines)
|
||||||
local _
|
local _
|
||||||
_, type = parse_ls_line(ls_lines[1])
|
_, type = parse_ls_line(ls_lines[1])
|
||||||
end
|
end
|
||||||
if type == 'directory' then
|
if type == "directory" then
|
||||||
abspath = util.addslash(abspath)
|
abspath = util.addslash(abspath)
|
||||||
end
|
end
|
||||||
callback(nil, abspath)
|
callback(nil, abspath)
|
||||||
|
|
@ -146,13 +146,13 @@ local dir_meta = {}
|
||||||
---@param path string
|
---@param path string
|
||||||
---@param callback fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun())
|
---@param callback fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun())
|
||||||
function SSHFS:list_dir(url, path, callback)
|
function SSHFS:list_dir(url, path, callback)
|
||||||
local path_postfix = ''
|
local path_postfix = ""
|
||||||
if path ~= '' then
|
if path ~= "" then
|
||||||
path_postfix = string.format(' %s', shellescape(path))
|
path_postfix = string.format(" %s", shellescape(path))
|
||||||
end
|
end
|
||||||
self.conn:run('LC_ALL=C ls -lan --color=never' .. path_postfix, function(err, lines)
|
self.conn:run("LC_ALL=C ls -lan --color=never" .. path_postfix, function(err, lines)
|
||||||
if err then
|
if err then
|
||||||
if err:match('No such file or directory%s*$') then
|
if err:match("No such file or directory%s*$") then
|
||||||
-- If the directory doesn't exist, treat the list as a success. We will be able to traverse
|
-- If the directory doesn't exist, treat the list as a success. We will be able to traverse
|
||||||
-- and edit a not-yet-existing directory.
|
-- and edit a not-yet-existing directory.
|
||||||
return callback()
|
return callback()
|
||||||
|
|
@ -165,12 +165,12 @@ function SSHFS:list_dir(url, path, callback)
|
||||||
local entries = {}
|
local entries = {}
|
||||||
local cache_entries = {}
|
local cache_entries = {}
|
||||||
for _, line in ipairs(lines) do
|
for _, line in ipairs(lines) do
|
||||||
if line ~= '' and not line:match('^total') then
|
if line ~= "" and not line:match("^total") then
|
||||||
local name, type, meta = parse_ls_line(line)
|
local name, type, meta = parse_ls_line(line)
|
||||||
if name == '.' then
|
if name == "." then
|
||||||
dir_meta[url] = meta
|
dir_meta[url] = meta
|
||||||
elseif name ~= '..' then
|
elseif name ~= ".." then
|
||||||
if type == 'link' then
|
if type == "link" then
|
||||||
any_links = true
|
any_links = true
|
||||||
end
|
end
|
||||||
local cache_entry = cache.create_entry(url, name, type)
|
local cache_entry = cache.create_entry(url, name, type)
|
||||||
|
|
@ -184,19 +184,19 @@ function SSHFS:list_dir(url, path, callback)
|
||||||
-- If there were any soft links, then we need to run another ls command with -L so that we can
|
-- If there were any soft links, then we need to run another ls command with -L so that we can
|
||||||
-- resolve the type of the link target
|
-- resolve the type of the link target
|
||||||
self.conn:run(
|
self.conn:run(
|
||||||
'LC_ALL=C ls -naLl --color=never' .. path_postfix .. ' 2> /dev/null',
|
"LC_ALL=C ls -naLl --color=never" .. path_postfix .. " 2> /dev/null",
|
||||||
function(link_err, link_lines)
|
function(link_err, link_lines)
|
||||||
-- Ignore exit code 1. That just means one of the links could not be resolved.
|
-- Ignore exit code 1. That just means one of the links could not be resolved.
|
||||||
if link_err and not link_err:match('^1:') then
|
if link_err and not link_err:match("^1:") then
|
||||||
return callback(link_err)
|
return callback(link_err)
|
||||||
end
|
end
|
||||||
assert(link_lines)
|
assert(link_lines)
|
||||||
for _, line in ipairs(link_lines) do
|
for _, line in ipairs(link_lines) do
|
||||||
if line ~= '' and not line:match('^total') then
|
if line ~= "" and not line:match("^total") then
|
||||||
local ok, name, type, meta = pcall(parse_ls_line, line)
|
local ok, name, type, meta = pcall(parse_ls_line, line)
|
||||||
if ok and name ~= '.' and name ~= '..' then
|
if ok and name ~= "." and name ~= ".." then
|
||||||
local cache_entry = entries[name]
|
local cache_entry = entries[name]
|
||||||
if cache_entry[FIELD_TYPE] == 'link' then
|
if cache_entry[FIELD_TYPE] == "link" then
|
||||||
cache_entry[FIELD_META].link_stat = {
|
cache_entry[FIELD_META].link_stat = {
|
||||||
type = type,
|
type = type,
|
||||||
size = meta.size,
|
size = meta.size,
|
||||||
|
|
@ -217,40 +217,40 @@ end
|
||||||
---@param path string
|
---@param path string
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function SSHFS:mkdir(path, callback)
|
function SSHFS:mkdir(path, callback)
|
||||||
self.conn:run(string.format('mkdir -p %s', shellescape(path)), callback)
|
self.conn:run(string.format("mkdir -p %s", shellescape(path)), callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param path string
|
---@param path string
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function SSHFS:touch(path, callback)
|
function SSHFS:touch(path, callback)
|
||||||
self.conn:run(string.format('touch %s', shellescape(path)), callback)
|
self.conn:run(string.format("touch %s", shellescape(path)), callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param path string
|
---@param path string
|
||||||
---@param link string
|
---@param link string
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function SSHFS:mklink(path, link, callback)
|
function SSHFS:mklink(path, link, callback)
|
||||||
self.conn:run(string.format('ln -s %s %s', shellescape(link), shellescape(path)), callback)
|
self.conn:run(string.format("ln -s %s %s", shellescape(link), shellescape(path)), callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param path string
|
---@param path string
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function SSHFS:rm(path, callback)
|
function SSHFS:rm(path, callback)
|
||||||
self.conn:run(string.format('rm -rf %s', shellescape(path)), callback)
|
self.conn:run(string.format("rm -rf %s", shellescape(path)), callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param src string
|
---@param src string
|
||||||
---@param dest string
|
---@param dest string
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function SSHFS:mv(src, dest, callback)
|
function SSHFS:mv(src, dest, callback)
|
||||||
self.conn:run(string.format('mv %s %s', shellescape(src), shellescape(dest)), callback)
|
self.conn:run(string.format("mv %s %s", shellescape(src), shellescape(dest)), callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param src string
|
---@param src string
|
||||||
---@param dest string
|
---@param dest string
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
function SSHFS:cp(src, dest, callback)
|
function SSHFS:cp(src, dest, callback)
|
||||||
self.conn:run(string.format('cp -r %s %s', shellescape(src), shellescape(dest)), callback)
|
self.conn:run(string.format("cp -r %s %s", shellescape(src), shellescape(dest)), callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
function SSHFS:get_dir_meta(url)
|
function SSHFS:get_dir_meta(url)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
local cache = require('oil.cache')
|
local cache = require("oil.cache")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
---@param url string
|
---@param url string
|
||||||
|
|
@ -32,24 +32,24 @@ end
|
||||||
---@param entry_type oil.EntryType
|
---@param entry_type oil.EntryType
|
||||||
---@return oil.InternalEntry
|
---@return oil.InternalEntry
|
||||||
M.test_set = function(path, entry_type)
|
M.test_set = function(path, entry_type)
|
||||||
if path == '/' then
|
if path == "/" then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
local parent = vim.fn.fnamemodify(path, ':h')
|
local parent = vim.fn.fnamemodify(path, ":h")
|
||||||
if parent ~= path then
|
if parent ~= path then
|
||||||
M.test_set(parent, 'directory')
|
M.test_set(parent, "directory")
|
||||||
end
|
end
|
||||||
parent = util.addslash(parent)
|
parent = util.addslash(parent)
|
||||||
if not dir_listing[parent] then
|
if not dir_listing[parent] then
|
||||||
dir_listing[parent] = {}
|
dir_listing[parent] = {}
|
||||||
end
|
end
|
||||||
local name = vim.fn.fnamemodify(path, ':t')
|
local name = vim.fn.fnamemodify(path, ":t")
|
||||||
local entry = {
|
local entry = {
|
||||||
name = name,
|
name = name,
|
||||||
entry_type = entry_type,
|
entry_type = entry_type,
|
||||||
}
|
}
|
||||||
table.insert(dir_listing[parent], entry)
|
table.insert(dir_listing[parent], entry)
|
||||||
local parent_url = 'oil-test://' .. parent
|
local parent_url = "oil-test://" .. parent
|
||||||
return cache.create_and_store_entry(parent_url, entry.name, entry.entry_type)
|
return cache.create_and_store_entry(parent_url, entry.name, entry.entry_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -68,12 +68,12 @@ end
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@return string
|
---@return string
|
||||||
M.render_action = function(action)
|
M.render_action = function(action)
|
||||||
if action.type == 'create' or action.type == 'delete' then
|
if action.type == "create" or action.type == "delete" then
|
||||||
return string.format('%s %s', action.type:upper(), action.url)
|
return string.format("%s %s", action.type:upper(), action.url)
|
||||||
elseif action.type == 'move' or action.type == 'copy' then
|
elseif action.type == "move" or action.type == "copy" then
|
||||||
return string.format(' %s %s -> %s', action.type:upper(), action.src_url, action.dest_url)
|
return string.format(" %s %s -> %s", action.type:upper(), action.src_url, action.dest_url)
|
||||||
else
|
else
|
||||||
error('Bad action type')
|
error("Bad action type")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
|
|
||||||
if fs.is_mac then
|
if fs.is_mac then
|
||||||
return require('oil.adapters.trash.mac')
|
return require("oil.adapters.trash.mac")
|
||||||
elseif fs.is_windows then
|
elseif fs.is_windows then
|
||||||
return require('oil.adapters.trash.windows')
|
return require("oil.adapters.trash.windows")
|
||||||
else
|
else
|
||||||
return require('oil.adapters.trash.freedesktop')
|
return require("oil.adapters.trash.freedesktop")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
-- Based on the FreeDesktop.org trash specification
|
-- Based on the FreeDesktop.org trash specification
|
||||||
-- https://specifications.freedesktop.org/trash/1.0/
|
-- https://specifications.freedesktop.org/trash/1.0/
|
||||||
local cache = require('oil.cache')
|
local cache = require("oil.cache")
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
local files = require('oil.adapters.files')
|
local files = require("oil.adapters.files")
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
|
|
||||||
local uv = vim.uv or vim.loop
|
local uv = vim.uv or vim.loop
|
||||||
local FIELD_META = constants.FIELD_META
|
local FIELD_META = constants.FIELD_META
|
||||||
|
|
@ -14,8 +14,8 @@ local M = {}
|
||||||
|
|
||||||
local function ensure_trash_dir(path)
|
local function ensure_trash_dir(path)
|
||||||
local mode = 448 -- 0700
|
local mode = 448 -- 0700
|
||||||
fs.mkdirp(fs.join(path, 'info'), mode)
|
fs.mkdirp(fs.join(path, "info"), mode)
|
||||||
fs.mkdirp(fs.join(path, 'files'), mode)
|
fs.mkdirp(fs.join(path, "files"), mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Gets the location of the home trash dir, creating it if necessary
|
---Gets the location of the home trash dir, creating it if necessary
|
||||||
|
|
@ -23,9 +23,9 @@ end
|
||||||
local function get_home_trash_dir()
|
local function get_home_trash_dir()
|
||||||
local xdg_home = vim.env.XDG_DATA_HOME
|
local xdg_home = vim.env.XDG_DATA_HOME
|
||||||
if not xdg_home then
|
if not xdg_home then
|
||||||
xdg_home = fs.join(assert(uv.os_homedir()), '.local', 'share')
|
xdg_home = fs.join(assert(uv.os_homedir()), ".local", "share")
|
||||||
end
|
end
|
||||||
local trash_dir = fs.join(xdg_home, 'Trash')
|
local trash_dir = fs.join(xdg_home, "Trash")
|
||||||
ensure_trash_dir(trash_dir)
|
ensure_trash_dir(trash_dir)
|
||||||
return trash_dir
|
return trash_dir
|
||||||
end
|
end
|
||||||
|
|
@ -43,13 +43,13 @@ end
|
||||||
local function get_top_trash_dirs(path)
|
local function get_top_trash_dirs(path)
|
||||||
local dirs = {}
|
local dirs = {}
|
||||||
local dev = (uv.fs_lstat(path) or {}).dev
|
local dev = (uv.fs_lstat(path) or {}).dev
|
||||||
local top_trash_dirs = vim.fs.find('.Trash', { upward = true, path = path, limit = math.huge })
|
local top_trash_dirs = vim.fs.find(".Trash", { upward = true, path = path, limit = math.huge })
|
||||||
for _, top_trash_dir in ipairs(top_trash_dirs) do
|
for _, top_trash_dir in ipairs(top_trash_dirs) do
|
||||||
local stat = uv.fs_lstat(top_trash_dir)
|
local stat = uv.fs_lstat(top_trash_dir)
|
||||||
if stat and not dev then
|
if stat and not dev then
|
||||||
dev = stat.dev
|
dev = stat.dev
|
||||||
end
|
end
|
||||||
if stat and stat.dev == dev and stat.type == 'directory' and is_sticky(stat.mode) then
|
if stat and stat.dev == dev and stat.type == "directory" and is_sticky(stat.mode) then
|
||||||
local trash_dir = fs.join(top_trash_dir, tostring(uv.getuid()))
|
local trash_dir = fs.join(top_trash_dir, tostring(uv.getuid()))
|
||||||
ensure_trash_dir(trash_dir)
|
ensure_trash_dir(trash_dir)
|
||||||
table.insert(dirs, trash_dir)
|
table.insert(dirs, trash_dir)
|
||||||
|
|
@ -58,7 +58,7 @@ local function get_top_trash_dirs(path)
|
||||||
|
|
||||||
-- Also search for the .Trash-$uid
|
-- Also search for the .Trash-$uid
|
||||||
top_trash_dirs = vim.fs.find(
|
top_trash_dirs = vim.fs.find(
|
||||||
string.format('.Trash-%d', uv.getuid()),
|
string.format(".Trash-%d", uv.getuid()),
|
||||||
{ upward = true, path = path, limit = math.huge }
|
{ upward = true, path = path, limit = math.huge }
|
||||||
)
|
)
|
||||||
for _, top_trash_dir in ipairs(top_trash_dirs) do
|
for _, top_trash_dir in ipairs(top_trash_dirs) do
|
||||||
|
|
@ -91,14 +91,14 @@ local function get_write_trash_dir(path)
|
||||||
return top_trash_dirs[1]
|
return top_trash_dirs[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
local parent = vim.fn.fnamemodify(path, ':h')
|
local parent = vim.fn.fnamemodify(path, ":h")
|
||||||
local next_parent = vim.fn.fnamemodify(parent, ':h')
|
local next_parent = vim.fn.fnamemodify(parent, ":h")
|
||||||
while parent ~= next_parent and uv.fs_lstat(next_parent).dev == dev do
|
while parent ~= next_parent and uv.fs_lstat(next_parent).dev == dev do
|
||||||
parent = next_parent
|
parent = next_parent
|
||||||
next_parent = vim.fn.fnamemodify(parent, ':h')
|
next_parent = vim.fn.fnamemodify(parent, ":h")
|
||||||
end
|
end
|
||||||
|
|
||||||
local top_trash = fs.join(parent, string.format('.Trash-%d', uv.getuid()))
|
local top_trash = fs.join(parent, string.format(".Trash-%d", uv.getuid()))
|
||||||
ensure_trash_dir(top_trash)
|
ensure_trash_dir(top_trash)
|
||||||
return top_trash
|
return top_trash
|
||||||
end
|
end
|
||||||
|
|
@ -116,7 +116,7 @@ end
|
||||||
M.normalize_url = function(url, callback)
|
M.normalize_url = function(url, callback)
|
||||||
local scheme, path = util.parse_url(url)
|
local scheme, path = util.parse_url(url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(path), ':p')
|
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(path), ":p")
|
||||||
uv.fs_realpath(
|
uv.fs_realpath(
|
||||||
os_path,
|
os_path,
|
||||||
vim.schedule_wrap(function(err, new_os_path)
|
vim.schedule_wrap(function(err, new_os_path)
|
||||||
|
|
@ -140,10 +140,10 @@ M.get_entry_path = function(url, entry, cb)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local path = fs.os_to_posix_path(trash_info.trash_file)
|
local path = fs.os_to_posix_path(trash_info.trash_file)
|
||||||
if meta.stat.type == 'directory' then
|
if meta.stat.type == "directory" then
|
||||||
path = util.addslash(path)
|
path = util.addslash(path)
|
||||||
end
|
end
|
||||||
cb('oil://' .. path)
|
cb("oil://" .. path)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@class oil.TrashInfo
|
---@class oil.TrashInfo
|
||||||
|
|
@ -156,10 +156,10 @@ end
|
||||||
---@param info_file string
|
---@param info_file string
|
||||||
---@param cb fun(err?: string, info?: oil.TrashInfo)
|
---@param cb fun(err?: string, info?: oil.TrashInfo)
|
||||||
local function read_trash_info(info_file, cb)
|
local function read_trash_info(info_file, cb)
|
||||||
if not vim.endswith(info_file, '.trashinfo') then
|
if not vim.endswith(info_file, ".trashinfo") then
|
||||||
return cb('File is not .trashinfo')
|
return cb("File is not .trashinfo")
|
||||||
end
|
end
|
||||||
uv.fs_open(info_file, 'r', 448, function(err, fd)
|
uv.fs_open(info_file, "r", 448, function(err, fd)
|
||||||
if err then
|
if err then
|
||||||
return cb(err)
|
return cb(err)
|
||||||
end
|
end
|
||||||
|
|
@ -182,32 +182,32 @@ local function read_trash_info(info_file, cb)
|
||||||
local trash_info = {
|
local trash_info = {
|
||||||
info_file = info_file,
|
info_file = info_file,
|
||||||
}
|
}
|
||||||
local lines = vim.split(content, '\r?\n')
|
local lines = vim.split(content, "\r?\n")
|
||||||
if lines[1] ~= '[Trash Info]' then
|
if lines[1] ~= "[Trash Info]" then
|
||||||
return cb('File missing [Trash Info] header')
|
return cb("File missing [Trash Info] header")
|
||||||
end
|
end
|
||||||
local trash_base = vim.fn.fnamemodify(info_file, ':h:h')
|
local trash_base = vim.fn.fnamemodify(info_file, ":h:h")
|
||||||
for _, line in ipairs(lines) do
|
for _, line in ipairs(lines) do
|
||||||
local key, value = unpack(vim.split(line, '=', { plain = true, trimempty = true }))
|
local key, value = unpack(vim.split(line, "=", { plain = true, trimempty = true }))
|
||||||
if key == 'Path' and not trash_info.original_path then
|
if key == "Path" and not trash_info.original_path then
|
||||||
if not vim.startswith(value, '/') then
|
if not vim.startswith(value, "/") then
|
||||||
value = fs.join(trash_base, value)
|
value = fs.join(trash_base, value)
|
||||||
end
|
end
|
||||||
trash_info.original_path = value
|
trash_info.original_path = value
|
||||||
elseif key == 'DeletionDate' and not trash_info.deletion_date then
|
elseif key == "DeletionDate" and not trash_info.deletion_date then
|
||||||
trash_info.deletion_date = vim.fn.strptime('%Y-%m-%dT%H:%M:%S', value)
|
trash_info.deletion_date = vim.fn.strptime("%Y-%m-%dT%H:%M:%S", value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not trash_info.original_path or not trash_info.deletion_date then
|
if not trash_info.original_path or not trash_info.deletion_date then
|
||||||
return cb('File missing required fields')
|
return cb("File missing required fields")
|
||||||
end
|
end
|
||||||
|
|
||||||
local basename = vim.fn.fnamemodify(info_file, ':t:r')
|
local basename = vim.fn.fnamemodify(info_file, ":t:r")
|
||||||
trash_info.trash_file = fs.join(trash_base, 'files', basename)
|
trash_info.trash_file = fs.join(trash_base, "files", basename)
|
||||||
uv.fs_lstat(trash_info.trash_file, function(trash_stat_err, trash_stat)
|
uv.fs_lstat(trash_info.trash_file, function(trash_stat_err, trash_stat)
|
||||||
if trash_stat_err then
|
if trash_stat_err then
|
||||||
cb('.trashinfo file points to non-existant file')
|
cb(".trashinfo file points to non-existant file")
|
||||||
else
|
else
|
||||||
trash_info.stat = trash_stat
|
trash_info.stat = trash_stat
|
||||||
---@cast trash_info oil.TrashInfo
|
---@cast trash_info oil.TrashInfo
|
||||||
|
|
@ -244,14 +244,14 @@ M.list = function(url, column_defs, cb)
|
||||||
-- The first trash dir is a special case; it is in the home directory and we should only show
|
-- The first trash dir is a special case; it is in the home directory and we should only show
|
||||||
-- all entries if we are in the top root path "/"
|
-- all entries if we are in the top root path "/"
|
||||||
if trash_idx == 1 then
|
if trash_idx == 1 then
|
||||||
show_all_files = path == '/'
|
show_all_files = path == "/"
|
||||||
end
|
end
|
||||||
|
|
||||||
local info_dir = fs.join(trash_dir, 'info')
|
local info_dir = fs.join(trash_dir, "info")
|
||||||
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
|
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
|
||||||
uv.fs_opendir(info_dir, function(open_err, fd)
|
uv.fs_opendir(info_dir, function(open_err, fd)
|
||||||
if open_err then
|
if open_err then
|
||||||
if open_err:match('^ENOENT: no such file or directory') then
|
if open_err:match("^ENOENT: no such file or directory") then
|
||||||
-- If the directory doesn't exist, treat the list as a success. We will be able to traverse
|
-- If the directory doesn't exist, treat the list as a success. We will be able to traverse
|
||||||
-- and edit a not-yet-existing directory.
|
-- and edit a not-yet-existing directory.
|
||||||
return read_next_trash_dir()
|
return read_next_trash_dir()
|
||||||
|
|
@ -286,12 +286,12 @@ M.list = function(url, column_defs, cb)
|
||||||
-- files.
|
-- files.
|
||||||
poll()
|
poll()
|
||||||
else
|
else
|
||||||
local parent = util.addslash(vim.fn.fnamemodify(info.original_path, ':h'))
|
local parent = util.addslash(vim.fn.fnamemodify(info.original_path, ":h"))
|
||||||
if path == parent or show_all_files then
|
if path == parent or show_all_files then
|
||||||
local name = vim.fn.fnamemodify(info.trash_file, ':t')
|
local name = vim.fn.fnamemodify(info.trash_file, ":t")
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local cache_entry = cache.create_entry(url, name, info.stat.type)
|
local cache_entry = cache.create_entry(url, name, info.stat.type)
|
||||||
local display_name = vim.fn.fnamemodify(info.original_path, ':t')
|
local display_name = vim.fn.fnamemodify(info.original_path, ":t")
|
||||||
cache_entry[FIELD_META] = {
|
cache_entry[FIELD_META] = {
|
||||||
stat = info.stat,
|
stat = info.stat,
|
||||||
trash_info = info,
|
trash_info = info,
|
||||||
|
|
@ -302,12 +302,12 @@ M.list = function(url, column_defs, cb)
|
||||||
if path ~= parent and (show_all_files or fs.is_subpath(path, parent)) then
|
if path ~= parent and (show_all_files or fs.is_subpath(path, parent)) then
|
||||||
local name = parent:sub(path:len() + 1)
|
local name = parent:sub(path:len() + 1)
|
||||||
local next_par = vim.fs.dirname(name)
|
local next_par = vim.fs.dirname(name)
|
||||||
while next_par ~= '.' do
|
while next_par ~= "." do
|
||||||
name = next_par
|
name = next_par
|
||||||
next_par = vim.fs.dirname(name)
|
next_par = vim.fs.dirname(name)
|
||||||
end
|
end
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local cache_entry = cache.create_entry(url, name, 'directory')
|
local cache_entry = cache.create_entry(url, name, "directory")
|
||||||
|
|
||||||
cache_entry[FIELD_META] = {
|
cache_entry[FIELD_META] = {
|
||||||
stat = info.stat,
|
stat = info.stat,
|
||||||
|
|
@ -348,7 +348,7 @@ local file_columns = {}
|
||||||
local current_year
|
local current_year
|
||||||
-- Make sure we run this import-time effect in the main loop (mostly for tests)
|
-- Make sure we run this import-time effect in the main loop (mostly for tests)
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
current_year = vim.fn.strftime('%Y')
|
current_year = vim.fn.strftime("%Y")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
file_columns.mtime = {
|
file_columns.mtime = {
|
||||||
|
|
@ -368,11 +368,11 @@ file_columns.mtime = {
|
||||||
if fmt then
|
if fmt then
|
||||||
ret = vim.fn.strftime(fmt, time)
|
ret = vim.fn.strftime(fmt, time)
|
||||||
else
|
else
|
||||||
local year = vim.fn.strftime('%Y', time)
|
local year = vim.fn.strftime("%Y", time)
|
||||||
if year ~= current_year then
|
if year ~= current_year then
|
||||||
ret = vim.fn.strftime('%b %d %Y', time)
|
ret = vim.fn.strftime("%b %d %Y", time)
|
||||||
else
|
else
|
||||||
ret = vim.fn.strftime('%b %d %H:%M', time)
|
ret = vim.fn.strftime("%b %d %H:%M", time)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
|
|
@ -393,11 +393,11 @@ file_columns.mtime = {
|
||||||
local fmt = conf and conf.format
|
local fmt = conf and conf.format
|
||||||
local pattern
|
local pattern
|
||||||
if fmt then
|
if fmt then
|
||||||
pattern = fmt:gsub('%%.', '%%S+')
|
pattern = fmt:gsub("%%.", "%%S+")
|
||||||
else
|
else
|
||||||
pattern = '%S+%s+%d+%s+%d%d:?%d%d'
|
pattern = "%S+%s+%d+%s+%d%d:?%d%d"
|
||||||
end
|
end
|
||||||
return line:match('^(' .. pattern .. ')%s+(.+)$')
|
return line:match("^(" .. pattern .. ")%s+(.+)$")
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -407,26 +407,25 @@ M.get_column = function(name)
|
||||||
return file_columns[name]
|
return file_columns[name]
|
||||||
end
|
end
|
||||||
|
|
||||||
M.supported_cross_adapter_actions = { files = 'move' }
|
M.supported_cross_adapter_actions = { files = "move" }
|
||||||
|
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@return boolean
|
---@return boolean
|
||||||
M.filter_action = function(action)
|
M.filter_action = function(action)
|
||||||
if action.type == 'create' then
|
if action.type == "create" then
|
||||||
return false
|
return false
|
||||||
elseif action.type == 'delete' then
|
elseif action.type == "delete" then
|
||||||
local entry = assert(cache.get_entry_by_url(action.url))
|
local entry = assert(cache.get_entry_by_url(action.url))
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
return meta ~= nil and meta.trash_info ~= nil
|
return meta ~= nil and meta.trash_info ~= nil
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
return src_adapter.name == 'files' or dest_adapter.name == 'files'
|
return src_adapter.name == "files" or dest_adapter.name == "files"
|
||||||
-- selene: allow(if_same_then_else)
|
elseif action.type == "copy" then
|
||||||
elseif action.type == 'copy' then
|
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
return src_adapter.name == 'files' or dest_adapter.name == 'files'
|
return src_adapter.name == "files" or dest_adapter.name == "files"
|
||||||
else
|
else
|
||||||
error(string.format("Bad action type '%s'", action.type))
|
error(string.format("Bad action type '%s'", action.type))
|
||||||
end
|
end
|
||||||
|
|
@ -435,7 +434,7 @@ end
|
||||||
---@param err oil.ParseError
|
---@param err oil.ParseError
|
||||||
---@return boolean
|
---@return boolean
|
||||||
M.filter_error = function(err)
|
M.filter_error = function(err)
|
||||||
if err.message == 'Duplicate filename' then
|
if err.message == "Duplicate filename" then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
|
|
@ -444,44 +443,44 @@ end
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@return string
|
---@return string
|
||||||
M.render_action = function(action)
|
M.render_action = function(action)
|
||||||
if action.type == 'delete' then
|
if action.type == "delete" then
|
||||||
local entry = assert(cache.get_entry_by_url(action.url))
|
local entry = assert(cache.get_entry_by_url(action.url))
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
---@type oil.TrashInfo
|
---@type oil.TrashInfo
|
||||||
local trash_info = assert(meta).trash_info
|
local trash_info = assert(meta).trash_info
|
||||||
local short_path = fs.shorten_path(trash_info.original_path)
|
local short_path = fs.shorten_path(trash_info.original_path)
|
||||||
return string.format(' PURGE %s', short_path)
|
return string.format(" PURGE %s", short_path)
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if src_adapter.name == 'files' then
|
if src_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.src_url)
|
local _, path = util.parse_url(action.src_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local short_path = files.to_short_os_path(path, action.entry_type)
|
local short_path = files.to_short_os_path(path, action.entry_type)
|
||||||
return string.format(' TRASH %s', short_path)
|
return string.format(" TRASH %s", short_path)
|
||||||
elseif dest_adapter.name == 'files' then
|
elseif dest_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.dest_url)
|
local _, path = util.parse_url(action.dest_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local short_path = files.to_short_os_path(path, action.entry_type)
|
local short_path = files.to_short_os_path(path, action.entry_type)
|
||||||
return string.format('RESTORE %s', short_path)
|
return string.format("RESTORE %s", short_path)
|
||||||
else
|
else
|
||||||
error('Must be moving files into or out of trash')
|
error("Must be moving files into or out of trash")
|
||||||
end
|
end
|
||||||
elseif action.type == 'copy' then
|
elseif action.type == "copy" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if src_adapter.name == 'files' then
|
if src_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.src_url)
|
local _, path = util.parse_url(action.src_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local short_path = files.to_short_os_path(path, action.entry_type)
|
local short_path = files.to_short_os_path(path, action.entry_type)
|
||||||
return string.format(' COPY %s -> TRASH', short_path)
|
return string.format(" COPY %s -> TRASH", short_path)
|
||||||
elseif dest_adapter.name == 'files' then
|
elseif dest_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.dest_url)
|
local _, path = util.parse_url(action.dest_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local short_path = files.to_short_os_path(path, action.entry_type)
|
local short_path = files.to_short_os_path(path, action.entry_type)
|
||||||
return string.format('RESTORE %s', short_path)
|
return string.format("RESTORE %s", short_path)
|
||||||
else
|
else
|
||||||
error('Must be copying files into or out of trash')
|
error("Must be copying files into or out of trash")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
error(string.format("Bad action type '%s'", action.type))
|
error(string.format("Bad action type '%s'", action.type))
|
||||||
|
|
@ -491,7 +490,7 @@ end
|
||||||
---@param trash_info oil.TrashInfo
|
---@param trash_info oil.TrashInfo
|
||||||
---@param cb fun(err?: string)
|
---@param cb fun(err?: string)
|
||||||
local function purge(trash_info, cb)
|
local function purge(trash_info, cb)
|
||||||
fs.recursive_delete('file', trash_info.info_file, function(err)
|
fs.recursive_delete("file", trash_info.info_file, function(err)
|
||||||
if err then
|
if err then
|
||||||
return cb(err)
|
return cb(err)
|
||||||
end
|
end
|
||||||
|
|
@ -506,15 +505,15 @@ end
|
||||||
local function write_info_file(path, info_path, cb)
|
local function write_info_file(path, info_path, cb)
|
||||||
uv.fs_open(
|
uv.fs_open(
|
||||||
info_path,
|
info_path,
|
||||||
'w',
|
"w",
|
||||||
448,
|
448,
|
||||||
vim.schedule_wrap(function(err, fd)
|
vim.schedule_wrap(function(err, fd)
|
||||||
if err then
|
if err then
|
||||||
return cb(err)
|
return cb(err)
|
||||||
end
|
end
|
||||||
assert(fd)
|
assert(fd)
|
||||||
local deletion_date = vim.fn.strftime('%Y-%m-%dT%H:%M:%S')
|
local deletion_date = vim.fn.strftime("%Y-%m-%dT%H:%M:%S")
|
||||||
local contents = string.format('[Trash Info]\nPath=%s\nDeletionDate=%s', path, deletion_date)
|
local contents = string.format("[Trash Info]\nPath=%s\nDeletionDate=%s", path, deletion_date)
|
||||||
uv.fs_write(fd, contents, function(write_err)
|
uv.fs_write(fd, contents, function(write_err)
|
||||||
uv.fs_close(fd, function(close_err)
|
uv.fs_close(fd, function(close_err)
|
||||||
cb(write_err or close_err)
|
cb(write_err or close_err)
|
||||||
|
|
@ -530,9 +529,9 @@ local function create_trash_info(path, cb)
|
||||||
local trash_dir = get_write_trash_dir(path)
|
local trash_dir = get_write_trash_dir(path)
|
||||||
local basename = vim.fs.basename(path)
|
local basename = vim.fs.basename(path)
|
||||||
local now = os.time()
|
local now = os.time()
|
||||||
local name = string.format('%s-%d.%d', basename, now, math.random(100000, 999999))
|
local name = string.format("%s-%d.%d", basename, now, math.random(100000, 999999))
|
||||||
local dest_path = fs.join(trash_dir, 'files', name)
|
local dest_path = fs.join(trash_dir, "files", name)
|
||||||
local dest_info = fs.join(trash_dir, 'info', name .. '.trashinfo')
|
local dest_info = fs.join(trash_dir, "info", name .. ".trashinfo")
|
||||||
uv.fs_lstat(path, function(err, stat)
|
uv.fs_lstat(path, function(err, stat)
|
||||||
if err then
|
if err then
|
||||||
return cb(err)
|
return cb(err)
|
||||||
|
|
@ -558,19 +557,19 @@ end
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@param cb fun(err: nil|string)
|
---@param cb fun(err: nil|string)
|
||||||
M.perform_action = function(action, cb)
|
M.perform_action = function(action, cb)
|
||||||
if action.type == 'delete' then
|
if action.type == "delete" then
|
||||||
local entry = assert(cache.get_entry_by_url(action.url))
|
local entry = assert(cache.get_entry_by_url(action.url))
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
---@type oil.TrashInfo
|
---@type oil.TrashInfo
|
||||||
local trash_info = assert(meta).trash_info
|
local trash_info = assert(meta).trash_info
|
||||||
purge(trash_info, cb)
|
purge(trash_info, cb)
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if src_adapter.name == 'files' then
|
if src_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.src_url)
|
local _, path = util.parse_url(action.src_url)
|
||||||
M.delete_to_trash(assert(path), cb)
|
M.delete_to_trash(assert(path), cb)
|
||||||
elseif dest_adapter.name == 'files' then
|
elseif dest_adapter.name == "files" then
|
||||||
-- Restore
|
-- Restore
|
||||||
local _, dest_path = util.parse_url(action.dest_url)
|
local _, dest_path = util.parse_url(action.dest_url)
|
||||||
assert(dest_path)
|
assert(dest_path)
|
||||||
|
|
@ -585,23 +584,23 @@ M.perform_action = function(action, cb)
|
||||||
uv.fs_unlink(trash_info.info_file, cb)
|
uv.fs_unlink(trash_info.info_file, cb)
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
error('Must be moving files into or out of trash')
|
error("Must be moving files into or out of trash")
|
||||||
end
|
end
|
||||||
elseif action.type == 'copy' then
|
elseif action.type == "copy" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if src_adapter.name == 'files' then
|
if src_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.src_url)
|
local _, path = util.parse_url(action.src_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
create_trash_info(path, function(err, trash_info)
|
create_trash_info(path, function(err, trash_info)
|
||||||
if err then
|
if err then
|
||||||
cb(err)
|
cb(err)
|
||||||
else
|
else
|
||||||
local stat_type = trash_info.stat.type or 'unknown'
|
local stat_type = trash_info.stat.type or "unknown"
|
||||||
fs.recursive_copy(stat_type, path, trash_info.trash_file, vim.schedule_wrap(cb))
|
fs.recursive_copy(stat_type, path, trash_info.trash_file, vim.schedule_wrap(cb))
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
elseif dest_adapter.name == 'files' then
|
elseif dest_adapter.name == "files" then
|
||||||
-- Restore
|
-- Restore
|
||||||
local _, dest_path = util.parse_url(action.dest_url)
|
local _, dest_path = util.parse_url(action.dest_url)
|
||||||
assert(dest_path)
|
assert(dest_path)
|
||||||
|
|
@ -611,10 +610,10 @@ M.perform_action = function(action, cb)
|
||||||
local trash_info = assert(meta).trash_info
|
local trash_info = assert(meta).trash_info
|
||||||
fs.recursive_copy(action.entry_type, trash_info.trash_file, dest_path, cb)
|
fs.recursive_copy(action.entry_type, trash_info.trash_file, dest_path, cb)
|
||||||
else
|
else
|
||||||
error('Must be moving files into or out of trash')
|
error("Must be moving files into or out of trash")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
cb(string.format('Bad action type: %s', action.type))
|
cb(string.format("Bad action type: %s", action.type))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -625,7 +624,7 @@ M.delete_to_trash = function(path, cb)
|
||||||
if err then
|
if err then
|
||||||
cb(err)
|
cb(err)
|
||||||
else
|
else
|
||||||
local stat_type = trash_info.stat.type or 'unknown'
|
local stat_type = trash_info.stat.type or "unknown"
|
||||||
fs.recursive_move(stat_type, path, trash_info.trash_file, vim.schedule_wrap(cb))
|
fs.recursive_move(stat_type, path, trash_info.trash_file, vim.schedule_wrap(cb))
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
local cache = require('oil.cache')
|
local cache = require("oil.cache")
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local files = require('oil.adapters.files')
|
local files = require("oil.adapters.files")
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
|
|
||||||
local uv = vim.uv or vim.loop
|
local uv = vim.uv or vim.loop
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ end
|
||||||
---Gets the location of the home trash dir, creating it if necessary
|
---Gets the location of the home trash dir, creating it if necessary
|
||||||
---@return string
|
---@return string
|
||||||
local function get_trash_dir()
|
local function get_trash_dir()
|
||||||
local trash_dir = fs.join(assert(uv.os_homedir()), '.Trash')
|
local trash_dir = fs.join(assert(uv.os_homedir()), ".Trash")
|
||||||
touch_dir(trash_dir)
|
touch_dir(trash_dir)
|
||||||
return trash_dir
|
return trash_dir
|
||||||
end
|
end
|
||||||
|
|
@ -25,7 +25,7 @@ end
|
||||||
M.normalize_url = function(url, callback)
|
M.normalize_url = function(url, callback)
|
||||||
local scheme, path = util.parse_url(url)
|
local scheme, path = util.parse_url(url)
|
||||||
assert(path)
|
assert(path)
|
||||||
callback(scheme .. '/')
|
callback(scheme .. "/")
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param url string
|
---@param url string
|
||||||
|
|
@ -34,8 +34,8 @@ end
|
||||||
M.get_entry_path = function(url, entry, cb)
|
M.get_entry_path = function(url, entry, cb)
|
||||||
local trash_dir = get_trash_dir()
|
local trash_dir = get_trash_dir()
|
||||||
local path = fs.join(trash_dir, entry.name)
|
local path = fs.join(trash_dir, entry.name)
|
||||||
if entry.type == 'directory' then
|
if entry.type == "directory" then
|
||||||
path = 'oil://' .. path
|
path = "oil://" .. path
|
||||||
end
|
end
|
||||||
cb(path)
|
cb(path)
|
||||||
end
|
end
|
||||||
|
|
@ -51,7 +51,7 @@ M.list = function(url, column_defs, cb)
|
||||||
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
|
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
|
||||||
uv.fs_opendir(trash_dir, function(open_err, fd)
|
uv.fs_opendir(trash_dir, function(open_err, fd)
|
||||||
if open_err then
|
if open_err then
|
||||||
if open_err:match('^ENOENT: no such file or directory') then
|
if open_err:match("^ENOENT: no such file or directory") then
|
||||||
-- If the directory doesn't exist, treat the list as a success. We will be able to traverse
|
-- If the directory doesn't exist, treat the list as a success. We will be able to traverse
|
||||||
-- and edit a not-yet-existing directory.
|
-- and edit a not-yet-existing directory.
|
||||||
return cb()
|
return cb()
|
||||||
|
|
@ -111,35 +111,35 @@ M.get_column = function(name)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
M.supported_cross_adapter_actions = { files = 'move' }
|
M.supported_cross_adapter_actions = { files = "move" }
|
||||||
|
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@return string
|
---@return string
|
||||||
M.render_action = function(action)
|
M.render_action = function(action)
|
||||||
if action.type == 'create' then
|
if action.type == "create" then
|
||||||
return string.format('CREATE %s', action.url)
|
return string.format("CREATE %s", action.url)
|
||||||
elseif action.type == 'delete' then
|
elseif action.type == "delete" then
|
||||||
return string.format(' PURGE %s', action.url)
|
return string.format(" PURGE %s", action.url)
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if src_adapter.name == 'files' then
|
if src_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.src_url)
|
local _, path = util.parse_url(action.src_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local short_path = files.to_short_os_path(path, action.entry_type)
|
local short_path = files.to_short_os_path(path, action.entry_type)
|
||||||
return string.format(' TRASH %s', short_path)
|
return string.format(" TRASH %s", short_path)
|
||||||
elseif dest_adapter.name == 'files' then
|
elseif dest_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.dest_url)
|
local _, path = util.parse_url(action.dest_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local short_path = files.to_short_os_path(path, action.entry_type)
|
local short_path = files.to_short_os_path(path, action.entry_type)
|
||||||
return string.format('RESTORE %s', short_path)
|
return string.format("RESTORE %s", short_path)
|
||||||
else
|
else
|
||||||
return string.format(' %s %s -> %s', action.type:upper(), action.src_url, action.dest_url)
|
return string.format(" %s %s -> %s", action.type:upper(), action.src_url, action.dest_url)
|
||||||
end
|
end
|
||||||
elseif action.type == 'copy' then
|
elseif action.type == "copy" then
|
||||||
return string.format(' %s %s -> %s', action.type:upper(), action.src_url, action.dest_url)
|
return string.format(" %s %s -> %s", action.type:upper(), action.src_url, action.dest_url)
|
||||||
else
|
else
|
||||||
error('Bad action type')
|
error("Bad action type")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -147,20 +147,20 @@ end
|
||||||
---@param cb fun(err: nil|string)
|
---@param cb fun(err: nil|string)
|
||||||
M.perform_action = function(action, cb)
|
M.perform_action = function(action, cb)
|
||||||
local trash_dir = get_trash_dir()
|
local trash_dir = get_trash_dir()
|
||||||
if action.type == 'create' then
|
if action.type == "create" then
|
||||||
local _, path = util.parse_url(action.url)
|
local _, path = util.parse_url(action.url)
|
||||||
assert(path)
|
assert(path)
|
||||||
path = trash_dir .. path
|
path = trash_dir .. path
|
||||||
if action.entry_type == 'directory' then
|
if action.entry_type == "directory" then
|
||||||
uv.fs_mkdir(path, 493, function(err)
|
uv.fs_mkdir(path, 493, function(err)
|
||||||
-- Ignore if the directory already exists
|
-- Ignore if the directory already exists
|
||||||
if not err or err:match('^EEXIST:') then
|
if not err or err:match("^EEXIST:") then
|
||||||
cb()
|
cb()
|
||||||
else
|
else
|
||||||
cb(err)
|
cb(err)
|
||||||
end
|
end
|
||||||
end) -- 0755
|
end) -- 0755
|
||||||
elseif action.entry_type == 'link' and action.link then
|
elseif action.entry_type == "link" and action.link then
|
||||||
local flags = nil
|
local flags = nil
|
||||||
local target = fs.posix_to_os_path(action.link)
|
local target = fs.posix_to_os_path(action.link)
|
||||||
---@diagnostic disable-next-line: param-type-mismatch
|
---@diagnostic disable-next-line: param-type-mismatch
|
||||||
|
|
@ -168,33 +168,33 @@ M.perform_action = function(action, cb)
|
||||||
else
|
else
|
||||||
fs.touch(path, config.new_file_mode, cb)
|
fs.touch(path, config.new_file_mode, cb)
|
||||||
end
|
end
|
||||||
elseif action.type == 'delete' then
|
elseif action.type == "delete" then
|
||||||
local _, path = util.parse_url(action.url)
|
local _, path = util.parse_url(action.url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local fullpath = trash_dir .. path
|
local fullpath = trash_dir .. path
|
||||||
fs.recursive_delete(action.entry_type, fullpath, cb)
|
fs.recursive_delete(action.entry_type, fullpath, cb)
|
||||||
elseif action.type == 'move' or action.type == 'copy' then
|
elseif action.type == "move" or action.type == "copy" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
local _, src_path = util.parse_url(action.src_url)
|
local _, src_path = util.parse_url(action.src_url)
|
||||||
local _, dest_path = util.parse_url(action.dest_url)
|
local _, dest_path = util.parse_url(action.dest_url)
|
||||||
assert(src_path and dest_path)
|
assert(src_path and dest_path)
|
||||||
if src_adapter.name == 'files' then
|
if src_adapter.name == "files" then
|
||||||
dest_path = trash_dir .. dest_path
|
dest_path = trash_dir .. dest_path
|
||||||
elseif dest_adapter.name == 'files' then
|
elseif dest_adapter.name == "files" then
|
||||||
src_path = trash_dir .. src_path
|
src_path = trash_dir .. src_path
|
||||||
else
|
else
|
||||||
dest_path = trash_dir .. dest_path
|
dest_path = trash_dir .. dest_path
|
||||||
src_path = trash_dir .. src_path
|
src_path = trash_dir .. src_path
|
||||||
end
|
end
|
||||||
|
|
||||||
if action.type == 'move' then
|
if action.type == "move" then
|
||||||
fs.recursive_move(action.entry_type, src_path, dest_path, cb)
|
fs.recursive_move(action.entry_type, src_path, dest_path, cb)
|
||||||
else
|
else
|
||||||
fs.recursive_copy(action.entry_type, src_path, dest_path, cb)
|
fs.recursive_copy(action.entry_type, src_path, dest_path, cb)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
cb(string.format('Bad action type: %s', action.type))
|
cb(string.format("Bad action type: %s", action.type))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -212,8 +212,8 @@ M.delete_to_trash = function(path, cb)
|
||||||
end
|
end
|
||||||
assert(src_stat)
|
assert(src_stat)
|
||||||
if uv.fs_lstat(dest) then
|
if uv.fs_lstat(dest) then
|
||||||
local date_str = vim.fn.strftime(' %Y-%m-%dT%H:%M:%S')
|
local date_str = vim.fn.strftime(" %Y-%m-%dT%H:%M:%S")
|
||||||
local name_pieces = vim.split(basename, '.', { plain = true })
|
local name_pieces = vim.split(basename, ".", { plain = true })
|
||||||
if #name_pieces > 1 then
|
if #name_pieces > 1 then
|
||||||
table.insert(name_pieces, #name_pieces - 1, date_str)
|
table.insert(name_pieces, #name_pieces - 1, date_str)
|
||||||
basename = table.concat(name_pieces)
|
basename = table.concat(name_pieces)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local uv = vim.uv or vim.loop
|
local uv = vim.uv or vim.loop
|
||||||
local cache = require('oil.cache')
|
local cache = require("oil.cache")
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
local files = require('oil.adapters.files')
|
local files = require("oil.adapters.files")
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local powershell_trash = require('oil.adapters.trash.windows.powershell-trash')
|
local powershell_trash = require("oil.adapters.trash.windows.powershell-trash")
|
||||||
|
|
||||||
local FIELD_META = constants.FIELD_META
|
local FIELD_META = constants.FIELD_META
|
||||||
local FIELD_TYPE = constants.FIELD_TYPE
|
local FIELD_TYPE = constants.FIELD_TYPE
|
||||||
|
|
@ -15,22 +15,22 @@ local M = {}
|
||||||
---@return string
|
---@return string
|
||||||
local function get_trash_dir()
|
local function get_trash_dir()
|
||||||
local cwd = assert(vim.fn.getcwd())
|
local cwd = assert(vim.fn.getcwd())
|
||||||
local trash_dir = cwd:sub(1, 3) .. '$Recycle.Bin'
|
local trash_dir = cwd:sub(1, 3) .. "$Recycle.Bin"
|
||||||
if vim.fn.isdirectory(trash_dir) == 1 then
|
if vim.fn.isdirectory(trash_dir) == 1 then
|
||||||
return trash_dir
|
return trash_dir
|
||||||
end
|
end
|
||||||
trash_dir = 'C:\\$Recycle.Bin'
|
trash_dir = "C:\\$Recycle.Bin"
|
||||||
if vim.fn.isdirectory(trash_dir) == 1 then
|
if vim.fn.isdirectory(trash_dir) == 1 then
|
||||||
return trash_dir
|
return trash_dir
|
||||||
end
|
end
|
||||||
error('No trash found')
|
error("No trash found")
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param path string
|
---@param path string
|
||||||
---@return string
|
---@return string
|
||||||
local win_addslash = function(path)
|
local win_addslash = function(path)
|
||||||
if not vim.endswith(path, '\\') then
|
if not vim.endswith(path, "\\") then
|
||||||
return path .. '\\'
|
return path .. "\\"
|
||||||
else
|
else
|
||||||
return path
|
return path
|
||||||
end
|
end
|
||||||
|
|
@ -61,7 +61,7 @@ M.list = function(url, column_defs, cb)
|
||||||
local raw_displayed_entries = vim.tbl_filter(
|
local raw_displayed_entries = vim.tbl_filter(
|
||||||
---@param entry {IsFolder: boolean, DeletionDate: integer, Name: string, Path: string, OriginalPath: string}
|
---@param entry {IsFolder: boolean, DeletionDate: integer, Name: string, Path: string, OriginalPath: string}
|
||||||
function(entry)
|
function(entry)
|
||||||
local parent = win_addslash(assert(vim.fn.fnamemodify(entry.OriginalPath, ':h')))
|
local parent = win_addslash(assert(vim.fn.fnamemodify(entry.OriginalPath, ":h")))
|
||||||
local is_in_path = path == parent
|
local is_in_path = path == parent
|
||||||
local is_subpath = fs.is_subpath(path, parent)
|
local is_subpath = fs.is_subpath(path, parent)
|
||||||
return is_in_path or is_subpath or show_all_files
|
return is_in_path or is_subpath or show_all_files
|
||||||
|
|
@ -72,27 +72,27 @@ M.list = function(url, column_defs, cb)
|
||||||
---@param entry {IsFolder: boolean, DeletionDate: integer, Name: string, Path: string, OriginalPath: string}
|
---@param entry {IsFolder: boolean, DeletionDate: integer, Name: string, Path: string, OriginalPath: string}
|
||||||
---@return {[1]:nil, [2]:string, [3]:string, [4]:{stat: uv_fs_t, trash_info: oil.WindowsTrashInfo, display_name: string}}
|
---@return {[1]:nil, [2]:string, [3]:string, [4]:{stat: uv_fs_t, trash_info: oil.WindowsTrashInfo, display_name: string}}
|
||||||
function(entry)
|
function(entry)
|
||||||
local parent = win_addslash(assert(vim.fn.fnamemodify(entry.OriginalPath, ':h')))
|
local parent = win_addslash(assert(vim.fn.fnamemodify(entry.OriginalPath, ":h")))
|
||||||
|
|
||||||
--- @type oil.InternalEntry
|
--- @type oil.InternalEntry
|
||||||
local cache_entry
|
local cache_entry
|
||||||
if path == parent or show_all_files then
|
if path == parent or show_all_files then
|
||||||
local deleted_file_tail = assert(vim.fn.fnamemodify(entry.Path, ':t'))
|
local deleted_file_tail = assert(vim.fn.fnamemodify(entry.Path, ":t"))
|
||||||
local deleted_file_head = assert(vim.fn.fnamemodify(entry.Path, ':h'))
|
local deleted_file_head = assert(vim.fn.fnamemodify(entry.Path, ":h"))
|
||||||
local info_file_head = deleted_file_head
|
local info_file_head = deleted_file_head
|
||||||
--- @type string?
|
--- @type string?
|
||||||
local info_file
|
local info_file
|
||||||
cache_entry =
|
cache_entry =
|
||||||
cache.create_entry(url, deleted_file_tail, entry.IsFolder and 'directory' or 'file')
|
cache.create_entry(url, deleted_file_tail, entry.IsFolder and "directory" or "file")
|
||||||
-- info_file on windows has the following format: $I<6 char hash>.<extension>
|
-- info_file on windows has the following format: $I<6 char hash>.<extension>
|
||||||
-- the hash is the same for the deleted file and the info file
|
-- the hash is the same for the deleted file and the info file
|
||||||
-- so, we take the hash (and extension) from the deleted file
|
-- so, we take the hash (and extension) from the deleted file
|
||||||
--
|
--
|
||||||
-- see https://superuser.com/questions/368890/how-does-the-recycle-bin-in-windows-work/1736690#1736690
|
-- see https://superuser.com/questions/368890/how-does-the-recycle-bin-in-windows-work/1736690#1736690
|
||||||
local info_file_tail = deleted_file_tail:match('^%$R(.*)$') --[[@as string?]]
|
local info_file_tail = deleted_file_tail:match("^%$R(.*)$") --[[@as string?]]
|
||||||
if info_file_tail then
|
if info_file_tail then
|
||||||
info_file_tail = '$I' .. info_file_tail
|
info_file_tail = "$I" .. info_file_tail
|
||||||
info_file = info_file_head .. '\\' .. info_file_tail
|
info_file = info_file_head .. "\\" .. info_file_tail
|
||||||
end
|
end
|
||||||
cache_entry[FIELD_META] = {
|
cache_entry[FIELD_META] = {
|
||||||
stat = nil,
|
stat = nil,
|
||||||
|
|
@ -109,10 +109,10 @@ M.list = function(url, column_defs, cb)
|
||||||
if path ~= parent and (show_all_files or fs.is_subpath(path, parent)) then
|
if path ~= parent and (show_all_files or fs.is_subpath(path, parent)) then
|
||||||
local name = parent:sub(path:len() + 1)
|
local name = parent:sub(path:len() + 1)
|
||||||
local next_par = vim.fs.dirname(name)
|
local next_par = vim.fs.dirname(name)
|
||||||
while next_par ~= '.' do
|
while next_par ~= "." do
|
||||||
name = next_par
|
name = next_par
|
||||||
next_par = vim.fs.dirname(name)
|
next_par = vim.fs.dirname(name)
|
||||||
cache_entry = cache.create_entry(url, name, 'directory')
|
cache_entry = cache.create_entry(url, name, "directory")
|
||||||
|
|
||||||
cache_entry[FIELD_META] = {}
|
cache_entry[FIELD_META] = {}
|
||||||
end
|
end
|
||||||
|
|
@ -132,7 +132,7 @@ end
|
||||||
local current_year
|
local current_year
|
||||||
-- Make sure we run this import-time effect in the main loop (mostly for tests)
|
-- Make sure we run this import-time effect in the main loop (mostly for tests)
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
current_year = vim.fn.strftime('%Y')
|
current_year = vim.fn.strftime("%Y")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local file_columns = {}
|
local file_columns = {}
|
||||||
|
|
@ -153,11 +153,11 @@ file_columns.mtime = {
|
||||||
if fmt then
|
if fmt then
|
||||||
ret = vim.fn.strftime(fmt, time)
|
ret = vim.fn.strftime(fmt, time)
|
||||||
else
|
else
|
||||||
local year = vim.fn.strftime('%Y', time)
|
local year = vim.fn.strftime("%Y", time)
|
||||||
if year ~= current_year then
|
if year ~= current_year then
|
||||||
ret = vim.fn.strftime('%b %d %Y', time)
|
ret = vim.fn.strftime("%b %d %Y", time)
|
||||||
else
|
else
|
||||||
ret = vim.fn.strftime('%b %d %H:%M', time)
|
ret = vim.fn.strftime("%b %d %H:%M", time)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
|
|
@ -178,11 +178,11 @@ file_columns.mtime = {
|
||||||
local fmt = conf and conf.format
|
local fmt = conf and conf.format
|
||||||
local pattern
|
local pattern
|
||||||
if fmt then
|
if fmt then
|
||||||
pattern = fmt:gsub('%%.', '%%S+')
|
pattern = fmt:gsub("%%.", "%%S+")
|
||||||
else
|
else
|
||||||
pattern = '%S+%s+%d+%s+%d%d:?%d%d'
|
pattern = "%S+%s+%d+%s+%d%d:?%d%d"
|
||||||
end
|
end
|
||||||
return line:match('^(' .. pattern .. ')%s+(.+)$')
|
return line:match("^(" .. pattern .. ")%s+(.+)$")
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,21 +195,20 @@ end
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@return boolean
|
---@return boolean
|
||||||
M.filter_action = function(action)
|
M.filter_action = function(action)
|
||||||
if action.type == 'create' then
|
if action.type == "create" then
|
||||||
return false
|
return false
|
||||||
elseif action.type == 'delete' then
|
elseif action.type == "delete" then
|
||||||
local entry = assert(cache.get_entry_by_url(action.url))
|
local entry = assert(cache.get_entry_by_url(action.url))
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
return meta ~= nil and meta.trash_info ~= nil
|
return meta ~= nil and meta.trash_info ~= nil
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
return src_adapter.name == 'files' or dest_adapter.name == 'files'
|
return src_adapter.name == "files" or dest_adapter.name == "files"
|
||||||
-- selene: allow(if_same_then_else)
|
elseif action.type == "copy" then
|
||||||
elseif action.type == 'copy' then
|
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
return src_adapter.name == 'files' or dest_adapter.name == 'files'
|
return src_adapter.name == "files" or dest_adapter.name == "files"
|
||||||
else
|
else
|
||||||
error(string.format("Bad action type '%s'", action.type))
|
error(string.format("Bad action type '%s'", action.type))
|
||||||
end
|
end
|
||||||
|
|
@ -220,7 +219,7 @@ end
|
||||||
M.normalize_url = function(url, callback)
|
M.normalize_url = function(url, callback)
|
||||||
local scheme, path = util.parse_url(url)
|
local scheme, path = util.parse_url(url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(path), ':p')
|
local os_path = vim.fn.fnamemodify(fs.posix_to_os_path(path), ":p")
|
||||||
assert(os_path)
|
assert(os_path)
|
||||||
uv.fs_realpath(
|
uv.fs_realpath(
|
||||||
os_path,
|
os_path,
|
||||||
|
|
@ -245,16 +244,16 @@ M.get_entry_path = function(url, entry, cb)
|
||||||
end
|
end
|
||||||
|
|
||||||
local path = fs.os_to_posix_path(trash_info.trash_file)
|
local path = fs.os_to_posix_path(trash_info.trash_file)
|
||||||
if entry.type == 'directory' then
|
if entry.type == "directory" then
|
||||||
path = win_addslash(path)
|
path = win_addslash(path)
|
||||||
end
|
end
|
||||||
cb('oil://' .. path)
|
cb("oil://" .. path)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param err oil.ParseError
|
---@param err oil.ParseError
|
||||||
---@return boolean
|
---@return boolean
|
||||||
M.filter_error = function(err)
|
M.filter_error = function(err)
|
||||||
if err.message == 'Duplicate filename' then
|
if err.message == "Duplicate filename" then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
|
|
@ -263,44 +262,44 @@ end
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@return string
|
---@return string
|
||||||
M.render_action = function(action)
|
M.render_action = function(action)
|
||||||
if action.type == 'delete' then
|
if action.type == "delete" then
|
||||||
local entry = assert(cache.get_entry_by_url(action.url))
|
local entry = assert(cache.get_entry_by_url(action.url))
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
---@type oil.WindowsTrashInfo
|
---@type oil.WindowsTrashInfo
|
||||||
local trash_info = assert(meta).trash_info
|
local trash_info = assert(meta).trash_info
|
||||||
local short_path = fs.shorten_path(trash_info.original_path)
|
local short_path = fs.shorten_path(trash_info.original_path)
|
||||||
return string.format(' PURGE %s', short_path)
|
return string.format(" PURGE %s", short_path)
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if src_adapter.name == 'files' then
|
if src_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.src_url)
|
local _, path = util.parse_url(action.src_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local short_path = files.to_short_os_path(path, action.entry_type)
|
local short_path = files.to_short_os_path(path, action.entry_type)
|
||||||
return string.format(' TRASH %s', short_path)
|
return string.format(" TRASH %s", short_path)
|
||||||
elseif dest_adapter.name == 'files' then
|
elseif dest_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.dest_url)
|
local _, path = util.parse_url(action.dest_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local short_path = files.to_short_os_path(path, action.entry_type)
|
local short_path = files.to_short_os_path(path, action.entry_type)
|
||||||
return string.format('RESTORE %s', short_path)
|
return string.format("RESTORE %s", short_path)
|
||||||
else
|
else
|
||||||
error('Must be moving files into or out of trash')
|
error("Must be moving files into or out of trash")
|
||||||
end
|
end
|
||||||
elseif action.type == 'copy' then
|
elseif action.type == "copy" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if src_adapter.name == 'files' then
|
if src_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.src_url)
|
local _, path = util.parse_url(action.src_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local short_path = files.to_short_os_path(path, action.entry_type)
|
local short_path = files.to_short_os_path(path, action.entry_type)
|
||||||
return string.format(' COPY %s -> TRASH', short_path)
|
return string.format(" COPY %s -> TRASH", short_path)
|
||||||
elseif dest_adapter.name == 'files' then
|
elseif dest_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.dest_url)
|
local _, path = util.parse_url(action.dest_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local short_path = files.to_short_os_path(path, action.entry_type)
|
local short_path = files.to_short_os_path(path, action.entry_type)
|
||||||
return string.format('RESTORE %s', short_path)
|
return string.format("RESTORE %s", short_path)
|
||||||
else
|
else
|
||||||
error('Must be copying files into or out of trash')
|
error("Must be copying files into or out of trash")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
error(string.format("Bad action type '%s'", action.type))
|
error(string.format("Bad action type '%s'", action.type))
|
||||||
|
|
@ -310,11 +309,11 @@ end
|
||||||
---@param trash_info oil.WindowsTrashInfo
|
---@param trash_info oil.WindowsTrashInfo
|
||||||
---@param cb fun(err?: string, raw_entries: oil.WindowsRawEntry[]?)
|
---@param cb fun(err?: string, raw_entries: oil.WindowsRawEntry[]?)
|
||||||
local purge = function(trash_info, cb)
|
local purge = function(trash_info, cb)
|
||||||
fs.recursive_delete('file', trash_info.info_file, function(err)
|
fs.recursive_delete("file", trash_info.info_file, function(err)
|
||||||
if err then
|
if err then
|
||||||
return cb(err)
|
return cb(err)
|
||||||
end
|
end
|
||||||
fs.recursive_delete('file', trash_info.trash_file, cb)
|
fs.recursive_delete("file", trash_info.trash_file, cb)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -322,7 +321,7 @@ end
|
||||||
---@param type string
|
---@param type string
|
||||||
---@param cb fun(err?: string, trash_info?: oil.TrashInfo)
|
---@param cb fun(err?: string, trash_info?: oil.TrashInfo)
|
||||||
local function create_trash_info_and_copy(path, type, cb)
|
local function create_trash_info_and_copy(path, type, cb)
|
||||||
local temp_path = path .. 'temp'
|
local temp_path = path .. "temp"
|
||||||
-- create a temporary copy on the same location
|
-- create a temporary copy on the same location
|
||||||
fs.recursive_copy(
|
fs.recursive_copy(
|
||||||
type,
|
type,
|
||||||
|
|
@ -347,19 +346,19 @@ end
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
---@param cb fun(err: nil|string)
|
---@param cb fun(err: nil|string)
|
||||||
M.perform_action = function(action, cb)
|
M.perform_action = function(action, cb)
|
||||||
if action.type == 'delete' then
|
if action.type == "delete" then
|
||||||
local entry = assert(cache.get_entry_by_url(action.url))
|
local entry = assert(cache.get_entry_by_url(action.url))
|
||||||
local meta = entry[FIELD_META] --[[@as {stat: uv_fs_t, trash_info: oil.WindowsTrashInfo, display_name: string}]]
|
local meta = entry[FIELD_META] --[[@as {stat: uv_fs_t, trash_info: oil.WindowsTrashInfo, display_name: string}]]
|
||||||
local trash_info = meta and meta.trash_info
|
local trash_info = meta and meta.trash_info
|
||||||
|
|
||||||
purge(trash_info, cb)
|
purge(trash_info, cb)
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if src_adapter.name == 'files' then
|
if src_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.src_url)
|
local _, path = util.parse_url(action.src_url)
|
||||||
M.delete_to_trash(assert(path), cb)
|
M.delete_to_trash(assert(path), cb)
|
||||||
elseif dest_adapter.name == 'files' then
|
elseif dest_adapter.name == "files" then
|
||||||
-- Restore
|
-- Restore
|
||||||
local _, dest_path = util.parse_url(action.dest_url)
|
local _, dest_path = util.parse_url(action.dest_url)
|
||||||
assert(dest_path)
|
assert(dest_path)
|
||||||
|
|
@ -374,16 +373,16 @@ M.perform_action = function(action, cb)
|
||||||
uv.fs_unlink(trash_info.info_file, cb)
|
uv.fs_unlink(trash_info.info_file, cb)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
elseif action.type == 'copy' then
|
elseif action.type == "copy" then
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
local src_adapter = assert(config.get_adapter_by_scheme(action.src_url))
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url))
|
||||||
if src_adapter.name == 'files' then
|
if src_adapter.name == "files" then
|
||||||
local _, path = util.parse_url(action.src_url)
|
local _, path = util.parse_url(action.src_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
path = fs.posix_to_os_path(path)
|
path = fs.posix_to_os_path(path)
|
||||||
local entry = assert(cache.get_entry_by_url(action.src_url))
|
local entry = assert(cache.get_entry_by_url(action.src_url))
|
||||||
create_trash_info_and_copy(path, entry[FIELD_TYPE], cb)
|
create_trash_info_and_copy(path, entry[FIELD_TYPE], cb)
|
||||||
elseif dest_adapter.name == 'files' then
|
elseif dest_adapter.name == "files" then
|
||||||
-- Restore
|
-- Restore
|
||||||
local _, dest_path = util.parse_url(action.dest_url)
|
local _, dest_path = util.parse_url(action.dest_url)
|
||||||
assert(dest_path)
|
assert(dest_path)
|
||||||
|
|
@ -393,14 +392,14 @@ M.perform_action = function(action, cb)
|
||||||
local trash_info = meta and meta.trash_info
|
local trash_info = meta and meta.trash_info
|
||||||
fs.recursive_copy(action.entry_type, trash_info.trash_file, dest_path, cb)
|
fs.recursive_copy(action.entry_type, trash_info.trash_file, dest_path, cb)
|
||||||
else
|
else
|
||||||
error('Must be moving files into or out of trash')
|
error("Must be moving files into or out of trash")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
cb(string.format('Bad action type: %s', action.type))
|
cb(string.format("Bad action type: %s", action.type))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
M.supported_cross_adapter_actions = { files = 'move' }
|
M.supported_cross_adapter_actions = { files = "move" }
|
||||||
|
|
||||||
---@param path string
|
---@param path string
|
||||||
---@param cb fun(err?: string)
|
---@param cb fun(err?: string)
|
||||||
|
|
|
||||||
|
|
@ -41,23 +41,23 @@ function PowershellConnection:_init(init_command)
|
||||||
-- 65001 is the UTF-8 codepage
|
-- 65001 is the UTF-8 codepage
|
||||||
-- powershell needs to be launched with the UTF-8 codepage to use it for both stdin and stdout
|
-- powershell needs to be launched with the UTF-8 codepage to use it for both stdin and stdout
|
||||||
local jid = vim.fn.jobstart({
|
local jid = vim.fn.jobstart({
|
||||||
'cmd',
|
"cmd",
|
||||||
'/c',
|
"/c",
|
||||||
'"chcp 65001 && powershell -NoProfile -NoLogo -ExecutionPolicy Bypass -NoExit -Command -"',
|
'"chcp 65001 && powershell -NoProfile -NoLogo -ExecutionPolicy Bypass -NoExit -Command -"',
|
||||||
}, {
|
}, {
|
||||||
---@param data string[]
|
---@param data string[]
|
||||||
on_stdout = function(_, data)
|
on_stdout = function(_, data)
|
||||||
for _, fragment in ipairs(data) do
|
for _, fragment in ipairs(data) do
|
||||||
if fragment:find('===DONE%((%a+)%)===') then
|
if fragment:find("===DONE%((%a+)%)===") then
|
||||||
self.is_reading_data = false
|
self.is_reading_data = false
|
||||||
local output = table.concat(self.stdout, '')
|
local output = table.concat(self.stdout, "")
|
||||||
local cb = self.commands[1].cb
|
local cb = self.commands[1].cb
|
||||||
table.remove(self.commands, 1)
|
table.remove(self.commands, 1)
|
||||||
local success = fragment:match('===DONE%((%a+)%)===')
|
local success = fragment:match("===DONE%((%a+)%)===")
|
||||||
if success == 'True' then
|
if success == "True" then
|
||||||
cb(nil, output)
|
cb(nil, output)
|
||||||
elseif success == 'False' then
|
elseif success == "False" then
|
||||||
cb(success .. ': ' .. output, output)
|
cb(success .. ": " .. output, output)
|
||||||
end
|
end
|
||||||
self.stdout = {}
|
self.stdout = {}
|
||||||
self:_consume()
|
self:_consume()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
-- A wrapper around trash operations using windows powershell
|
-- A wrapper around trash operations using windows powershell
|
||||||
local Powershell = require('oil.adapters.trash.windows.powershell-connection')
|
local Powershell = require("oil.adapters.trash.windows.powershell-connection")
|
||||||
|
|
||||||
---@class oil.WindowsRawEntry
|
---@class oil.WindowsRawEntry
|
||||||
---@field IsFolder boolean
|
---@field IsFolder boolean
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local FIELD_ID = constants.FIELD_ID
|
local FIELD_ID = constants.FIELD_ID
|
||||||
|
|
@ -28,7 +28,7 @@ local _cached_id_fmt
|
||||||
M.format_id = function(id)
|
M.format_id = function(id)
|
||||||
if not _cached_id_fmt then
|
if not _cached_id_fmt then
|
||||||
local id_str_length = math.max(3, 1 + math.floor(math.log10(next_id)))
|
local id_str_length = math.max(3, 1 + math.floor(math.log10(next_id)))
|
||||||
_cached_id_fmt = '/%0' .. string.format('%d', id_str_length) .. 'd'
|
_cached_id_fmt = "/%0" .. string.format("%d", id_str_length) .. "d"
|
||||||
end
|
end
|
||||||
return _cached_id_fmt:format(id)
|
return _cached_id_fmt:format(id)
|
||||||
end
|
end
|
||||||
|
|
@ -125,8 +125,8 @@ end
|
||||||
M.get_entry_by_url = function(url)
|
M.get_entry_by_url = function(url)
|
||||||
local scheme, path = util.parse_url(url)
|
local scheme, path = util.parse_url(url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local parent_url = scheme .. vim.fn.fnamemodify(path, ':h')
|
local parent_url = scheme .. vim.fn.fnamemodify(path, ":h")
|
||||||
local basename = vim.fn.fnamemodify(path, ':t')
|
local basename = vim.fn.fnamemodify(path, ":t")
|
||||||
return M.list_url(parent_url)[basename]
|
return M.list_url(parent_url)[basename]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -135,7 +135,7 @@ end
|
||||||
M.get_parent_url = function(id)
|
M.get_parent_url = function(id)
|
||||||
local url = parent_url_by_id[id]
|
local url = parent_url_by_id[id]
|
||||||
if not url then
|
if not url then
|
||||||
error(string.format('Entry %d missing parent url', id))
|
error(string.format("Entry %d missing parent url", id))
|
||||||
end
|
end
|
||||||
return url
|
return url
|
||||||
end
|
end
|
||||||
|
|
@ -149,32 +149,32 @@ end
|
||||||
|
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
M.perform_action = function(action)
|
M.perform_action = function(action)
|
||||||
if action.type == 'create' then
|
if action.type == "create" then
|
||||||
local scheme, path = util.parse_url(action.url)
|
local scheme, path = util.parse_url(action.url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ':h'))
|
local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ":h"))
|
||||||
local name = vim.fn.fnamemodify(path, ':t')
|
local name = vim.fn.fnamemodify(path, ":t")
|
||||||
M.create_and_store_entry(parent_url, name, action.entry_type)
|
M.create_and_store_entry(parent_url, name, action.entry_type)
|
||||||
elseif action.type == 'delete' then
|
elseif action.type == "delete" then
|
||||||
local scheme, path = util.parse_url(action.url)
|
local scheme, path = util.parse_url(action.url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ':h'))
|
local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ":h"))
|
||||||
local name = vim.fn.fnamemodify(path, ':t')
|
local name = vim.fn.fnamemodify(path, ":t")
|
||||||
local entry = url_directory[parent_url][name]
|
local entry = url_directory[parent_url][name]
|
||||||
url_directory[parent_url][name] = nil
|
url_directory[parent_url][name] = nil
|
||||||
entries_by_id[entry[FIELD_ID]] = nil
|
entries_by_id[entry[FIELD_ID]] = nil
|
||||||
parent_url_by_id[entry[FIELD_ID]] = nil
|
parent_url_by_id[entry[FIELD_ID]] = nil
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
local src_scheme, src_path = util.parse_url(action.src_url)
|
local src_scheme, src_path = util.parse_url(action.src_url)
|
||||||
assert(src_path)
|
assert(src_path)
|
||||||
local src_parent_url = util.addslash(src_scheme .. vim.fn.fnamemodify(src_path, ':h'))
|
local src_parent_url = util.addslash(src_scheme .. vim.fn.fnamemodify(src_path, ":h"))
|
||||||
local src_name = vim.fn.fnamemodify(src_path, ':t')
|
local src_name = vim.fn.fnamemodify(src_path, ":t")
|
||||||
local entry = url_directory[src_parent_url][src_name]
|
local entry = url_directory[src_parent_url][src_name]
|
||||||
|
|
||||||
local dest_scheme, dest_path = util.parse_url(action.dest_url)
|
local dest_scheme, dest_path = util.parse_url(action.dest_url)
|
||||||
assert(dest_path)
|
assert(dest_path)
|
||||||
local dest_parent_url = util.addslash(dest_scheme .. vim.fn.fnamemodify(dest_path, ':h'))
|
local dest_parent_url = util.addslash(dest_scheme .. vim.fn.fnamemodify(dest_path, ":h"))
|
||||||
local dest_name = vim.fn.fnamemodify(dest_path, ':t')
|
local dest_name = vim.fn.fnamemodify(dest_path, ":t")
|
||||||
|
|
||||||
url_directory[src_parent_url][src_name] = nil
|
url_directory[src_parent_url][src_name] = nil
|
||||||
local dest_parent = url_directory[dest_parent_url]
|
local dest_parent = url_directory[dest_parent_url]
|
||||||
|
|
@ -188,14 +188,13 @@ M.perform_action = function(action)
|
||||||
parent_url_by_id[entry[FIELD_ID]] = dest_parent_url
|
parent_url_by_id[entry[FIELD_ID]] = dest_parent_url
|
||||||
entry[FIELD_NAME] = dest_name
|
entry[FIELD_NAME] = dest_name
|
||||||
util.update_moved_buffers(action.entry_type, action.src_url, action.dest_url)
|
util.update_moved_buffers(action.entry_type, action.src_url, action.dest_url)
|
||||||
elseif action.type == 'copy' then
|
elseif action.type == "copy" then
|
||||||
local scheme, path = util.parse_url(action.dest_url)
|
local scheme, path = util.parse_url(action.dest_url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ':h'))
|
local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ":h"))
|
||||||
local name = vim.fn.fnamemodify(path, ':t')
|
local name = vim.fn.fnamemodify(path, ":t")
|
||||||
M.create_and_store_entry(parent_url, name, action.entry_type)
|
M.create_and_store_entry(parent_url, name, action.entry_type)
|
||||||
-- selene: allow(empty_if)
|
elseif action.type == "change" then
|
||||||
elseif action.type == 'change' then
|
|
||||||
-- Cache doesn't need to update
|
-- Cache doesn't need to update
|
||||||
else
|
else
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
local cache = require('oil.cache')
|
local cache = require("oil.cache")
|
||||||
local columns = require('oil.columns')
|
local columns = require("oil.columns")
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local oil = require('oil')
|
local oil = require("oil")
|
||||||
local parser = require('oil.mutator.parser')
|
local parser = require("oil.mutator.parser")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local view = require('oil.view')
|
local view = require("oil.view")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
|
@ -16,10 +16,10 @@ local function get_linux_session_type()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
xdg_session_type = xdg_session_type:lower()
|
xdg_session_type = xdg_session_type:lower()
|
||||||
if xdg_session_type:find('x11') then
|
if xdg_session_type:find("x11") then
|
||||||
return 'x11'
|
return "x11"
|
||||||
elseif xdg_session_type:find('wayland') then
|
elseif xdg_session_type:find("wayland") then
|
||||||
return 'wayland'
|
return "wayland"
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
@ -29,9 +29,9 @@ end
|
||||||
local function is_linux_desktop_gnome()
|
local function is_linux_desktop_gnome()
|
||||||
local cur_desktop = vim.env.XDG_CURRENT_DESKTOP
|
local cur_desktop = vim.env.XDG_CURRENT_DESKTOP
|
||||||
local session_desktop = vim.env.XDG_SESSION_DESKTOP
|
local session_desktop = vim.env.XDG_SESSION_DESKTOP
|
||||||
local idx = session_desktop and session_desktop:lower():find('gnome')
|
local idx = session_desktop and session_desktop:lower():find("gnome")
|
||||||
or cur_desktop and cur_desktop:lower():find('gnome')
|
or cur_desktop and cur_desktop:lower():find("gnome")
|
||||||
return idx ~= nil or cur_desktop == 'X-Cinnamon' or cur_desktop == 'XFCE'
|
return idx ~= nil or cur_desktop == "X-Cinnamon" or cur_desktop == "XFCE"
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param winid integer
|
---@param winid integer
|
||||||
|
|
@ -55,7 +55,7 @@ end
|
||||||
---@param entry oil.InternalEntry
|
---@param entry oil.InternalEntry
|
||||||
local function remove_entry_from_parent_buffer(parent_url, entry)
|
local function remove_entry_from_parent_buffer(parent_url, entry)
|
||||||
local bufnr = vim.fn.bufadd(parent_url)
|
local bufnr = vim.fn.bufadd(parent_url)
|
||||||
assert(vim.api.nvim_buf_is_loaded(bufnr), 'Expected parent buffer to be loaded during paste')
|
assert(vim.api.nvim_buf_is_loaded(bufnr), "Expected parent buffer to be loaded during paste")
|
||||||
local adapter = assert(util.get_adapter(bufnr))
|
local adapter = assert(util.get_adapter(bufnr))
|
||||||
local column_defs = columns.get_supported_columns(adapter)
|
local column_defs = columns.get_supported_columns(adapter)
|
||||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
|
|
@ -77,7 +77,7 @@ end
|
||||||
---@param delete_original? boolean
|
---@param delete_original? boolean
|
||||||
local function paste_paths(paths, delete_original)
|
local function paste_paths(paths, delete_original)
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
local scheme = 'oil://'
|
local scheme = "oil://"
|
||||||
local adapter = assert(config.get_adapter_by_scheme(scheme))
|
local adapter = assert(config.get_adapter_by_scheme(scheme))
|
||||||
local column_defs = columns.get_supported_columns(scheme)
|
local column_defs = columns.get_supported_columns(scheme)
|
||||||
local winid = vim.api.nvim_get_current_win()
|
local winid = vim.api.nvim_get_current_win()
|
||||||
|
|
@ -88,7 +88,7 @@ local function paste_paths(paths, delete_original)
|
||||||
-- Handle as many paths synchronously as possible
|
-- Handle as many paths synchronously as possible
|
||||||
for _, path in ipairs(paths) do
|
for _, path in ipairs(paths) do
|
||||||
-- Trim the trailing slash off directories
|
-- Trim the trailing slash off directories
|
||||||
if vim.endswith(path, '/') then
|
if vim.endswith(path, "/") then
|
||||||
path = path:sub(1, -2)
|
path = path:sub(1, -2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -114,7 +114,7 @@ local function paste_paths(paths, delete_original)
|
||||||
local cursor = vim.api.nvim_win_get_cursor(winid)
|
local cursor = vim.api.nvim_win_get_cursor(winid)
|
||||||
local complete_loading = util.cb_collect(#vim.tbl_keys(parent_urls), function(err)
|
local complete_loading = util.cb_collect(#vim.tbl_keys(parent_urls), function(err)
|
||||||
if err then
|
if err then
|
||||||
vim.notify(string.format('Error loading parent directory: %s', err), vim.log.levels.ERROR)
|
vim.notify(string.format("Error loading parent directory: %s", err), vim.log.levels.ERROR)
|
||||||
else
|
else
|
||||||
-- Something in this process moves the cursor to the top of the window, so have to restore it
|
-- Something in this process moves the cursor to the top of the window, so have to restore it
|
||||||
vim.api.nvim_win_set_cursor(winid, cursor)
|
vim.api.nvim_win_set_cursor(winid, cursor)
|
||||||
|
|
@ -149,8 +149,8 @@ end
|
||||||
---@return integer end
|
---@return integer end
|
||||||
local function range_from_selection()
|
local function range_from_selection()
|
||||||
-- [bufnum, lnum, col, off]; both row and column 1-indexed
|
-- [bufnum, lnum, col, off]; both row and column 1-indexed
|
||||||
local start = vim.fn.getpos('v')
|
local start = vim.fn.getpos("v")
|
||||||
local end_ = vim.fn.getpos('.')
|
local end_ = vim.fn.getpos(".")
|
||||||
local start_row = start[2]
|
local start_row = start[2]
|
||||||
local end_row = end_[2]
|
local end_row = end_[2]
|
||||||
|
|
||||||
|
|
@ -164,16 +164,16 @@ end
|
||||||
M.copy_to_system_clipboard = function()
|
M.copy_to_system_clipboard = function()
|
||||||
local dir = oil.get_current_dir()
|
local dir = oil.get_current_dir()
|
||||||
if not dir then
|
if not dir then
|
||||||
vim.notify('System clipboard only works for local files', vim.log.levels.ERROR)
|
vim.notify("System clipboard only works for local files", vim.log.levels.ERROR)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local entries = {}
|
local entries = {}
|
||||||
local mode = vim.api.nvim_get_mode().mode
|
local mode = vim.api.nvim_get_mode().mode
|
||||||
if mode == 'v' or mode == 'V' then
|
if mode == "v" or mode == "V" then
|
||||||
if fs.is_mac then
|
if fs.is_mac then
|
||||||
vim.notify(
|
vim.notify(
|
||||||
'Copying multiple paths to clipboard is not supported on mac',
|
"Copying multiple paths to clipboard is not supported on mac",
|
||||||
vim.log.levels.ERROR
|
vim.log.levels.ERROR
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
@ -184,7 +184,7 @@ M.copy_to_system_clipboard = function()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- leave visual mode
|
-- leave visual mode
|
||||||
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Esc>', true, false, true), 'n', true)
|
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<Esc>", true, false, true), "n", true)
|
||||||
else
|
else
|
||||||
table.insert(entries, oil.get_cursor_entry())
|
table.insert(entries, oil.get_cursor_entry())
|
||||||
end
|
end
|
||||||
|
|
@ -193,7 +193,7 @@ M.copy_to_system_clipboard = function()
|
||||||
entries = vim.tbl_values(entries)
|
entries = vim.tbl_values(entries)
|
||||||
|
|
||||||
if #entries == 0 then
|
if #entries == 0 then
|
||||||
vim.notify('Could not find local file under cursor', vim.log.levels.WARN)
|
vim.notify("Could not find local file under cursor", vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local paths = {}
|
local paths = {}
|
||||||
|
|
@ -204,38 +204,38 @@ M.copy_to_system_clipboard = function()
|
||||||
local stdin
|
local stdin
|
||||||
if fs.is_mac then
|
if fs.is_mac then
|
||||||
cmd = {
|
cmd = {
|
||||||
'osascript',
|
"osascript",
|
||||||
'-e',
|
"-e",
|
||||||
'on run args',
|
"on run args",
|
||||||
'-e',
|
"-e",
|
||||||
'set the clipboard to POSIX file (first item of args)',
|
"set the clipboard to POSIX file (first item of args)",
|
||||||
'-e',
|
"-e",
|
||||||
'end run',
|
"end run",
|
||||||
paths[1],
|
paths[1],
|
||||||
}
|
}
|
||||||
elseif fs.is_linux then
|
elseif fs.is_linux then
|
||||||
local xdg_session_type = get_linux_session_type()
|
local xdg_session_type = get_linux_session_type()
|
||||||
if xdg_session_type == 'x11' then
|
if xdg_session_type == "x11" then
|
||||||
vim.list_extend(cmd, { 'xclip', '-i', '-selection', 'clipboard' })
|
vim.list_extend(cmd, { "xclip", "-i", "-selection", "clipboard" })
|
||||||
elseif xdg_session_type == 'wayland' then
|
elseif xdg_session_type == "wayland" then
|
||||||
table.insert(cmd, 'wl-copy')
|
table.insert(cmd, "wl-copy")
|
||||||
else
|
else
|
||||||
vim.notify('System clipboard not supported, check $XDG_SESSION_TYPE', vim.log.levels.ERROR)
|
vim.notify("System clipboard not supported, check $XDG_SESSION_TYPE", vim.log.levels.ERROR)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local urls = {}
|
local urls = {}
|
||||||
for _, path in ipairs(paths) do
|
for _, path in ipairs(paths) do
|
||||||
table.insert(urls, 'file://' .. path)
|
table.insert(urls, "file://" .. path)
|
||||||
end
|
end
|
||||||
if is_linux_desktop_gnome() then
|
if is_linux_desktop_gnome() then
|
||||||
stdin = string.format('copy\n%s\0', table.concat(urls, '\n'))
|
stdin = string.format("copy\n%s\0", table.concat(urls, "\n"))
|
||||||
vim.list_extend(cmd, { '-t', 'x-special/gnome-copied-files' })
|
vim.list_extend(cmd, { "-t", "x-special/gnome-copied-files" })
|
||||||
else
|
else
|
||||||
stdin = table.concat(urls, '\n') .. '\n'
|
stdin = table.concat(urls, "\n") .. "\n"
|
||||||
vim.list_extend(cmd, { '-t', 'text/uri-list' })
|
vim.list_extend(cmd, { "-t", "text/uri-list" })
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
vim.notify('System clipboard not supported on Windows', vim.log.levels.ERROR)
|
vim.notify("System clipboard not supported on Windows", vim.log.levels.ERROR)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -243,11 +243,11 @@ M.copy_to_system_clipboard = function()
|
||||||
vim.notify(string.format("Could not find executable '%s'", cmd[1]), vim.log.levels.ERROR)
|
vim.notify(string.format("Could not find executable '%s'", cmd[1]), vim.log.levels.ERROR)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local stderr = ''
|
local stderr = ""
|
||||||
local jid = vim.fn.jobstart(cmd, {
|
local jid = vim.fn.jobstart(cmd, {
|
||||||
stderr_buffered = true,
|
stderr_buffered = true,
|
||||||
on_stderr = function(_, data)
|
on_stderr = function(_, data)
|
||||||
stderr = table.concat(data, '\n')
|
stderr = table.concat(data, "\n")
|
||||||
end,
|
end,
|
||||||
on_exit = function(j, exit_code)
|
on_exit = function(j, exit_code)
|
||||||
if exit_code ~= 0 then
|
if exit_code ~= 0 then
|
||||||
|
|
@ -259,15 +259,15 @@ M.copy_to_system_clipboard = function()
|
||||||
if #paths == 1 then
|
if #paths == 1 then
|
||||||
vim.notify(string.format("Copied '%s' to system clipboard", paths[1]))
|
vim.notify(string.format("Copied '%s' to system clipboard", paths[1]))
|
||||||
else
|
else
|
||||||
vim.notify(string.format('Copied %d files to system clipboard', #paths))
|
vim.notify(string.format("Copied %d files to system clipboard", #paths))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
assert(jid > 0, 'Failed to start job')
|
assert(jid > 0, "Failed to start job")
|
||||||
if stdin then
|
if stdin then
|
||||||
vim.api.nvim_chan_send(jid, stdin)
|
vim.api.nvim_chan_send(jid, stdin)
|
||||||
vim.fn.chanclose(jid, 'stdin')
|
vim.fn.chanclose(jid, "stdin")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -276,7 +276,7 @@ end
|
||||||
local function handle_paste_output_mac(lines)
|
local function handle_paste_output_mac(lines)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
for _, line in ipairs(lines) do
|
for _, line in ipairs(lines) do
|
||||||
if not line:match('^%s*$') then
|
if not line:match("^%s*$") then
|
||||||
table.insert(ret, line)
|
table.insert(ret, line)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -288,7 +288,7 @@ end
|
||||||
local function handle_paste_output_linux(lines)
|
local function handle_paste_output_linux(lines)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
for _, line in ipairs(lines) do
|
for _, line in ipairs(lines) do
|
||||||
local path = line:match('^file://(.+)$')
|
local path = line:match("^file://(.+)$")
|
||||||
if path then
|
if path then
|
||||||
table.insert(ret, util.url_unescape(path))
|
table.insert(ret, util.url_unescape(path))
|
||||||
end
|
end
|
||||||
|
|
@ -306,37 +306,37 @@ M.paste_from_system_clipboard = function(delete_original)
|
||||||
local handle_paste_output
|
local handle_paste_output
|
||||||
if fs.is_mac then
|
if fs.is_mac then
|
||||||
cmd = {
|
cmd = {
|
||||||
'osascript',
|
"osascript",
|
||||||
'-e',
|
"-e",
|
||||||
'on run',
|
"on run",
|
||||||
'-e',
|
"-e",
|
||||||
'POSIX path of (the clipboard as «class furl»)',
|
"POSIX path of (the clipboard as «class furl»)",
|
||||||
'-e',
|
"-e",
|
||||||
'end run',
|
"end run",
|
||||||
}
|
}
|
||||||
handle_paste_output = handle_paste_output_mac
|
handle_paste_output = handle_paste_output_mac
|
||||||
elseif fs.is_linux then
|
elseif fs.is_linux then
|
||||||
local xdg_session_type = get_linux_session_type()
|
local xdg_session_type = get_linux_session_type()
|
||||||
if xdg_session_type == 'x11' then
|
if xdg_session_type == "x11" then
|
||||||
vim.list_extend(cmd, { 'xclip', '-o', '-selection', 'clipboard' })
|
vim.list_extend(cmd, { "xclip", "-o", "-selection", "clipboard" })
|
||||||
elseif xdg_session_type == 'wayland' then
|
elseif xdg_session_type == "wayland" then
|
||||||
table.insert(cmd, 'wl-paste')
|
table.insert(cmd, "wl-paste")
|
||||||
else
|
else
|
||||||
vim.notify('System clipboard not supported, check $XDG_SESSION_TYPE', vim.log.levels.ERROR)
|
vim.notify("System clipboard not supported, check $XDG_SESSION_TYPE", vim.log.levels.ERROR)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if is_linux_desktop_gnome() then
|
if is_linux_desktop_gnome() then
|
||||||
vim.list_extend(cmd, { '-t', 'x-special/gnome-copied-files' })
|
vim.list_extend(cmd, { "-t", "x-special/gnome-copied-files" })
|
||||||
else
|
else
|
||||||
vim.list_extend(cmd, { '-t', 'text/uri-list' })
|
vim.list_extend(cmd, { "-t", "text/uri-list" })
|
||||||
end
|
end
|
||||||
handle_paste_output = handle_paste_output_linux
|
handle_paste_output = handle_paste_output_linux
|
||||||
else
|
else
|
||||||
vim.notify('System clipboard not supported on Windows', vim.log.levels.ERROR)
|
vim.notify("System clipboard not supported on Windows", vim.log.levels.ERROR)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local paths
|
local paths
|
||||||
local stderr = ''
|
local stderr = ""
|
||||||
if vim.fn.executable(cmd[1]) == 0 then
|
if vim.fn.executable(cmd[1]) == 0 then
|
||||||
vim.notify(string.format("Could not find executable '%s'", cmd[1]), vim.log.levels.ERROR)
|
vim.notify(string.format("Could not find executable '%s'", cmd[1]), vim.log.levels.ERROR)
|
||||||
return
|
return
|
||||||
|
|
@ -345,26 +345,26 @@ M.paste_from_system_clipboard = function(delete_original)
|
||||||
stdout_buffered = true,
|
stdout_buffered = true,
|
||||||
stderr_buffered = true,
|
stderr_buffered = true,
|
||||||
on_stdout = function(j, data)
|
on_stdout = function(j, data)
|
||||||
local lines = vim.split(table.concat(data, '\n'), '\r?\n')
|
local lines = vim.split(table.concat(data, "\n"), "\r?\n")
|
||||||
paths = handle_paste_output(lines)
|
paths = handle_paste_output(lines)
|
||||||
end,
|
end,
|
||||||
on_stderr = function(_, data)
|
on_stderr = function(_, data)
|
||||||
stderr = table.concat(data, '\n')
|
stderr = table.concat(data, "\n")
|
||||||
end,
|
end,
|
||||||
on_exit = function(j, exit_code)
|
on_exit = function(j, exit_code)
|
||||||
if exit_code ~= 0 or not paths then
|
if exit_code ~= 0 or not paths then
|
||||||
vim.notify(
|
vim.notify(
|
||||||
string.format('Error pasting from system clipboard: %s', stderr),
|
string.format("Error pasting from system clipboard: %s", stderr),
|
||||||
vim.log.levels.ERROR
|
vim.log.levels.ERROR
|
||||||
)
|
)
|
||||||
elseif #paths == 0 then
|
elseif #paths == 0 then
|
||||||
vim.notify('No valid files found in system clipboard', vim.log.levels.WARN)
|
vim.notify("No valid files found in system clipboard", vim.log.levels.WARN)
|
||||||
else
|
else
|
||||||
paste_paths(paths, delete_original)
|
paste_paths(paths, delete_original)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
assert(jid > 0, 'Failed to start job')
|
assert(jid > 0, "Failed to start job")
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local FIELD_NAME = constants.FIELD_NAME
|
local FIELD_NAME = constants.FIELD_NAME
|
||||||
|
|
@ -38,7 +38,7 @@ end
|
||||||
---@return oil.ColumnSpec[]
|
---@return oil.ColumnSpec[]
|
||||||
M.get_supported_columns = function(adapter_or_scheme)
|
M.get_supported_columns = function(adapter_or_scheme)
|
||||||
local adapter
|
local adapter
|
||||||
if type(adapter_or_scheme) == 'string' then
|
if type(adapter_or_scheme) == "string" then
|
||||||
adapter = config.get_adapter_by_scheme(adapter_or_scheme)
|
adapter = config.get_adapter_by_scheme(adapter_or_scheme)
|
||||||
else
|
else
|
||||||
adapter = adapter_or_scheme
|
adapter = adapter_or_scheme
|
||||||
|
|
@ -53,7 +53,7 @@ M.get_supported_columns = function(adapter_or_scheme)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
local EMPTY = { '-', 'OilEmpty' }
|
local EMPTY = { "-", "OilEmpty" }
|
||||||
|
|
||||||
M.EMPTY = EMPTY
|
M.EMPTY = EMPTY
|
||||||
|
|
||||||
|
|
@ -71,17 +71,17 @@ M.render_col = function(adapter, col_def, entry, bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
local chunk = column.render(entry, conf, bufnr)
|
local chunk = column.render(entry, conf, bufnr)
|
||||||
if type(chunk) == 'table' then
|
if type(chunk) == "table" then
|
||||||
if chunk[1]:match('^%s*$') then
|
if chunk[1]:match("^%s*$") then
|
||||||
return EMPTY
|
return EMPTY
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if not chunk or chunk:match('^%s*$') then
|
if not chunk or chunk:match("^%s*$") then
|
||||||
return EMPTY
|
return EMPTY
|
||||||
end
|
end
|
||||||
if conf and conf.highlight then
|
if conf and conf.highlight then
|
||||||
local highlight = conf.highlight
|
local highlight = conf.highlight
|
||||||
if type(highlight) == 'function' then
|
if type(highlight) == "function" then
|
||||||
highlight = conf.highlight(chunk)
|
highlight = conf.highlight(chunk)
|
||||||
end
|
end
|
||||||
return { chunk, highlight }
|
return { chunk, highlight }
|
||||||
|
|
@ -98,13 +98,13 @@ end
|
||||||
M.parse_col = function(adapter, line, col_def)
|
M.parse_col = function(adapter, line, col_def)
|
||||||
local name, conf = util.split_config(col_def)
|
local name, conf = util.split_config(col_def)
|
||||||
-- If rendering failed, there will just be a "-"
|
-- If rendering failed, there will just be a "-"
|
||||||
local empty_col, rem = line:match('^%s*(-%s+)(.*)$')
|
local empty_col, rem = line:match("^%s*(-%s+)(.*)$")
|
||||||
if empty_col then
|
if empty_col then
|
||||||
return nil, rem
|
return nil, rem
|
||||||
end
|
end
|
||||||
local column = M.get_column(adapter, name)
|
local column = M.get_column(adapter, name)
|
||||||
if column then
|
if column then
|
||||||
return column.parse(line:gsub('^%s+', ''), conf)
|
return column.parse(line:gsub("^%s+", ""), conf)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -128,12 +128,12 @@ end
|
||||||
M.render_change_action = function(adapter, action)
|
M.render_change_action = function(adapter, action)
|
||||||
local column = M.get_column(adapter, action.column)
|
local column = M.get_column(adapter, action.column)
|
||||||
if not column then
|
if not column then
|
||||||
error(string.format('Received change action for nonexistant column %s', action.column))
|
error(string.format("Received change action for nonexistant column %s", action.column))
|
||||||
end
|
end
|
||||||
if column.render_action then
|
if column.render_action then
|
||||||
return column.render_action(action)
|
return column.render_action(action)
|
||||||
else
|
else
|
||||||
return string.format('CHANGE %s %s = %s', action.url, action.column, action.value)
|
return string.format("CHANGE %s %s = %s", action.url, action.column, action.value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -144,7 +144,7 @@ M.perform_change_action = function(adapter, action, callback)
|
||||||
local column = M.get_column(adapter, action.column)
|
local column = M.get_column(adapter, action.column)
|
||||||
if not column then
|
if not column then
|
||||||
return callback(
|
return callback(
|
||||||
string.format('Received change action for nonexistant column %s', action.column)
|
string.format("Received change action for nonexistant column %s", action.column)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
column.perform_action(action, callback)
|
column.perform_action(action, callback)
|
||||||
|
|
@ -152,12 +152,12 @@ end
|
||||||
|
|
||||||
local icon_provider = util.get_icon_provider()
|
local icon_provider = util.get_icon_provider()
|
||||||
if icon_provider then
|
if icon_provider then
|
||||||
M.register('icon', {
|
M.register("icon", {
|
||||||
render = function(entry, conf, bufnr)
|
render = function(entry, conf, bufnr)
|
||||||
local field_type = entry[FIELD_TYPE]
|
local field_type = entry[FIELD_TYPE]
|
||||||
local name = entry[FIELD_NAME]
|
local name = entry[FIELD_NAME]
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
if field_type == 'link' and meta then
|
if field_type == "link" and meta then
|
||||||
if meta.link then
|
if meta.link then
|
||||||
name = meta.link
|
name = meta.link
|
||||||
end
|
end
|
||||||
|
|
@ -170,11 +170,11 @@ if icon_provider then
|
||||||
end
|
end
|
||||||
|
|
||||||
local ft = nil
|
local ft = nil
|
||||||
if conf and conf.use_slow_filetype_detection and field_type == 'file' then
|
if conf and conf.use_slow_filetype_detection and field_type == "file" then
|
||||||
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
||||||
local _, path = util.parse_url(bufname)
|
local _, path = util.parse_url(bufname)
|
||||||
if path then
|
if path then
|
||||||
local lines = vim.fn.readfile(path .. name, '', 16)
|
local lines = vim.fn.readfile(path .. name, "", 16)
|
||||||
if lines and #lines > 0 then
|
if lines and #lines > 0 then
|
||||||
ft = vim.filetype.match({ filename = name, contents = lines })
|
ft = vim.filetype.match({ filename = name, contents = lines })
|
||||||
end
|
end
|
||||||
|
|
@ -183,10 +183,10 @@ if icon_provider then
|
||||||
|
|
||||||
local icon, hl = icon_provider(field_type, name, conf, ft)
|
local icon, hl = icon_provider(field_type, name, conf, ft)
|
||||||
if not conf or conf.add_padding ~= false then
|
if not conf or conf.add_padding ~= false then
|
||||||
icon = icon .. ' '
|
icon = icon .. " "
|
||||||
end
|
end
|
||||||
if conf and conf.highlight then
|
if conf and conf.highlight then
|
||||||
if type(conf.highlight) == 'function' then
|
if type(conf.highlight) == "function" then
|
||||||
hl = conf.highlight(icon)
|
hl = conf.highlight(icon)
|
||||||
else
|
else
|
||||||
hl = conf.highlight
|
hl = conf.highlight
|
||||||
|
|
@ -196,29 +196,29 @@ if icon_provider then
|
||||||
end,
|
end,
|
||||||
|
|
||||||
parse = function(line, conf)
|
parse = function(line, conf)
|
||||||
return line:match('^(%S+)%s+(.*)$')
|
return line:match("^(%S+)%s+(.*)$")
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
local default_type_icons = {
|
local default_type_icons = {
|
||||||
directory = 'dir',
|
directory = "dir",
|
||||||
socket = 'sock',
|
socket = "sock",
|
||||||
}
|
}
|
||||||
---@param entry oil.InternalEntry
|
---@param entry oil.InternalEntry
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function is_entry_directory(entry)
|
local function is_entry_directory(entry)
|
||||||
local type = entry[FIELD_TYPE]
|
local type = entry[FIELD_TYPE]
|
||||||
if type == 'directory' then
|
if type == "directory" then
|
||||||
return true
|
return true
|
||||||
elseif type == 'link' then
|
elseif type == "link" then
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
return (meta and meta.link_stat and meta.link_stat.type == 'directory') == true
|
return (meta and meta.link_stat and meta.link_stat.type == "directory") == true
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
M.register('type', {
|
M.register("type", {
|
||||||
render = function(entry, conf)
|
render = function(entry, conf)
|
||||||
local entry_type = entry[FIELD_TYPE]
|
local entry_type = entry[FIELD_TYPE]
|
||||||
if conf and conf.icons then
|
if conf and conf.icons then
|
||||||
|
|
@ -229,7 +229,7 @@ M.register('type', {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
parse = function(line, conf)
|
parse = function(line, conf)
|
||||||
return line:match('^(%S+)%s+(.*)$')
|
return line:match("^(%S+)%s+(.*)$")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_sort_value = function(entry)
|
get_sort_value = function(entry)
|
||||||
|
|
@ -242,22 +242,22 @@ M.register('type', {
|
||||||
})
|
})
|
||||||
|
|
||||||
local function adjust_number(int)
|
local function adjust_number(int)
|
||||||
return string.format('%03d%s', #int, int)
|
return string.format("%03d%s", #int, int)
|
||||||
end
|
end
|
||||||
|
|
||||||
M.register('name', {
|
M.register("name", {
|
||||||
render = function(entry, conf)
|
render = function(entry, conf)
|
||||||
error('Do not use the name column. It is for sorting only')
|
error("Do not use the name column. It is for sorting only")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
parse = function(line, conf)
|
parse = function(line, conf)
|
||||||
error('Do not use the name column. It is for sorting only')
|
error("Do not use the name column. It is for sorting only")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
create_sort_value_factory = function(num_entries)
|
create_sort_value_factory = function(num_entries)
|
||||||
if
|
if
|
||||||
config.view_options.natural_order == false
|
config.view_options.natural_order == false
|
||||||
or (config.view_options.natural_order == 'fast' and num_entries > 5000)
|
or (config.view_options.natural_order == "fast" and num_entries > 5000)
|
||||||
then
|
then
|
||||||
if config.view_options.case_insensitive then
|
if config.view_options.case_insensitive then
|
||||||
return function(entry)
|
return function(entry)
|
||||||
|
|
@ -272,7 +272,7 @@ M.register('name', {
|
||||||
local memo = {}
|
local memo = {}
|
||||||
return function(entry)
|
return function(entry)
|
||||||
if memo[entry] == nil then
|
if memo[entry] == nil then
|
||||||
local name = entry[FIELD_NAME]:gsub('0*(%d+)', adjust_number)
|
local name = entry[FIELD_NAME]:gsub("0*(%d+)", adjust_number)
|
||||||
if config.view_options.case_insensitive then
|
if config.view_options.case_insensitive then
|
||||||
name = name:lower()
|
name = name:lower()
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ local default_config = {
|
||||||
-- Id is automatically added at the beginning, and name at the end
|
-- Id is automatically added at the beginning, and name at the end
|
||||||
-- See :help oil-columns
|
-- See :help oil-columns
|
||||||
columns = {
|
columns = {
|
||||||
'icon',
|
"icon",
|
||||||
-- "permissions",
|
-- "permissions",
|
||||||
-- "size",
|
-- "size",
|
||||||
-- "mtime",
|
-- "mtime",
|
||||||
|
|
@ -13,18 +13,18 @@ local default_config = {
|
||||||
-- Buffer-local options to use for oil buffers
|
-- Buffer-local options to use for oil buffers
|
||||||
buf_options = {
|
buf_options = {
|
||||||
buflisted = false,
|
buflisted = false,
|
||||||
bufhidden = 'hide',
|
bufhidden = "hide",
|
||||||
},
|
},
|
||||||
-- Window-local options to use for oil buffers
|
-- Window-local options to use for oil buffers
|
||||||
win_options = {
|
win_options = {
|
||||||
wrap = false,
|
wrap = false,
|
||||||
signcolumn = 'no',
|
signcolumn = "no",
|
||||||
cursorcolumn = false,
|
cursorcolumn = false,
|
||||||
foldcolumn = '0',
|
foldcolumn = "0",
|
||||||
spell = false,
|
spell = false,
|
||||||
list = false,
|
list = false,
|
||||||
conceallevel = 3,
|
conceallevel = 3,
|
||||||
concealcursor = 'nvic',
|
concealcursor = "nvic",
|
||||||
},
|
},
|
||||||
-- Send deleted files to the trash instead of permanently deleting them (:help oil-trash)
|
-- Send deleted files to the trash instead of permanently deleting them (:help oil-trash)
|
||||||
delete_to_trash = false,
|
delete_to_trash = false,
|
||||||
|
|
@ -48,7 +48,7 @@ local default_config = {
|
||||||
},
|
},
|
||||||
-- Constrain the cursor to the editable parts of the oil buffer
|
-- Constrain the cursor to the editable parts of the oil buffer
|
||||||
-- Set to `false` to disable, or "name" to keep it on the file names
|
-- Set to `false` to disable, or "name" to keep it on the file names
|
||||||
constrain_cursor = 'editable',
|
constrain_cursor = "editable",
|
||||||
-- Set to true to watch the filesystem for changes and reload oil
|
-- Set to true to watch the filesystem for changes and reload oil
|
||||||
watch_for_changes = false,
|
watch_for_changes = false,
|
||||||
-- Keymaps in oil buffer. Can be any value that `vim.keymap.set` accepts OR a table of keymap
|
-- Keymaps in oil buffer. Can be any value that `vim.keymap.set` accepts OR a table of keymap
|
||||||
|
|
@ -58,22 +58,22 @@ local default_config = {
|
||||||
-- Set to `false` to remove a keymap
|
-- Set to `false` to remove a keymap
|
||||||
-- See :help oil-actions for a list of all available actions
|
-- See :help oil-actions for a list of all available actions
|
||||||
keymaps = {
|
keymaps = {
|
||||||
['g?'] = { 'actions.show_help', mode = 'n' },
|
["g?"] = { "actions.show_help", mode = "n" },
|
||||||
['<CR>'] = 'actions.select',
|
["<CR>"] = "actions.select",
|
||||||
['<C-s>'] = { 'actions.select', opts = { vertical = true } },
|
["<C-s>"] = { "actions.select", opts = { vertical = true } },
|
||||||
['<C-h>'] = { 'actions.select', opts = { horizontal = true } },
|
["<C-h>"] = { "actions.select", opts = { horizontal = true } },
|
||||||
['<C-t>'] = { 'actions.select', opts = { tab = true } },
|
["<C-t>"] = { "actions.select", opts = { tab = true } },
|
||||||
['<C-p>'] = 'actions.preview',
|
["<C-p>"] = "actions.preview",
|
||||||
['<C-c>'] = { 'actions.close', mode = 'n' },
|
["<C-c>"] = { "actions.close", mode = "n" },
|
||||||
['<C-l>'] = 'actions.refresh',
|
["<C-l>"] = "actions.refresh",
|
||||||
['-'] = { 'actions.parent', mode = 'n' },
|
["-"] = { "actions.parent", mode = "n" },
|
||||||
['_'] = { 'actions.open_cwd', mode = 'n' },
|
["_"] = { "actions.open_cwd", mode = "n" },
|
||||||
['`'] = { 'actions.cd', mode = 'n' },
|
["`"] = { "actions.cd", mode = "n" },
|
||||||
['g~'] = { 'actions.cd', opts = { scope = 'tab' }, mode = 'n' },
|
["g~"] = { "actions.cd", opts = { scope = "tab" }, mode = "n" },
|
||||||
['gs'] = { 'actions.change_sort', mode = 'n' },
|
["gs"] = { "actions.change_sort", mode = "n" },
|
||||||
['gx'] = 'actions.open_external',
|
["gx"] = "actions.open_external",
|
||||||
['g.'] = { 'actions.toggle_hidden', mode = 'n' },
|
["g."] = { "actions.toggle_hidden", mode = "n" },
|
||||||
['g\\'] = { 'actions.toggle_trash', mode = 'n' },
|
["g\\"] = { "actions.toggle_trash", mode = "n" },
|
||||||
},
|
},
|
||||||
-- Set to false to disable all of the above keymaps
|
-- Set to false to disable all of the above keymaps
|
||||||
use_default_keymaps = true,
|
use_default_keymaps = true,
|
||||||
|
|
@ -82,7 +82,7 @@ local default_config = {
|
||||||
show_hidden = false,
|
show_hidden = false,
|
||||||
-- This function defines what is considered a "hidden" file
|
-- This function defines what is considered a "hidden" file
|
||||||
is_hidden_file = function(name, bufnr)
|
is_hidden_file = function(name, bufnr)
|
||||||
local m = name:match('^%.')
|
local m = name:match("^%.")
|
||||||
return m ~= nil
|
return m ~= nil
|
||||||
end,
|
end,
|
||||||
-- This function defines what will never be shown, even when `show_hidden` is set
|
-- This function defines what will never be shown, even when `show_hidden` is set
|
||||||
|
|
@ -91,14 +91,14 @@ local default_config = {
|
||||||
end,
|
end,
|
||||||
-- Sort file names with numbers in a more intuitive order for humans.
|
-- 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.
|
-- Can be "fast", true, or false. "fast" will turn it off for large directories.
|
||||||
natural_order = 'fast',
|
natural_order = "fast",
|
||||||
-- Sort file and directory names case insensitive
|
-- Sort file and directory names case insensitive
|
||||||
case_insensitive = false,
|
case_insensitive = false,
|
||||||
sort = {
|
sort = {
|
||||||
-- sort order can be "asc" or "desc"
|
-- sort order can be "asc" or "desc"
|
||||||
-- see :help oil-columns to see which columns are sortable
|
-- see :help oil-columns to see which columns are sortable
|
||||||
{ 'type', 'asc' },
|
{ "type", "asc" },
|
||||||
{ 'name', 'asc' },
|
{ "name", "asc" },
|
||||||
},
|
},
|
||||||
-- Customize the highlight group for the file name
|
-- Customize the highlight group for the file name
|
||||||
highlight_filename = function(entry, is_hidden, is_link_target, is_link_orphan)
|
highlight_filename = function(entry, is_hidden, is_link_target, is_link_orphan)
|
||||||
|
|
@ -138,7 +138,7 @@ local default_config = {
|
||||||
-- optionally override the oil buffers window title with custom function: fun(winid: integer): string
|
-- optionally override the oil buffers window title with custom function: fun(winid: integer): string
|
||||||
get_win_title = nil,
|
get_win_title = nil,
|
||||||
-- preview_split: Split direction: "auto", "left", "right", "above", "below".
|
-- preview_split: Split direction: "auto", "left", "right", "above", "below".
|
||||||
preview_split = 'auto',
|
preview_split = "auto",
|
||||||
-- This is the config that will be passed to nvim_open_win.
|
-- This is the config that will be passed to nvim_open_win.
|
||||||
-- Change values here to customize the layout
|
-- Change values here to customize the layout
|
||||||
override = function(conf)
|
override = function(conf)
|
||||||
|
|
@ -150,7 +150,7 @@ local default_config = {
|
||||||
-- Whether the preview window is automatically updated when the cursor is moved
|
-- Whether the preview window is automatically updated when the cursor is moved
|
||||||
update_on_cursor_moved = true,
|
update_on_cursor_moved = true,
|
||||||
-- How to open the preview window "load"|"scratch"|"fast_scratch"
|
-- How to open the preview window "load"|"scratch"|"fast_scratch"
|
||||||
preview_method = 'fast_scratch',
|
preview_method = "fast_scratch",
|
||||||
-- A function that returns true to disable preview on a file e.g. to avoid lag
|
-- A function that returns true to disable preview on a file e.g. to avoid lag
|
||||||
disable_preview = function(filename)
|
disable_preview = function(filename)
|
||||||
return false
|
return false
|
||||||
|
|
@ -190,7 +190,7 @@ local default_config = {
|
||||||
min_height = { 5, 0.1 },
|
min_height = { 5, 0.1 },
|
||||||
height = nil,
|
height = nil,
|
||||||
border = nil,
|
border = nil,
|
||||||
minimized_border = 'none',
|
minimized_border = "none",
|
||||||
win_options = {
|
win_options = {
|
||||||
winblend = 0,
|
winblend = 0,
|
||||||
},
|
},
|
||||||
|
|
@ -211,12 +211,12 @@ local default_config = {
|
||||||
|
|
||||||
-- not "oil-s3://" on older neovim versions, since it doesn't open buffers correctly with a number
|
-- not "oil-s3://" on older neovim versions, since it doesn't open buffers correctly with a number
|
||||||
-- in the name
|
-- in the name
|
||||||
local oil_s3_string = vim.fn.has('nvim-0.12') == 1 and 'oil-s3://' or 'oil-sss://'
|
local oil_s3_string = vim.fn.has("nvim-0.12") == 1 and "oil-s3://" or "oil-sss://"
|
||||||
default_config.adapters = {
|
default_config.adapters = {
|
||||||
['oil://'] = 'files',
|
["oil://"] = "files",
|
||||||
['oil-ssh://'] = 'ssh',
|
["oil-ssh://"] = "ssh",
|
||||||
[oil_s3_string] = 's3',
|
[oil_s3_string] = "s3",
|
||||||
['oil-trash://'] = 'trash',
|
["oil-trash://"] = "trash",
|
||||||
}
|
}
|
||||||
default_config.adapter_aliases = {}
|
default_config.adapter_aliases = {}
|
||||||
-- We want the function in the default config for documentation generation, but if we nil it out
|
-- We want the function in the default config for documentation generation, but if we nil it out
|
||||||
|
|
@ -408,7 +408,7 @@ local M = {}
|
||||||
M.setup = function(opts)
|
M.setup = function(opts)
|
||||||
opts = opts or vim.g.oil or {}
|
opts = opts or vim.g.oil or {}
|
||||||
|
|
||||||
local new_conf = vim.tbl_deep_extend('keep', opts, default_config)
|
local new_conf = vim.tbl_deep_extend("keep", opts, default_config)
|
||||||
if not new_conf.use_default_keymaps then
|
if not new_conf.use_default_keymaps then
|
||||||
new_conf.keymaps = opts.keymaps or {}
|
new_conf.keymaps = opts.keymaps or {}
|
||||||
elseif opts.keymaps then
|
elseif opts.keymaps then
|
||||||
|
|
@ -429,19 +429,19 @@ M.setup = function(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Backwards compatibility for old versions that don't support winborder
|
-- Backwards compatibility for old versions that don't support winborder
|
||||||
if vim.fn.has('nvim-0.11') == 0 then
|
if vim.fn.has("nvim-0.11") == 0 then
|
||||||
new_conf = vim.tbl_deep_extend('keep', new_conf, {
|
new_conf = vim.tbl_deep_extend("keep", new_conf, {
|
||||||
float = { border = 'rounded' },
|
float = { border = "rounded" },
|
||||||
confirmation = { border = 'rounded' },
|
confirmation = { border = "rounded" },
|
||||||
progress = { border = 'rounded' },
|
progress = { border = "rounded" },
|
||||||
ssh = { border = 'rounded' },
|
ssh = { border = "rounded" },
|
||||||
keymaps_help = { border = 'rounded' },
|
keymaps_help = { border = "rounded" },
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Backwards compatibility. We renamed the 'preview' window config to be called 'confirmation'.
|
-- Backwards compatibility. We renamed the 'preview' window config to be called 'confirmation'.
|
||||||
if opts.preview and not opts.confirmation then
|
if opts.preview and not opts.confirmation then
|
||||||
new_conf.confirmation = vim.tbl_deep_extend('keep', opts.preview, default_config.confirmation)
|
new_conf.confirmation = vim.tbl_deep_extend("keep", opts.preview, default_config.confirmation)
|
||||||
end
|
end
|
||||||
-- Backwards compatibility. We renamed the 'preview' config to 'preview_win'
|
-- Backwards compatibility. We renamed the 'preview' config to 'preview_win'
|
||||||
if opts.preview and opts.preview.update_on_cursor_moved ~= nil then
|
if opts.preview and opts.preview.update_on_cursor_moved ~= nil then
|
||||||
|
|
@ -452,7 +452,7 @@ M.setup = function(opts)
|
||||||
new_conf.lsp_file_methods.autosave_changes = new_conf.lsp_rename_autosave
|
new_conf.lsp_file_methods.autosave_changes = new_conf.lsp_rename_autosave
|
||||||
new_conf.lsp_rename_autosave = nil
|
new_conf.lsp_rename_autosave = nil
|
||||||
vim.notify_once(
|
vim.notify_once(
|
||||||
'oil config value lsp_rename_autosave has moved to lsp_file_methods.autosave_changes.\nCompatibility will be removed on 2024-09-01.',
|
"oil config value lsp_rename_autosave has moved to lsp_file_methods.autosave_changes.\nCompatibility will be removed on 2024-09-01.",
|
||||||
vim.log.levels.WARN
|
vim.log.levels.WARN
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
@ -479,10 +479,10 @@ M.get_adapter_by_scheme = function(scheme)
|
||||||
if not scheme then
|
if not scheme then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
if not vim.endswith(scheme, '://') then
|
if not vim.endswith(scheme, "://") then
|
||||||
local pieces = vim.split(scheme, '://', { plain = true })
|
local pieces = vim.split(scheme, "://", { plain = true })
|
||||||
if #pieces <= 2 then
|
if #pieces <= 2 then
|
||||||
scheme = pieces[1] .. '://'
|
scheme = pieces[1] .. "://"
|
||||||
else
|
else
|
||||||
error(string.format("Malformed url: '%s'", scheme))
|
error(string.format("Malformed url: '%s'", scheme))
|
||||||
end
|
end
|
||||||
|
|
@ -494,7 +494,7 @@ M.get_adapter_by_scheme = function(scheme)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
local ok
|
local ok
|
||||||
ok, adapter = pcall(require, string.format('oil.adapters.%s', name))
|
ok, adapter = pcall(require, string.format("oil.adapters.%s", name))
|
||||||
if ok then
|
if ok then
|
||||||
adapter.name = name
|
adapter.name = name
|
||||||
M._adapter_by_scheme[scheme] = adapter
|
M._adapter_by_scheme[scheme] = adapter
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
local log = require('oil.log')
|
local log = require("oil.log")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local uv = vim.uv or vim.loop
|
local uv = vim.uv or vim.loop
|
||||||
|
|
||||||
---@type boolean
|
---@type boolean
|
||||||
M.is_windows = uv.os_uname().version:match('Windows')
|
M.is_windows = uv.os_uname().version:match("Windows")
|
||||||
|
|
||||||
M.is_mac = uv.os_uname().sysname == 'Darwin'
|
M.is_mac = uv.os_uname().sysname == "Darwin"
|
||||||
|
|
||||||
M.is_linux = not M.is_windows and not M.is_mac
|
M.is_linux = not M.is_windows and not M.is_mac
|
||||||
|
|
||||||
---@type string
|
---@type string
|
||||||
M.sep = M.is_windows and '\\' or '/'
|
M.sep = M.is_windows and "\\" or "/"
|
||||||
|
|
||||||
---@param ... string
|
---@param ... string
|
||||||
M.join = function(...)
|
M.join = function(...)
|
||||||
|
|
@ -23,15 +23,15 @@ end
|
||||||
---@return boolean
|
---@return boolean
|
||||||
M.is_absolute = function(dir)
|
M.is_absolute = function(dir)
|
||||||
if M.is_windows then
|
if M.is_windows then
|
||||||
return dir:match('^%a:\\')
|
return dir:match("^%a:\\")
|
||||||
else
|
else
|
||||||
return vim.startswith(dir, '/')
|
return vim.startswith(dir, "/")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
M.abspath = function(path)
|
M.abspath = function(path)
|
||||||
if not M.is_absolute(path) then
|
if not M.is_absolute(path) then
|
||||||
path = vim.fn.fnamemodify(path, ':p')
|
path = vim.fn.fnamemodify(path, ":p")
|
||||||
end
|
end
|
||||||
return path
|
return path
|
||||||
end
|
end
|
||||||
|
|
@ -40,11 +40,11 @@ end
|
||||||
---@param mode? integer File mode in decimal (default 420 = 0644)
|
---@param mode? integer File mode in decimal (default 420 = 0644)
|
||||||
---@param cb fun(err: nil|string)
|
---@param cb fun(err: nil|string)
|
||||||
M.touch = function(path, mode, cb)
|
M.touch = function(path, mode, cb)
|
||||||
if type(mode) == 'function' then
|
if type(mode) == "function" then
|
||||||
cb = mode
|
cb = mode
|
||||||
mode = 420
|
mode = 420
|
||||||
end
|
end
|
||||||
uv.fs_open(path, 'a', mode or 420, function(err, fd)
|
uv.fs_open(path, "a", mode or 420, function(err, fd)
|
||||||
if err then
|
if err then
|
||||||
cb(err)
|
cb(err)
|
||||||
else
|
else
|
||||||
|
|
@ -59,12 +59,12 @@ end
|
||||||
---@param candidate string
|
---@param candidate string
|
||||||
---@return boolean
|
---@return boolean
|
||||||
M.is_subpath = function(root, candidate)
|
M.is_subpath = function(root, candidate)
|
||||||
if candidate == '' then
|
if candidate == "" then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
root = vim.fs.normalize(M.abspath(root))
|
root = vim.fs.normalize(M.abspath(root))
|
||||||
-- Trim trailing "/" from the root
|
-- Trim trailing "/" from the root
|
||||||
if root:find('/', -1) then
|
if root:find("/", -1) then
|
||||||
root = root:sub(1, -2)
|
root = root:sub(1, -2)
|
||||||
end
|
end
|
||||||
candidate = vim.fs.normalize(M.abspath(candidate))
|
candidate = vim.fs.normalize(M.abspath(candidate))
|
||||||
|
|
@ -80,8 +80,8 @@ M.is_subpath = function(root, candidate)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local candidate_starts_with_sep = candidate:find('/', root:len() + 1, true) == root:len() + 1
|
local candidate_starts_with_sep = candidate:find("/", root:len() + 1, true) == root:len() + 1
|
||||||
local root_ends_with_sep = root:find('/', root:len(), true) == root:len()
|
local root_ends_with_sep = root:find("/", root:len(), true) == root:len()
|
||||||
|
|
||||||
return candidate_starts_with_sep or root_ends_with_sep
|
return candidate_starts_with_sep or root_ends_with_sep
|
||||||
end
|
end
|
||||||
|
|
@ -90,15 +90,15 @@ end
|
||||||
---@return string
|
---@return string
|
||||||
M.posix_to_os_path = function(path)
|
M.posix_to_os_path = function(path)
|
||||||
if M.is_windows then
|
if M.is_windows then
|
||||||
if vim.startswith(path, '/') then
|
if vim.startswith(path, "/") then
|
||||||
local drive = path:match('^/(%a+)')
|
local drive = path:match("^/(%a+)")
|
||||||
if not drive then
|
if not drive then
|
||||||
return path
|
return path
|
||||||
end
|
end
|
||||||
local rem = path:sub(drive:len() + 2)
|
local rem = path:sub(drive:len() + 2)
|
||||||
return string.format('%s:%s', drive, rem:gsub('/', '\\'))
|
return string.format("%s:%s", drive, rem:gsub("/", "\\"))
|
||||||
else
|
else
|
||||||
local newpath = path:gsub('/', '\\')
|
local newpath = path:gsub("/", "\\")
|
||||||
return newpath
|
return newpath
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
@ -111,10 +111,10 @@ end
|
||||||
M.os_to_posix_path = function(path)
|
M.os_to_posix_path = function(path)
|
||||||
if M.is_windows then
|
if M.is_windows then
|
||||||
if M.is_absolute(path) then
|
if M.is_absolute(path) then
|
||||||
local drive, rem = path:match('^([^:]+):\\(.*)$')
|
local drive, rem = path:match("^([^:]+):\\(.*)$")
|
||||||
return string.format('/%s/%s', drive:upper(), rem:gsub('\\', '/'))
|
return string.format("/%s/%s", drive:upper(), rem:gsub("\\", "/"))
|
||||||
else
|
else
|
||||||
local newpath = path:gsub('\\', '/')
|
local newpath = path:gsub("\\", "/")
|
||||||
return newpath
|
return newpath
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
@ -135,16 +135,16 @@ M.shorten_path = function(path, relative_to)
|
||||||
if M.is_subpath(relative_to, path) then
|
if M.is_subpath(relative_to, path) then
|
||||||
local idx = relative_to:len() + 1
|
local idx = relative_to:len() + 1
|
||||||
-- Trim the dividing slash if it's not included in relative_to
|
-- Trim the dividing slash if it's not included in relative_to
|
||||||
if not vim.endswith(relative_to, '/') and not vim.endswith(relative_to, '\\') then
|
if not vim.endswith(relative_to, "/") and not vim.endswith(relative_to, "\\") then
|
||||||
idx = idx + 1
|
idx = idx + 1
|
||||||
end
|
end
|
||||||
relpath = path:sub(idx)
|
relpath = path:sub(idx)
|
||||||
if relpath == '' then
|
if relpath == "" then
|
||||||
relpath = '.'
|
relpath = "."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if M.is_subpath(home_dir, path) then
|
if M.is_subpath(home_dir, path) then
|
||||||
local homepath = '~' .. path:sub(home_dir:len() + 1)
|
local homepath = "~" .. path:sub(home_dir:len() + 1)
|
||||||
if not relpath or homepath:len() < relpath:len() then
|
if not relpath or homepath:len() < relpath:len() then
|
||||||
return homepath
|
return homepath
|
||||||
end
|
end
|
||||||
|
|
@ -156,13 +156,13 @@ end
|
||||||
---@param mode? integer
|
---@param mode? integer
|
||||||
M.mkdirp = function(dir, mode)
|
M.mkdirp = function(dir, mode)
|
||||||
mode = mode or 493
|
mode = mode or 493
|
||||||
local mod = ''
|
local mod = ""
|
||||||
local path = dir
|
local path = dir
|
||||||
while vim.fn.isdirectory(path) == 0 do
|
while vim.fn.isdirectory(path) == 0 do
|
||||||
mod = mod .. ':h'
|
mod = mod .. ":h"
|
||||||
path = vim.fn.fnamemodify(dir, mod)
|
path = vim.fn.fnamemodify(dir, mod)
|
||||||
end
|
end
|
||||||
while mod ~= '' do
|
while mod ~= "" do
|
||||||
mod = mod:sub(3)
|
mod = mod:sub(3)
|
||||||
path = vim.fn.fnamemodify(dir, mod)
|
path = vim.fn.fnamemodify(dir, mod)
|
||||||
uv.fs_mkdir(path, mode)
|
uv.fs_mkdir(path, mode)
|
||||||
|
|
@ -209,7 +209,7 @@ end
|
||||||
---@param path string
|
---@param path string
|
||||||
---@param cb fun(err: nil|string)
|
---@param cb fun(err: nil|string)
|
||||||
M.recursive_delete = function(entry_type, path, cb)
|
M.recursive_delete = function(entry_type, path, cb)
|
||||||
if entry_type ~= 'directory' then
|
if entry_type ~= "directory" then
|
||||||
return uv.fs_unlink(path, cb)
|
return uv.fs_unlink(path, cb)
|
||||||
end
|
end
|
||||||
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
|
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
|
||||||
|
|
@ -271,13 +271,13 @@ local move_undofile = vim.schedule_wrap(function(src_path, dest_path, copy)
|
||||||
if copy then
|
if copy then
|
||||||
uv.fs_copyfile(src_path, dest_path, function(err)
|
uv.fs_copyfile(src_path, dest_path, function(err)
|
||||||
if err then
|
if err then
|
||||||
log.warn('Error copying undofile %s: %s', undofile, err)
|
log.warn("Error copying undofile %s: %s", undofile, err)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
uv.fs_rename(undofile, dest_undofile, function(err)
|
uv.fs_rename(undofile, dest_undofile, function(err)
|
||||||
if err then
|
if err then
|
||||||
log.warn('Error moving undofile %s: %s', undofile, err)
|
log.warn("Error moving undofile %s: %s", undofile, err)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
@ -290,7 +290,7 @@ end)
|
||||||
---@param dest_path string
|
---@param dest_path string
|
||||||
---@param cb fun(err: nil|string)
|
---@param cb fun(err: nil|string)
|
||||||
M.recursive_copy = function(entry_type, src_path, dest_path, cb)
|
M.recursive_copy = function(entry_type, src_path, dest_path, cb)
|
||||||
if entry_type == 'link' then
|
if entry_type == "link" then
|
||||||
uv.fs_readlink(src_path, function(link_err, link)
|
uv.fs_readlink(src_path, function(link_err, link)
|
||||||
if link_err then
|
if link_err then
|
||||||
return cb(link_err)
|
return cb(link_err)
|
||||||
|
|
@ -300,7 +300,7 @@ M.recursive_copy = function(entry_type, src_path, dest_path, cb)
|
||||||
end)
|
end)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if entry_type ~= 'directory' then
|
if entry_type ~= "directory" then
|
||||||
uv.fs_copyfile(src_path, dest_path, { excl = true }, cb)
|
uv.fs_copyfile(src_path, dest_path, { excl = true }, cb)
|
||||||
move_undofile(src_path, dest_path, true)
|
move_undofile(src_path, dest_path, true)
|
||||||
return
|
return
|
||||||
|
|
@ -374,7 +374,7 @@ M.recursive_move = function(entry_type, src_path, dest_path, cb)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
if entry_type ~= 'directory' then
|
if entry_type ~= "directory" then
|
||||||
move_undofile(src_path, dest_path, false)
|
move_undofile(src_path, dest_path, false)
|
||||||
end
|
end
|
||||||
cb()
|
cb()
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
-- integration with git operations
|
-- integration with git operations
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
---@param path string
|
---@param path string
|
||||||
---@return string|nil
|
---@return string|nil
|
||||||
M.get_root = function(path)
|
M.get_root = function(path)
|
||||||
local git_dir = vim.fs.find('.git', { upward = true, path = path })[1]
|
local git_dir = vim.fs.find(".git", { upward = true, path = path })[1]
|
||||||
if git_dir then
|
if git_dir then
|
||||||
return vim.fs.dirname(git_dir)
|
return vim.fs.dirname(git_dir)
|
||||||
else
|
else
|
||||||
|
|
@ -22,16 +22,16 @@ M.add = function(path, cb)
|
||||||
return cb()
|
return cb()
|
||||||
end
|
end
|
||||||
|
|
||||||
local stderr = ''
|
local stderr = ""
|
||||||
local jid = vim.fn.jobstart({ 'git', 'add', path }, {
|
local jid = vim.fn.jobstart({ "git", "add", path }, {
|
||||||
cwd = root,
|
cwd = root,
|
||||||
stderr_buffered = true,
|
stderr_buffered = true,
|
||||||
on_stderr = function(_, data)
|
on_stderr = function(_, data)
|
||||||
stderr = table.concat(data, '\n')
|
stderr = table.concat(data, "\n")
|
||||||
end,
|
end,
|
||||||
on_exit = function(_, code)
|
on_exit = function(_, code)
|
||||||
if code ~= 0 then
|
if code ~= 0 then
|
||||||
cb('Error in git add: ' .. stderr)
|
cb("Error in git add: " .. stderr)
|
||||||
else
|
else
|
||||||
cb()
|
cb()
|
||||||
end
|
end
|
||||||
|
|
@ -50,12 +50,12 @@ M.rm = function(path, cb)
|
||||||
return cb()
|
return cb()
|
||||||
end
|
end
|
||||||
|
|
||||||
local stderr = ''
|
local stderr = ""
|
||||||
local jid = vim.fn.jobstart({ 'git', 'rm', '-r', path }, {
|
local jid = vim.fn.jobstart({ "git", "rm", "-r", path }, {
|
||||||
cwd = root,
|
cwd = root,
|
||||||
stderr_buffered = true,
|
stderr_buffered = true,
|
||||||
on_stderr = function(_, data)
|
on_stderr = function(_, data)
|
||||||
stderr = table.concat(data, '\n')
|
stderr = table.concat(data, "\n")
|
||||||
end,
|
end,
|
||||||
on_exit = function(_, code)
|
on_exit = function(_, code)
|
||||||
if code ~= 0 then
|
if code ~= 0 then
|
||||||
|
|
@ -63,7 +63,7 @@ M.rm = function(path, cb)
|
||||||
if stderr:match("^fatal: pathspec '.*' did not match any files$") then
|
if stderr:match("^fatal: pathspec '.*' did not match any files$") then
|
||||||
cb()
|
cb()
|
||||||
else
|
else
|
||||||
cb('Error in git rm: ' .. stderr)
|
cb("Error in git rm: " .. stderr)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
cb()
|
cb()
|
||||||
|
|
@ -86,23 +86,23 @@ M.mv = function(entry_type, src_path, dest_path, cb)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local stderr = ''
|
local stderr = ""
|
||||||
local jid = vim.fn.jobstart({ 'git', 'mv', src_path, dest_path }, {
|
local jid = vim.fn.jobstart({ "git", "mv", src_path, dest_path }, {
|
||||||
cwd = src_git,
|
cwd = src_git,
|
||||||
stderr_buffered = true,
|
stderr_buffered = true,
|
||||||
on_stderr = function(_, data)
|
on_stderr = function(_, data)
|
||||||
stderr = table.concat(data, '\n')
|
stderr = table.concat(data, "\n")
|
||||||
end,
|
end,
|
||||||
on_exit = function(_, code)
|
on_exit = function(_, code)
|
||||||
if code ~= 0 then
|
if code ~= 0 then
|
||||||
stderr = vim.trim(stderr)
|
stderr = vim.trim(stderr)
|
||||||
if
|
if
|
||||||
stderr:match('^fatal: not under version control')
|
stderr:match("^fatal: not under version control")
|
||||||
or stderr:match('^fatal: source directory is empty')
|
or stderr:match("^fatal: source directory is empty")
|
||||||
then
|
then
|
||||||
fs.recursive_move(entry_type, src_path, dest_path, cb)
|
fs.recursive_move(entry_type, src_path, dest_path, cb)
|
||||||
else
|
else
|
||||||
cb('Error in git mv: ' .. stderr)
|
cb("Error in git mv: " .. stderr)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
cb()
|
cb()
|
||||||
|
|
|
||||||
586
lua/oil/init.lua
586
lua/oil/init.lua
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,7 @@
|
||||||
local actions = require('oil.actions')
|
local actions = require("oil.actions")
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local layout = require('oil.layout')
|
local layout = require("oil.layout")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
---@param rhs string|table|fun()
|
---@param rhs string|table|fun()
|
||||||
|
|
@ -9,14 +9,14 @@ local M = {}
|
||||||
---@return table opts
|
---@return table opts
|
||||||
---@return string|nil mode
|
---@return string|nil mode
|
||||||
local function resolve(rhs)
|
local function resolve(rhs)
|
||||||
if type(rhs) == 'string' and vim.startswith(rhs, 'actions.') then
|
if type(rhs) == "string" and vim.startswith(rhs, "actions.") then
|
||||||
local action_name = vim.split(rhs, '.', { plain = true })[2]
|
local action_name = vim.split(rhs, ".", { plain = true })[2]
|
||||||
local action = actions[action_name]
|
local action = actions[action_name]
|
||||||
if not action then
|
if not action then
|
||||||
vim.notify('[oil.nvim] Unknown action name: ' .. action_name, vim.log.levels.ERROR)
|
vim.notify("[oil.nvim] Unknown action name: " .. action_name, vim.log.levels.ERROR)
|
||||||
end
|
end
|
||||||
return resolve(action)
|
return resolve(action)
|
||||||
elseif type(rhs) == 'table' then
|
elseif type(rhs) == "table" then
|
||||||
local opts = vim.deepcopy(rhs)
|
local opts = vim.deepcopy(rhs)
|
||||||
-- We support passing in a `callback` key, or using the 1 index as the rhs of the keymap
|
-- We support passing in a `callback` key, or using the 1 index as the rhs of the keymap
|
||||||
local callback, parent_opts = resolve(opts.callback or opts[1])
|
local callback, parent_opts = resolve(opts.callback or opts[1])
|
||||||
|
|
@ -25,17 +25,17 @@ local function resolve(rhs)
|
||||||
if parent_opts.desc and not opts.desc then
|
if parent_opts.desc and not opts.desc then
|
||||||
if opts.opts then
|
if opts.opts then
|
||||||
opts.desc =
|
opts.desc =
|
||||||
string.format('%s %s', parent_opts.desc, vim.inspect(opts.opts):gsub('%s+', ' '))
|
string.format("%s %s", parent_opts.desc, vim.inspect(opts.opts):gsub("%s+", " "))
|
||||||
else
|
else
|
||||||
opts.desc = parent_opts.desc
|
opts.desc = parent_opts.desc
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local mode = opts.mode
|
local mode = opts.mode
|
||||||
if type(rhs.callback) == 'string' then
|
if type(rhs.callback) == "string" then
|
||||||
local action_opts, action_mode
|
local action_opts, action_mode
|
||||||
callback, action_opts, action_mode = resolve(rhs.callback)
|
callback, action_opts, action_mode = resolve(rhs.callback)
|
||||||
opts = vim.tbl_extend('keep', opts, action_opts)
|
opts = vim.tbl_extend("keep", opts, action_opts)
|
||||||
mode = mode or action_mode
|
mode = mode or action_mode
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -46,7 +46,7 @@ local function resolve(rhs)
|
||||||
opts.deprecated = nil
|
opts.deprecated = nil
|
||||||
opts.parameters = nil
|
opts.parameters = nil
|
||||||
|
|
||||||
if opts.opts and type(callback) == 'function' then
|
if opts.opts and type(callback) == "function" then
|
||||||
local callback_args = opts.opts
|
local callback_args = opts.opts
|
||||||
opts.opts = nil
|
opts.opts = nil
|
||||||
local orig_callback = callback
|
local orig_callback = callback
|
||||||
|
|
@ -68,7 +68,7 @@ M.set_keymaps = function(keymaps, bufnr)
|
||||||
for k, v in pairs(keymaps) do
|
for k, v in pairs(keymaps) do
|
||||||
local rhs, opts, mode = resolve(v)
|
local rhs, opts, mode = resolve(v)
|
||||||
if rhs then
|
if rhs then
|
||||||
vim.keymap.set(mode or '', k, rhs, vim.tbl_extend('keep', { buffer = bufnr }, opts))
|
vim.keymap.set(mode or "", k, rhs, vim.tbl_extend("keep", { buffer = bufnr }, opts))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -95,9 +95,9 @@ M.show_help = function(keymaps)
|
||||||
local all_lhs = lhs_to_all_lhs[k]
|
local all_lhs = lhs_to_all_lhs[k]
|
||||||
if all_lhs then
|
if all_lhs then
|
||||||
local _, opts = resolve(rhs)
|
local _, opts = resolve(rhs)
|
||||||
local keystr = table.concat(all_lhs, '/')
|
local keystr = table.concat(all_lhs, "/")
|
||||||
max_lhs = math.max(max_lhs, vim.api.nvim_strwidth(keystr))
|
max_lhs = math.max(max_lhs, vim.api.nvim_strwidth(keystr))
|
||||||
table.insert(keymap_entries, { str = keystr, all_lhs = all_lhs, desc = opts.desc or '' })
|
table.insert(keymap_entries, { str = keystr, all_lhs = all_lhs, desc = opts.desc or "" })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.sort(keymap_entries, function(a, b)
|
table.sort(keymap_entries, function(a, b)
|
||||||
|
|
@ -108,20 +108,20 @@ M.show_help = function(keymaps)
|
||||||
local highlights = {}
|
local highlights = {}
|
||||||
local max_line = 1
|
local max_line = 1
|
||||||
for _, entry in ipairs(keymap_entries) do
|
for _, entry in ipairs(keymap_entries) do
|
||||||
local line = string.format(' %s %s', util.pad_align(entry.str, max_lhs, 'left'), entry.desc)
|
local line = string.format(" %s %s", util.pad_align(entry.str, max_lhs, "left"), entry.desc)
|
||||||
max_line = math.max(max_line, vim.api.nvim_strwidth(line))
|
max_line = math.max(max_line, vim.api.nvim_strwidth(line))
|
||||||
table.insert(lines, line)
|
table.insert(lines, line)
|
||||||
local start = 1
|
local start = 1
|
||||||
for _, key in ipairs(entry.all_lhs) do
|
for _, key in ipairs(entry.all_lhs) do
|
||||||
local keywidth = vim.api.nvim_strwidth(key)
|
local keywidth = vim.api.nvim_strwidth(key)
|
||||||
table.insert(highlights, { 'Special', #lines, start, start + keywidth })
|
table.insert(highlights, { "Special", #lines, start, start + keywidth })
|
||||||
start = start + keywidth + 1
|
start = start + keywidth + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local bufnr = vim.api.nvim_create_buf(false, true)
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
|
||||||
local ns = vim.api.nvim_create_namespace('Oil')
|
local ns = vim.api.nvim_create_namespace("Oil")
|
||||||
for _, hl in ipairs(highlights) do
|
for _, hl in ipairs(highlights) do
|
||||||
local hl_group, lnum, start_col, end_col = unpack(hl)
|
local hl_group, lnum, start_col, end_col = unpack(hl)
|
||||||
vim.api.nvim_buf_set_extmark(bufnr, ns, lnum - 1, start_col, {
|
vim.api.nvim_buf_set_extmark(bufnr, ns, lnum - 1, start_col, {
|
||||||
|
|
@ -129,21 +129,21 @@ M.show_help = function(keymaps)
|
||||||
hl_group = hl_group,
|
hl_group = hl_group,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
vim.keymap.set('n', 'q', '<cmd>close<CR>', { buffer = bufnr })
|
vim.keymap.set("n", "q", "<cmd>close<CR>", { buffer = bufnr })
|
||||||
vim.keymap.set('n', '<c-c>', '<cmd>close<CR>', { buffer = bufnr })
|
vim.keymap.set("n", "<c-c>", "<cmd>close<CR>", { buffer = bufnr })
|
||||||
vim.bo[bufnr].modifiable = false
|
vim.bo[bufnr].modifiable = false
|
||||||
vim.bo[bufnr].bufhidden = 'wipe'
|
vim.bo[bufnr].bufhidden = "wipe"
|
||||||
|
|
||||||
local editor_width = vim.o.columns
|
local editor_width = vim.o.columns
|
||||||
local editor_height = layout.get_editor_height()
|
local editor_height = layout.get_editor_height()
|
||||||
local winid = vim.api.nvim_open_win(bufnr, true, {
|
local winid = vim.api.nvim_open_win(bufnr, true, {
|
||||||
relative = 'editor',
|
relative = "editor",
|
||||||
row = math.max(0, (editor_height - #lines) / 2),
|
row = math.max(0, (editor_height - #lines) / 2),
|
||||||
col = math.max(0, (editor_width - max_line - 1) / 2),
|
col = math.max(0, (editor_width - max_line - 1) / 2),
|
||||||
width = math.min(editor_width, max_line + 1),
|
width = math.min(editor_width, max_line + 1),
|
||||||
height = math.min(editor_height, #lines),
|
height = math.min(editor_height, #lines),
|
||||||
zindex = 150,
|
zindex = 150,
|
||||||
style = 'minimal',
|
style = "minimal",
|
||||||
border = config.keymaps_help.border,
|
border = config.keymaps_help.border,
|
||||||
})
|
})
|
||||||
local function close()
|
local function close()
|
||||||
|
|
@ -151,13 +151,13 @@ M.show_help = function(keymaps)
|
||||||
vim.api.nvim_win_close(winid, true)
|
vim.api.nvim_win_close(winid, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.api.nvim_create_autocmd('BufLeave', {
|
vim.api.nvim_create_autocmd("BufLeave", {
|
||||||
callback = close,
|
callback = close,
|
||||||
once = true,
|
once = true,
|
||||||
nested = true,
|
nested = true,
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
})
|
})
|
||||||
vim.api.nvim_create_autocmd('WinLeave', {
|
vim.api.nvim_create_autocmd("WinLeave", {
|
||||||
callback = close,
|
callback = close,
|
||||||
once = true,
|
once = true,
|
||||||
nested = true,
|
nested = true,
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ local function calc_list(values, max_value, aggregator, limit)
|
||||||
local ret = limit
|
local ret = limit
|
||||||
if not max_value or not values then
|
if not max_value or not values then
|
||||||
return nil
|
return nil
|
||||||
elseif type(values) == 'table' then
|
elseif type(values) == "table" then
|
||||||
for _, v in ipairs(values) do
|
for _, v in ipairs(values) do
|
||||||
ret = aggregator(ret, calc_float(v, max_value))
|
ret = aggregator(ret, calc_float(v, max_value))
|
||||||
end
|
end
|
||||||
|
|
@ -106,12 +106,12 @@ end
|
||||||
|
|
||||||
---@return vim.api.keyset.win_config
|
---@return vim.api.keyset.win_config
|
||||||
M.get_fullscreen_win_opts = function()
|
M.get_fullscreen_win_opts = function()
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
|
|
||||||
local total_width = M.get_editor_width()
|
local total_width = M.get_editor_width()
|
||||||
local total_height = M.get_editor_height()
|
local total_height = M.get_editor_height()
|
||||||
local width = total_width - 2 * config.float.padding
|
local width = total_width - 2 * config.float.padding
|
||||||
if config.float.border ~= 'none' then
|
if config.float.border ~= "none" then
|
||||||
width = width - 2 -- The border consumes 1 col on each side
|
width = width - 2 -- The border consumes 1 col on each side
|
||||||
end
|
end
|
||||||
if config.float.max_width > 0 then
|
if config.float.max_width > 0 then
|
||||||
|
|
@ -127,7 +127,7 @@ M.get_fullscreen_win_opts = function()
|
||||||
local col = math.floor((total_width - width) / 2) - 1 -- adjust for border width
|
local col = math.floor((total_width - width) / 2) - 1 -- adjust for border width
|
||||||
|
|
||||||
local win_opts = {
|
local win_opts = {
|
||||||
relative = 'editor',
|
relative = "editor",
|
||||||
width = width,
|
width = width,
|
||||||
height = height,
|
height = height,
|
||||||
row = row,
|
row = row,
|
||||||
|
|
@ -144,8 +144,8 @@ end
|
||||||
---@return oil.WinLayout root_dim New dimensions of the original window
|
---@return oil.WinLayout root_dim New dimensions of the original window
|
||||||
---@return oil.WinLayout new_dim New dimensions of the new window
|
---@return oil.WinLayout new_dim New dimensions of the new window
|
||||||
M.split_window = function(winid, direction, gap)
|
M.split_window = function(winid, direction, gap)
|
||||||
if direction == 'auto' then
|
if direction == "auto" then
|
||||||
direction = vim.o.splitright and 'right' or 'left'
|
direction = vim.o.splitright and "right" or "left"
|
||||||
end
|
end
|
||||||
|
|
||||||
local float_config = vim.api.nvim_win_get_config(winid)
|
local float_config = vim.api.nvim_win_get_config(winid)
|
||||||
|
|
@ -156,14 +156,14 @@ M.split_window = function(winid, direction, gap)
|
||||||
col = float_config.col,
|
col = float_config.col,
|
||||||
row = float_config.row,
|
row = float_config.row,
|
||||||
}
|
}
|
||||||
if vim.fn.has('nvim-0.10') == 0 then
|
if vim.fn.has("nvim-0.10") == 0 then
|
||||||
-- read https://github.com/neovim/neovim/issues/24430 for more infos.
|
-- read https://github.com/neovim/neovim/issues/24430 for more infos.
|
||||||
dim_root.col = float_config.col[vim.val_idx]
|
dim_root.col = float_config.col[vim.val_idx]
|
||||||
dim_root.row = float_config.row[vim.val_idx]
|
dim_root.row = float_config.row[vim.val_idx]
|
||||||
end
|
end
|
||||||
local dim_new = vim.deepcopy(dim_root)
|
local dim_new = vim.deepcopy(dim_root)
|
||||||
|
|
||||||
if direction == 'left' or direction == 'right' then
|
if direction == "left" or direction == "right" then
|
||||||
dim_new.width = math.floor(float_config.width / 2) - math.ceil(gap / 2)
|
dim_new.width = math.floor(float_config.width / 2) - math.ceil(gap / 2)
|
||||||
dim_root.width = dim_new.width
|
dim_root.width = dim_new.width
|
||||||
else
|
else
|
||||||
|
|
@ -171,13 +171,13 @@ M.split_window = function(winid, direction, gap)
|
||||||
dim_root.height = dim_new.height
|
dim_root.height = dim_new.height
|
||||||
end
|
end
|
||||||
|
|
||||||
if direction == 'left' then
|
if direction == "left" then
|
||||||
dim_root.col = dim_root.col + dim_root.width + gap
|
dim_root.col = dim_root.col + dim_root.width + gap
|
||||||
elseif direction == 'right' then
|
elseif direction == "right" then
|
||||||
dim_new.col = dim_new.col + dim_new.width + gap
|
dim_new.col = dim_new.col + dim_new.width + gap
|
||||||
elseif direction == 'above' then
|
elseif direction == "above" then
|
||||||
dim_root.row = dim_root.row + dim_root.height + gap
|
dim_root.row = dim_root.row + dim_root.height + gap
|
||||||
elseif direction == 'below' then
|
elseif direction == "below" then
|
||||||
dim_new.row = dim_new.row + dim_new.height + gap
|
dim_new.row = dim_new.row + dim_new.height + gap
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local timers = {}
|
local timers = {}
|
||||||
|
|
@ -12,14 +12,14 @@ M.is_loading = function(bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
local spinners = {
|
local spinners = {
|
||||||
dots = { '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' },
|
dots = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" },
|
||||||
}
|
}
|
||||||
|
|
||||||
---@param name_or_frames string|string[]
|
---@param name_or_frames string|string[]
|
||||||
---@return fun(): string
|
---@return fun(): string
|
||||||
M.get_iter = function(name_or_frames)
|
M.get_iter = function(name_or_frames)
|
||||||
local frames
|
local frames
|
||||||
if type(name_or_frames) == 'string' then
|
if type(name_or_frames) == "string" then
|
||||||
frames = spinners[name_or_frames]
|
frames = spinners[name_or_frames]
|
||||||
if not frames then
|
if not frames then
|
||||||
error(string.format("Unrecognized spinner: '%s'", name_or_frames))
|
error(string.format("Unrecognized spinner: '%s'", name_or_frames))
|
||||||
|
|
@ -35,26 +35,26 @@ M.get_iter = function(name_or_frames)
|
||||||
end
|
end
|
||||||
|
|
||||||
M.get_bar_iter = function(opts)
|
M.get_bar_iter = function(opts)
|
||||||
opts = vim.tbl_deep_extend('keep', opts or {}, {
|
opts = vim.tbl_deep_extend("keep", opts or {}, {
|
||||||
bar_size = 3,
|
bar_size = 3,
|
||||||
width = 20,
|
width = 20,
|
||||||
})
|
})
|
||||||
local i = 0
|
local i = 0
|
||||||
return function()
|
return function()
|
||||||
local chars = { '[' }
|
local chars = { "[" }
|
||||||
for _ = 1, opts.width - 2 do
|
for _ = 1, opts.width - 2 do
|
||||||
table.insert(chars, ' ')
|
table.insert(chars, " ")
|
||||||
end
|
end
|
||||||
table.insert(chars, ']')
|
table.insert(chars, "]")
|
||||||
|
|
||||||
for j = i - opts.bar_size, i do
|
for j = i - opts.bar_size, i do
|
||||||
if j > 1 and j < opts.width then
|
if j > 1 and j < opts.width then
|
||||||
chars[j] = '='
|
chars[j] = "="
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
i = (i + 1) % (opts.width + opts.bar_size)
|
i = (i + 1) % (opts.width + opts.bar_size)
|
||||||
return table.concat(chars, '')
|
return table.concat(chars, "")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -75,7 +75,7 @@ M.set_loading = function(bufnr, is_loading)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local lines =
|
local lines =
|
||||||
{ util.pad_align('Loading', math.floor(width / 2) - 3, 'right'), bar_iter() }
|
{ util.pad_align("Loading", math.floor(width / 2) - 3, "right"), bar_iter() }
|
||||||
util.render_text(bufnr, lines)
|
util.render_text(bufnr, lines)
|
||||||
end)
|
end)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,14 @@ Log.level = vim.log.levels.WARN
|
||||||
|
|
||||||
---@return string
|
---@return string
|
||||||
Log.get_logfile = function()
|
Log.get_logfile = function()
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
|
|
||||||
local ok, stdpath = pcall(vim.fn.stdpath, 'log')
|
local ok, stdpath = pcall(vim.fn.stdpath, "log")
|
||||||
if not ok then
|
if not ok then
|
||||||
stdpath = vim.fn.stdpath('cache')
|
stdpath = vim.fn.stdpath("cache")
|
||||||
end
|
end
|
||||||
assert(type(stdpath) == 'string')
|
assert(type(stdpath) == "string")
|
||||||
return fs.join(stdpath, 'oil.log')
|
return fs.join(stdpath, "oil.log")
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param level integer
|
---@param level integer
|
||||||
|
|
@ -29,19 +29,19 @@ local function format(level, msg, ...)
|
||||||
local args = vim.F.pack_len(...)
|
local args = vim.F.pack_len(...)
|
||||||
for i = 1, args.n do
|
for i = 1, args.n do
|
||||||
local v = args[i]
|
local v = args[i]
|
||||||
if type(v) == 'table' then
|
if type(v) == "table" then
|
||||||
args[i] = vim.inspect(v)
|
args[i] = vim.inspect(v)
|
||||||
elseif v == nil then
|
elseif v == nil then
|
||||||
args[i] = 'nil'
|
args[i] = "nil"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local ok, text = pcall(string.format, msg, vim.F.unpack_len(args))
|
local ok, text = pcall(string.format, msg, vim.F.unpack_len(args))
|
||||||
-- TODO figure out how to get formatted time inside luv callback
|
-- TODO figure out how to get formatted time inside luv callback
|
||||||
-- local timestr = vim.fn.strftime("%Y-%m-%d %H:%M:%S")
|
-- local timestr = vim.fn.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
local timestr = ''
|
local timestr = ""
|
||||||
if ok then
|
if ok then
|
||||||
local str_level = levels_reverse[level]
|
local str_level = levels_reverse[level]
|
||||||
return string.format('%s[%s] %s', timestr, str_level, text)
|
return string.format("%s[%s] %s", timestr, str_level, text)
|
||||||
else
|
else
|
||||||
return string.format(
|
return string.format(
|
||||||
"%s[ERROR] error formatting log line: '%s' args %s",
|
"%s[ERROR] error formatting log line: '%s' args %s",
|
||||||
|
|
@ -67,22 +67,22 @@ local function initialize()
|
||||||
|
|
||||||
local stat = uv.fs_stat(filepath)
|
local stat = uv.fs_stat(filepath)
|
||||||
if stat and stat.size > 10 * 1024 * 1024 then
|
if stat and stat.size > 10 * 1024 * 1024 then
|
||||||
local backup = filepath .. '.1'
|
local backup = filepath .. ".1"
|
||||||
uv.fs_unlink(backup)
|
uv.fs_unlink(backup)
|
||||||
uv.fs_rename(filepath, backup)
|
uv.fs_rename(filepath, backup)
|
||||||
end
|
end
|
||||||
|
|
||||||
local parent = vim.fs.dirname(filepath)
|
local parent = vim.fs.dirname(filepath)
|
||||||
require('oil.fs').mkdirp(parent)
|
require("oil.fs").mkdirp(parent)
|
||||||
|
|
||||||
local logfile, openerr = io.open(filepath, 'a+')
|
local logfile, openerr = io.open(filepath, "a+")
|
||||||
if not logfile then
|
if not logfile then
|
||||||
local err_msg = string.format('Failed to open oil.nvim log file: %s', openerr)
|
local err_msg = string.format("Failed to open oil.nvim log file: %s", openerr)
|
||||||
vim.notify(err_msg, vim.log.levels.ERROR)
|
vim.notify(err_msg, vim.log.levels.ERROR)
|
||||||
else
|
else
|
||||||
write = function(line)
|
write = function(line)
|
||||||
logfile:write(line)
|
logfile:write(line)
|
||||||
logfile:write('\n')
|
logfile:write("\n")
|
||||||
logfile:flush()
|
logfile:flush()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local workspace = require('oil.lsp.workspace')
|
local workspace = require("oil.lsp.workspace")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@ M.will_perform_file_operations = function(actions)
|
||||||
local creates = {}
|
local creates = {}
|
||||||
local deletes = {}
|
local deletes = {}
|
||||||
for _, action in ipairs(actions) do
|
for _, action in ipairs(actions) do
|
||||||
if action.type == 'move' then
|
if action.type == "move" then
|
||||||
local src_scheme, src_path = util.parse_url(action.src_url)
|
local src_scheme, src_path = util.parse_url(action.src_url)
|
||||||
assert(src_path)
|
assert(src_path)
|
||||||
local src_adapter = assert(config.get_adapter_by_scheme(src_scheme))
|
local src_adapter = assert(config.get_adapter_by_scheme(src_scheme))
|
||||||
|
|
@ -20,32 +20,32 @@ M.will_perform_file_operations = function(actions)
|
||||||
local dest_adapter = assert(config.get_adapter_by_scheme(dest_scheme))
|
local dest_adapter = assert(config.get_adapter_by_scheme(dest_scheme))
|
||||||
src_path = fs.posix_to_os_path(src_path)
|
src_path = fs.posix_to_os_path(src_path)
|
||||||
dest_path = fs.posix_to_os_path(assert(dest_path))
|
dest_path = fs.posix_to_os_path(assert(dest_path))
|
||||||
if src_adapter.name == 'files' and dest_adapter.name == 'files' then
|
if src_adapter.name == "files" and dest_adapter.name == "files" then
|
||||||
moves[src_path] = dest_path
|
moves[src_path] = dest_path
|
||||||
elseif src_adapter.name == 'files' then
|
elseif src_adapter.name == "files" then
|
||||||
table.insert(deletes, src_path)
|
table.insert(deletes, src_path)
|
||||||
elseif dest_adapter.name == 'files' then
|
elseif dest_adapter.name == "files" then
|
||||||
table.insert(creates, src_path)
|
table.insert(creates, src_path)
|
||||||
end
|
end
|
||||||
elseif action.type == 'create' then
|
elseif action.type == "create" then
|
||||||
local scheme, path = util.parse_url(action.url)
|
local scheme, path = util.parse_url(action.url)
|
||||||
path = fs.posix_to_os_path(assert(path))
|
path = fs.posix_to_os_path(assert(path))
|
||||||
local adapter = assert(config.get_adapter_by_scheme(scheme))
|
local adapter = assert(config.get_adapter_by_scheme(scheme))
|
||||||
if adapter.name == 'files' then
|
if adapter.name == "files" then
|
||||||
table.insert(creates, path)
|
table.insert(creates, path)
|
||||||
end
|
end
|
||||||
elseif action.type == 'delete' then
|
elseif action.type == "delete" then
|
||||||
local scheme, path = util.parse_url(action.url)
|
local scheme, path = util.parse_url(action.url)
|
||||||
path = fs.posix_to_os_path(assert(path))
|
path = fs.posix_to_os_path(assert(path))
|
||||||
local adapter = assert(config.get_adapter_by_scheme(scheme))
|
local adapter = assert(config.get_adapter_by_scheme(scheme))
|
||||||
if adapter.name == 'files' then
|
if adapter.name == "files" then
|
||||||
table.insert(deletes, path)
|
table.insert(deletes, path)
|
||||||
end
|
end
|
||||||
elseif action.type == 'copy' then
|
elseif action.type == "copy" then
|
||||||
local scheme, path = util.parse_url(action.dest_url)
|
local scheme, path = util.parse_url(action.dest_url)
|
||||||
path = fs.posix_to_os_path(assert(path))
|
path = fs.posix_to_os_path(assert(path))
|
||||||
local adapter = assert(config.get_adapter_by_scheme(scheme))
|
local adapter = assert(config.get_adapter_by_scheme(scheme))
|
||||||
if adapter.name == 'files' then
|
if adapter.name == "files" then
|
||||||
table.insert(creates, path)
|
table.insert(creates, path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -84,7 +84,7 @@ M.will_perform_file_operations = function(actions)
|
||||||
accum(workspace.will_rename_files(moves, { timeout_ms = timeout_ms }))
|
accum(workspace.will_rename_files(moves, { timeout_ms = timeout_ms }))
|
||||||
if final_err then
|
if final_err then
|
||||||
vim.notify(
|
vim.notify(
|
||||||
string.format('[lsp] file operation error: %s', vim.inspect(final_err)),
|
string.format("[lsp] file operation error: %s", vim.inspect(final_err)),
|
||||||
vim.log.levels.WARN
|
vim.log.levels.WARN
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
@ -102,7 +102,7 @@ M.will_perform_file_operations = function(actions)
|
||||||
local bufnr = vim.uri_to_bufnr(uri)
|
local bufnr = vim.uri_to_bufnr(uri)
|
||||||
local was_open = buf_was_modified[bufnr] ~= nil
|
local was_open = buf_was_modified[bufnr] ~= nil
|
||||||
local was_modified = buf_was_modified[bufnr]
|
local was_modified = buf_was_modified[bufnr]
|
||||||
local should_save = autosave == true or (autosave == 'unmodified' and not was_modified)
|
local should_save = autosave == true or (autosave == "unmodified" and not was_modified)
|
||||||
-- Autosave changed buffers if they were not modified before
|
-- Autosave changed buffers if they were not modified before
|
||||||
if should_save then
|
if should_save then
|
||||||
vim.api.nvim_buf_call(bufnr, function()
|
vim.api.nvim_buf_call(bufnr, function()
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local ms = require('vim.lsp.protocol').Methods
|
local ms = require("vim.lsp.protocol").Methods
|
||||||
if vim.fn.has('nvim-0.10') == 0 then
|
if vim.fn.has("nvim-0.10") == 0 then
|
||||||
ms = {
|
ms = {
|
||||||
workspace_willCreateFiles = 'workspace/willCreateFiles',
|
workspace_willCreateFiles = "workspace/willCreateFiles",
|
||||||
workspace_didCreateFiles = 'workspace/didCreateFiles',
|
workspace_didCreateFiles = "workspace/didCreateFiles",
|
||||||
workspace_willDeleteFiles = 'workspace/willDeleteFiles',
|
workspace_willDeleteFiles = "workspace/willDeleteFiles",
|
||||||
workspace_didDeleteFiles = 'workspace/didDeleteFiles',
|
workspace_didDeleteFiles = "workspace/didDeleteFiles",
|
||||||
workspace_willRenameFiles = 'workspace/willRenameFiles',
|
workspace_willRenameFiles = "workspace/willRenameFiles",
|
||||||
workspace_didRenameFiles = 'workspace/didRenameFiles',
|
workspace_didRenameFiles = "workspace/didRenameFiles",
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -16,7 +16,7 @@ local M = {}
|
||||||
---@param method string
|
---@param method string
|
||||||
---@return vim.lsp.Client[]
|
---@return vim.lsp.Client[]
|
||||||
local function get_clients(method)
|
local function get_clients(method)
|
||||||
if vim.fn.has('nvim-0.10') == 1 then
|
if vim.fn.has("nvim-0.10") == 1 then
|
||||||
return vim.lsp.get_clients({ method = method })
|
return vim.lsp.get_clients({ method = method })
|
||||||
else
|
else
|
||||||
---@diagnostic disable-next-line: deprecated
|
---@diagnostic disable-next-line: deprecated
|
||||||
|
|
@ -32,7 +32,7 @@ end
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function match_glob(glob, path)
|
local function match_glob(glob, path)
|
||||||
-- nvim-0.10 will have vim.glob.to_lpeg, so this will be a LPeg pattern
|
-- nvim-0.10 will have vim.glob.to_lpeg, so this will be a LPeg pattern
|
||||||
if type(glob) ~= 'string' then
|
if type(glob) ~= "string" then
|
||||||
return glob:match(path) ~= nil
|
return glob:match(path) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ local function get_matching_paths(client, filters, paths)
|
||||||
|
|
||||||
local match_fns = {}
|
local match_fns = {}
|
||||||
for _, filter in ipairs(filters) do
|
for _, filter in ipairs(filters) do
|
||||||
if filter.scheme == nil or filter.scheme == 'file' then
|
if filter.scheme == nil or filter.scheme == "file" then
|
||||||
local pattern = filter.pattern
|
local pattern = filter.pattern
|
||||||
local glob = pattern.glob
|
local glob = pattern.glob
|
||||||
local ignore_case = pattern.options and pattern.options.ignoreCase
|
local ignore_case = pattern.options and pattern.options.ignoreCase
|
||||||
|
|
@ -69,32 +69,32 @@ local function get_matching_paths(client, filters, paths)
|
||||||
|
|
||||||
-- Some language servers use forward slashes as path separators on Windows (LuaLS)
|
-- Some language servers use forward slashes as path separators on Windows (LuaLS)
|
||||||
-- We no longer need this after 0.12: https://github.com/neovim/neovim/commit/322a6d305d088420b23071c227af07b7c1beb41a
|
-- We no longer need this after 0.12: https://github.com/neovim/neovim/commit/322a6d305d088420b23071c227af07b7c1beb41a
|
||||||
if vim.fn.has('nvim-0.12') == 0 and fs.is_windows then
|
if vim.fn.has("nvim-0.12") == 0 and fs.is_windows then
|
||||||
glob = glob:gsub('/', '\\')
|
glob = glob:gsub("/", "\\")
|
||||||
end
|
end
|
||||||
|
|
||||||
---@type string|vim.lpeg.Pattern
|
---@type string|vim.lpeg.Pattern
|
||||||
local glob_to_match = glob
|
local glob_to_match = glob
|
||||||
if vim.glob and vim.glob.to_lpeg then
|
if vim.glob and vim.glob.to_lpeg then
|
||||||
glob = glob:gsub('{(.-)}', function(s)
|
glob = glob:gsub("{(.-)}", function(s)
|
||||||
local patterns = vim.split(s, ',')
|
local patterns = vim.split(s, ",")
|
||||||
local filtered = {}
|
local filtered = {}
|
||||||
for _, pat in ipairs(patterns) do
|
for _, pat in ipairs(patterns) do
|
||||||
if pat ~= '' then
|
if pat ~= "" then
|
||||||
table.insert(filtered, pat)
|
table.insert(filtered, pat)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if #filtered == 0 then
|
if #filtered == 0 then
|
||||||
return ''
|
return ""
|
||||||
end
|
end
|
||||||
-- HACK around https://github.com/neovim/neovim/issues/28931
|
-- HACK around https://github.com/neovim/neovim/issues/28931
|
||||||
-- find alternations and sort them by length to try to match the longest first
|
-- find alternations and sort them by length to try to match the longest first
|
||||||
if vim.fn.has('nvim-0.11') == 0 then
|
if vim.fn.has("nvim-0.11") == 0 then
|
||||||
table.sort(filtered, function(a, b)
|
table.sort(filtered, function(a, b)
|
||||||
return a:len() > b:len()
|
return a:len() > b:len()
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
return '{' .. table.concat(filtered, ',') .. '}'
|
return "{" .. table.concat(filtered, ",") .. "}"
|
||||||
end)
|
end)
|
||||||
|
|
||||||
glob_to_match = vim.glob.to_lpeg(glob)
|
glob_to_match = vim.glob.to_lpeg(glob)
|
||||||
|
|
@ -102,7 +102,7 @@ local function get_matching_paths(client, filters, paths)
|
||||||
local matches = pattern.matches
|
local matches = pattern.matches
|
||||||
table.insert(match_fns, function(path)
|
table.insert(match_fns, function(path)
|
||||||
local is_dir = vim.fn.isdirectory(path) == 1
|
local is_dir = vim.fn.isdirectory(path) == 1
|
||||||
if matches and ((matches == 'file' and is_dir) or (matches == 'folder' and not is_dir)) then
|
if matches and ((matches == "file" and is_dir) or (matches == "folder" and not is_dir)) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -163,10 +163,10 @@ local function will_file_operation(method, capability_name, files, options)
|
||||||
for _, client in ipairs(clients) do
|
for _, client in ipairs(clients) do
|
||||||
local filters = vim.tbl_get(
|
local filters = vim.tbl_get(
|
||||||
client.server_capabilities,
|
client.server_capabilities,
|
||||||
'workspace',
|
"workspace",
|
||||||
'fileOperations',
|
"fileOperations",
|
||||||
capability_name,
|
capability_name,
|
||||||
'filters'
|
"filters"
|
||||||
)
|
)
|
||||||
local matching_files = get_matching_paths(client, filters, files)
|
local matching_files = get_matching_paths(client, filters, files)
|
||||||
if matching_files then
|
if matching_files then
|
||||||
|
|
@ -178,7 +178,7 @@ local function will_file_operation(method, capability_name, files, options)
|
||||||
end, matching_files),
|
end, matching_files),
|
||||||
}
|
}
|
||||||
local result, err
|
local result, err
|
||||||
if vim.fn.has('nvim-0.11') == 1 then
|
if vim.fn.has("nvim-0.11") == 1 then
|
||||||
result, err = client:request_sync(method, params, options.timeout_ms or 1000, 0)
|
result, err = client:request_sync(method, params, options.timeout_ms or 1000, 0)
|
||||||
else
|
else
|
||||||
---@diagnostic disable-next-line: param-type-mismatch
|
---@diagnostic disable-next-line: param-type-mismatch
|
||||||
|
|
@ -205,10 +205,10 @@ local function did_file_operation(method, capability_name, files)
|
||||||
for _, client in ipairs(clients) do
|
for _, client in ipairs(clients) do
|
||||||
local filters = vim.tbl_get(
|
local filters = vim.tbl_get(
|
||||||
client.server_capabilities,
|
client.server_capabilities,
|
||||||
'workspace',
|
"workspace",
|
||||||
'fileOperations',
|
"fileOperations",
|
||||||
capability_name,
|
capability_name,
|
||||||
'filters'
|
"filters"
|
||||||
)
|
)
|
||||||
local matching_files = get_matching_paths(client, filters, files)
|
local matching_files = get_matching_paths(client, filters, files)
|
||||||
if matching_files then
|
if matching_files then
|
||||||
|
|
@ -219,7 +219,7 @@ local function did_file_operation(method, capability_name, files)
|
||||||
}
|
}
|
||||||
end, matching_files),
|
end, matching_files),
|
||||||
}
|
}
|
||||||
if vim.fn.has('nvim-0.11') == 1 then
|
if vim.fn.has("nvim-0.11") == 1 then
|
||||||
client:notify(method, params)
|
client:notify(method, params)
|
||||||
else
|
else
|
||||||
---@diagnostic disable-next-line: param-type-mismatch
|
---@diagnostic disable-next-line: param-type-mismatch
|
||||||
|
|
@ -239,13 +239,13 @@ end
|
||||||
---@return nil|{edit: lsp.WorkspaceEdit, offset_encoding: string}[]
|
---@return nil|{edit: lsp.WorkspaceEdit, offset_encoding: string}[]
|
||||||
---@return nil|string|lsp.ResponseError err
|
---@return nil|string|lsp.ResponseError err
|
||||||
function M.will_create_files(files, options)
|
function M.will_create_files(files, options)
|
||||||
return will_file_operation(ms.workspace_willCreateFiles, 'willCreate', files, options)
|
return will_file_operation(ms.workspace_willCreateFiles, "willCreate", files, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Notify the server that files were created from within the client.
|
--- Notify the server that files were created from within the client.
|
||||||
---@param files string[] The files and folders that will be created
|
---@param files string[] The files and folders that will be created
|
||||||
function M.did_create_files(files)
|
function M.did_create_files(files)
|
||||||
did_file_operation(ms.workspace_didCreateFiles, 'didCreate', files)
|
did_file_operation(ms.workspace_didCreateFiles, "didCreate", files)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Notify the server that the client is about to delete files.
|
--- Notify the server that the client is about to delete files.
|
||||||
|
|
@ -258,13 +258,13 @@ end
|
||||||
---@return nil|{edit: lsp.WorkspaceEdit, offset_encoding: string}[]
|
---@return nil|{edit: lsp.WorkspaceEdit, offset_encoding: string}[]
|
||||||
---@return nil|string|lsp.ResponseError err
|
---@return nil|string|lsp.ResponseError err
|
||||||
function M.will_delete_files(files, options)
|
function M.will_delete_files(files, options)
|
||||||
return will_file_operation(ms.workspace_willDeleteFiles, 'willDelete', files, options)
|
return will_file_operation(ms.workspace_willDeleteFiles, "willDelete", files, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Notify the server that files were deleted from within the client.
|
--- Notify the server that files were deleted from within the client.
|
||||||
---@param files string[] The files and folders that were deleted
|
---@param files string[] The files and folders that were deleted
|
||||||
function M.did_delete_files(files)
|
function M.did_delete_files(files)
|
||||||
did_file_operation(ms.workspace_didDeleteFiles, 'didDelete', files)
|
did_file_operation(ms.workspace_didDeleteFiles, "didDelete", files)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Notify the server that the client is about to rename files.
|
--- Notify the server that the client is about to rename files.
|
||||||
|
|
@ -284,10 +284,10 @@ function M.will_rename_files(files, options)
|
||||||
for _, client in ipairs(clients) do
|
for _, client in ipairs(clients) do
|
||||||
local filters = vim.tbl_get(
|
local filters = vim.tbl_get(
|
||||||
client.server_capabilities,
|
client.server_capabilities,
|
||||||
'workspace',
|
"workspace",
|
||||||
'fileOperations',
|
"fileOperations",
|
||||||
'willRename',
|
"willRename",
|
||||||
'filters'
|
"filters"
|
||||||
)
|
)
|
||||||
local matching_files = get_matching_paths(client, filters, vim.tbl_keys(files))
|
local matching_files = get_matching_paths(client, filters, vim.tbl_keys(files))
|
||||||
if matching_files then
|
if matching_files then
|
||||||
|
|
@ -300,7 +300,7 @@ function M.will_rename_files(files, options)
|
||||||
end, matching_files),
|
end, matching_files),
|
||||||
}
|
}
|
||||||
local result, err
|
local result, err
|
||||||
if vim.fn.has('nvim-0.11') == 1 then
|
if vim.fn.has("nvim-0.11") == 1 then
|
||||||
result, err =
|
result, err =
|
||||||
client:request_sync(ms.workspace_willRenameFiles, params, options.timeout_ms or 1000, 0)
|
client:request_sync(ms.workspace_willRenameFiles, params, options.timeout_ms or 1000, 0)
|
||||||
else
|
else
|
||||||
|
|
@ -327,7 +327,7 @@ function M.did_rename_files(files)
|
||||||
local clients = get_clients(ms.workspace_didRenameFiles)
|
local clients = get_clients(ms.workspace_didRenameFiles)
|
||||||
for _, client in ipairs(clients) do
|
for _, client in ipairs(clients) do
|
||||||
local filters =
|
local filters =
|
||||||
vim.tbl_get(client.server_capabilities, 'workspace', 'fileOperations', 'didRename', 'filters')
|
vim.tbl_get(client.server_capabilities, "workspace", "fileOperations", "didRename", "filters")
|
||||||
local matching_files = get_matching_paths(client, filters, vim.tbl_keys(files))
|
local matching_files = get_matching_paths(client, filters, vim.tbl_keys(files))
|
||||||
if matching_files then
|
if matching_files then
|
||||||
local params = {
|
local params = {
|
||||||
|
|
@ -338,7 +338,7 @@ function M.did_rename_files(files)
|
||||||
}
|
}
|
||||||
end, matching_files),
|
end, matching_files),
|
||||||
}
|
}
|
||||||
if vim.fn.has('nvim-0.11') == 1 then
|
if vim.fn.has("nvim-0.11") == 1 then
|
||||||
client:notify(ms.workspace_didRenameFiles, params)
|
client:notify(ms.workspace_didRenameFiles, params)
|
||||||
else
|
else
|
||||||
---@diagnostic disable-next-line: param-type-mismatch
|
---@diagnostic disable-next-line: param-type-mismatch
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
local columns = require('oil.columns')
|
local columns = require("oil.columns")
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local layout = require('oil.layout')
|
local layout = require("oil.layout")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
---@param actions oil.Action[]
|
---@param actions oil.Action[]
|
||||||
|
|
@ -12,17 +12,17 @@ local function is_simple_edit(actions)
|
||||||
local num_move = 0
|
local num_move = 0
|
||||||
for _, action in ipairs(actions) do
|
for _, action in ipairs(actions) do
|
||||||
-- If there are any deletes, it is not a simple edit
|
-- If there are any deletes, it is not a simple edit
|
||||||
if action.type == 'delete' then
|
if action.type == "delete" then
|
||||||
return false
|
return false
|
||||||
elseif action.type == 'create' then
|
elseif action.type == "create" then
|
||||||
num_create = num_create + 1
|
num_create = num_create + 1
|
||||||
elseif action.type == 'copy' then
|
elseif action.type == "copy" then
|
||||||
num_copy = num_copy + 1
|
num_copy = num_copy + 1
|
||||||
-- Cross-adapter copies are not simple
|
-- Cross-adapter copies are not simple
|
||||||
if util.parse_url(action.src_url) ~= util.parse_url(action.dest_url) then
|
if util.parse_url(action.src_url) ~= util.parse_url(action.dest_url) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
num_move = num_move + 1
|
num_move = num_move + 1
|
||||||
-- Cross-adapter moves are not simple
|
-- Cross-adapter moves are not simple
|
||||||
if util.parse_url(action.src_url) ~= util.parse_url(action.dest_url) then
|
if util.parse_url(action.src_url) ~= util.parse_url(action.dest_url) then
|
||||||
|
|
@ -46,10 +46,10 @@ end
|
||||||
---@param lines string[]
|
---@param lines string[]
|
||||||
local function render_lines(winid, bufnr, lines)
|
local function render_lines(winid, bufnr, lines)
|
||||||
util.render_text(bufnr, lines, {
|
util.render_text(bufnr, lines, {
|
||||||
v_align = 'top',
|
v_align = "top",
|
||||||
h_align = 'left',
|
h_align = "left",
|
||||||
winid = winid,
|
winid = winid,
|
||||||
actions = { '[Y]es', '[N]o' },
|
actions = { "[Y]es", "[N]o" },
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -70,48 +70,48 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb)
|
||||||
|
|
||||||
-- Create the buffer
|
-- Create the buffer
|
||||||
local bufnr = vim.api.nvim_create_buf(false, true)
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
vim.bo[bufnr].bufhidden = 'wipe'
|
vim.bo[bufnr].bufhidden = "wipe"
|
||||||
local lines = {}
|
local lines = {}
|
||||||
local max_line_width = 0
|
local max_line_width = 0
|
||||||
for _, action in ipairs(actions) do
|
for _, action in ipairs(actions) do
|
||||||
local adapter = util.get_adapter_for_action(action)
|
local adapter = util.get_adapter_for_action(action)
|
||||||
local line
|
local line
|
||||||
if action.type == 'change' then
|
if action.type == "change" then
|
||||||
---@cast action oil.ChangeAction
|
---@cast action oil.ChangeAction
|
||||||
line = columns.render_change_action(adapter, action)
|
line = columns.render_change_action(adapter, action)
|
||||||
else
|
else
|
||||||
line = adapter.render_action(action)
|
line = adapter.render_action(action)
|
||||||
end
|
end
|
||||||
-- We can't handle lines with newlines in them
|
-- We can't handle lines with newlines in them
|
||||||
line = line:gsub('\n', '')
|
line = line:gsub("\n", "")
|
||||||
table.insert(lines, line)
|
table.insert(lines, line)
|
||||||
local line_width = vim.api.nvim_strwidth(line)
|
local line_width = vim.api.nvim_strwidth(line)
|
||||||
if line_width > max_line_width then
|
if line_width > max_line_width then
|
||||||
max_line_width = line_width
|
max_line_width = line_width
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.insert(lines, '')
|
table.insert(lines, "")
|
||||||
|
|
||||||
-- Create the floating window
|
-- Create the floating window
|
||||||
local width, height = layout.calculate_dims(max_line_width, #lines + 1, config.confirmation)
|
local width, height = layout.calculate_dims(max_line_width, #lines + 1, config.confirmation)
|
||||||
local ok, winid = pcall(vim.api.nvim_open_win, bufnr, true, {
|
local ok, winid = pcall(vim.api.nvim_open_win, bufnr, true, {
|
||||||
relative = 'editor',
|
relative = "editor",
|
||||||
width = width,
|
width = width,
|
||||||
height = height,
|
height = height,
|
||||||
row = math.floor((layout.get_editor_height() - height) / 2),
|
row = math.floor((layout.get_editor_height() - height) / 2),
|
||||||
col = math.floor((layout.get_editor_width() - width) / 2),
|
col = math.floor((layout.get_editor_width() - width) / 2),
|
||||||
zindex = 152, -- render on top of the floating window title
|
zindex = 152, -- render on top of the floating window title
|
||||||
style = 'minimal',
|
style = "minimal",
|
||||||
border = config.confirmation.border,
|
border = config.confirmation.border,
|
||||||
})
|
})
|
||||||
if not ok then
|
if not ok then
|
||||||
vim.notify(string.format('Error showing oil preview window: %s', winid), vim.log.levels.ERROR)
|
vim.notify(string.format("Error showing oil preview window: %s", winid), vim.log.levels.ERROR)
|
||||||
cb(false)
|
cb(false)
|
||||||
end
|
end
|
||||||
vim.bo[bufnr].filetype = 'oil_preview'
|
vim.bo[bufnr].filetype = "oil_preview"
|
||||||
vim.bo[bufnr].syntax = 'oil_preview'
|
vim.bo[bufnr].syntax = "oil_preview"
|
||||||
for k, v in pairs(config.confirmation.win_options) do
|
for k, v in pairs(config.confirmation.win_options) do
|
||||||
vim.api.nvim_set_option_value(k, v, { scope = 'local', win = winid })
|
vim.api.nvim_set_option_value(k, v, { scope = "local", win = winid })
|
||||||
end
|
end
|
||||||
|
|
||||||
render_lines(winid, bufnr, lines)
|
render_lines(winid, bufnr, lines)
|
||||||
|
|
@ -137,7 +137,7 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb)
|
||||||
end
|
end
|
||||||
cancel = make_callback(false)
|
cancel = make_callback(false)
|
||||||
confirm = make_callback(true)
|
confirm = make_callback(true)
|
||||||
vim.api.nvim_create_autocmd('BufLeave', {
|
vim.api.nvim_create_autocmd("BufLeave", {
|
||||||
callback = function()
|
callback = function()
|
||||||
cancel()
|
cancel()
|
||||||
end,
|
end,
|
||||||
|
|
@ -145,7 +145,7 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb)
|
||||||
nested = true,
|
nested = true,
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
})
|
})
|
||||||
vim.api.nvim_create_autocmd('WinLeave', {
|
vim.api.nvim_create_autocmd("WinLeave", {
|
||||||
callback = function()
|
callback = function()
|
||||||
cancel()
|
cancel()
|
||||||
end,
|
end,
|
||||||
|
|
@ -154,12 +154,12 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb)
|
||||||
})
|
})
|
||||||
table.insert(
|
table.insert(
|
||||||
autocmds,
|
autocmds,
|
||||||
vim.api.nvim_create_autocmd('VimResized', {
|
vim.api.nvim_create_autocmd("VimResized", {
|
||||||
callback = function()
|
callback = function()
|
||||||
if vim.api.nvim_win_is_valid(winid) then
|
if vim.api.nvim_win_is_valid(winid) then
|
||||||
width, height = layout.calculate_dims(max_line_width, #lines, config.confirmation)
|
width, height = layout.calculate_dims(max_line_width, #lines, config.confirmation)
|
||||||
vim.api.nvim_win_set_config(winid, {
|
vim.api.nvim_win_set_config(winid, {
|
||||||
relative = 'editor',
|
relative = "editor",
|
||||||
width = width,
|
width = width,
|
||||||
height = height,
|
height = height,
|
||||||
row = math.floor((layout.get_editor_height() - height) / 2),
|
row = math.floor((layout.get_editor_height() - height) / 2),
|
||||||
|
|
@ -173,17 +173,17 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb)
|
||||||
)
|
)
|
||||||
|
|
||||||
-- We used to use [C]ancel to cancel, so preserve the old keymap
|
-- We used to use [C]ancel to cancel, so preserve the old keymap
|
||||||
local cancel_keys = { 'n', 'N', 'c', 'C', 'q', '<C-c>', '<Esc>' }
|
local cancel_keys = { "n", "N", "c", "C", "q", "<C-c>", "<Esc>" }
|
||||||
for _, cancel_key in ipairs(cancel_keys) do
|
for _, cancel_key in ipairs(cancel_keys) do
|
||||||
vim.keymap.set('n', cancel_key, function()
|
vim.keymap.set("n", cancel_key, function()
|
||||||
cancel()
|
cancel()
|
||||||
end, { buffer = bufnr, nowait = true })
|
end, { buffer = bufnr, nowait = true })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- We used to use [O]k to confirm, so preserve the old keymap
|
-- We used to use [O]k to confirm, so preserve the old keymap
|
||||||
local confirm_keys = { 'y', 'Y', 'o', 'O' }
|
local confirm_keys = { "y", "Y", "o", "O" }
|
||||||
for _, confirm_key in ipairs(confirm_keys) do
|
for _, confirm_key in ipairs(confirm_keys) do
|
||||||
vim.keymap.set('n', confirm_key, function()
|
vim.keymap.set("n", confirm_key, function()
|
||||||
confirm()
|
confirm()
|
||||||
end, { buffer = bufnr, nowait = true })
|
end, { buffer = bufnr, nowait = true })
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
local Progress = require('oil.mutator.progress')
|
local Progress = require("oil.mutator.progress")
|
||||||
local Trie = require('oil.mutator.trie')
|
local Trie = require("oil.mutator.trie")
|
||||||
local cache = require('oil.cache')
|
local cache = require("oil.cache")
|
||||||
local columns = require('oil.columns')
|
local columns = require("oil.columns")
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local confirmation = require('oil.mutator.confirmation')
|
local confirmation = require("oil.mutator.confirmation")
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local lsp_helpers = require('oil.lsp.helpers')
|
local lsp_helpers = require("oil.lsp.helpers")
|
||||||
local oil = require('oil')
|
local oil = require("oil")
|
||||||
local parser = require('oil.mutator.parser')
|
local parser = require("oil.mutator.parser")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local view = require('oil.view')
|
local view = require("oil.view")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local FIELD_NAME = constants.FIELD_NAME
|
local FIELD_NAME = constants.FIELD_NAME
|
||||||
|
|
@ -73,7 +73,7 @@ M.create_actions_from_diffs = function(all_diffs)
|
||||||
local function add_action(action)
|
local function add_action(action)
|
||||||
local adapter = assert(config.get_adapter_by_scheme(action.dest_url or action.url))
|
local adapter = assert(config.get_adapter_by_scheme(action.dest_url or action.url))
|
||||||
if not adapter.filter_action or adapter.filter_action(action) then
|
if not adapter.filter_action or adapter.filter_action(action) then
|
||||||
if action.type == 'create' then
|
if action.type == "create" then
|
||||||
if seen_creates[action.url] then
|
if seen_creates[action.url] then
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
|
|
@ -87,11 +87,11 @@ M.create_actions_from_diffs = function(all_diffs)
|
||||||
for bufnr, diffs in pairs(all_diffs) do
|
for bufnr, diffs in pairs(all_diffs) do
|
||||||
local adapter = util.get_adapter(bufnr, true)
|
local adapter = util.get_adapter(bufnr, true)
|
||||||
if not adapter then
|
if not adapter then
|
||||||
error('Missing adapter')
|
error("Missing adapter")
|
||||||
end
|
end
|
||||||
local parent_url = vim.api.nvim_buf_get_name(bufnr)
|
local parent_url = vim.api.nvim_buf_get_name(bufnr)
|
||||||
for _, diff in ipairs(diffs) do
|
for _, diff in ipairs(diffs) do
|
||||||
if diff.type == 'new' then
|
if diff.type == "new" then
|
||||||
if diff.id then
|
if diff.id then
|
||||||
local by_id = diff_by_id[diff.id]
|
local by_id = diff_by_id[diff.id]
|
||||||
---HACK: set the destination on this diff for use later
|
---HACK: set the destination on this diff for use later
|
||||||
|
|
@ -100,28 +100,28 @@ M.create_actions_from_diffs = function(all_diffs)
|
||||||
table.insert(by_id, diff)
|
table.insert(by_id, diff)
|
||||||
else
|
else
|
||||||
-- Parse nested files like foo/bar/baz
|
-- Parse nested files like foo/bar/baz
|
||||||
local path_sep = fs.is_windows and '[/\\]' or '/'
|
local path_sep = fs.is_windows and "[/\\]" or "/"
|
||||||
local pieces = vim.split(diff.name, path_sep)
|
local pieces = vim.split(diff.name, path_sep)
|
||||||
local url = parent_url:gsub('/$', '')
|
local url = parent_url:gsub("/$", "")
|
||||||
for i, v in ipairs(pieces) do
|
for i, v in ipairs(pieces) do
|
||||||
local is_last = i == #pieces
|
local is_last = i == #pieces
|
||||||
local entry_type = is_last and diff.entry_type or 'directory'
|
local entry_type = is_last and diff.entry_type or "directory"
|
||||||
local alternation = v:match('{([^}]+)}')
|
local alternation = v:match("{([^}]+)}")
|
||||||
if is_last and alternation then
|
if is_last and alternation then
|
||||||
-- Parse alternations like foo.{js,test.js}
|
-- Parse alternations like foo.{js,test.js}
|
||||||
for _, alt in ipairs(vim.split(alternation, ',')) do
|
for _, alt in ipairs(vim.split(alternation, ",")) do
|
||||||
local alt_url = url .. '/' .. v:gsub('{[^}]+}', alt)
|
local alt_url = url .. "/" .. v:gsub("{[^}]+}", alt)
|
||||||
add_action({
|
add_action({
|
||||||
type = 'create',
|
type = "create",
|
||||||
url = alt_url,
|
url = alt_url,
|
||||||
entry_type = entry_type,
|
entry_type = entry_type,
|
||||||
link = diff.link,
|
link = diff.link,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
url = url .. '/' .. v
|
url = url .. "/" .. v
|
||||||
add_action({
|
add_action({
|
||||||
type = 'create',
|
type = "create",
|
||||||
url = url,
|
url = url,
|
||||||
entry_type = entry_type,
|
entry_type = entry_type,
|
||||||
link = diff.link,
|
link = diff.link,
|
||||||
|
|
@ -129,9 +129,9 @@ M.create_actions_from_diffs = function(all_diffs)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif diff.type == 'change' then
|
elseif diff.type == "change" then
|
||||||
add_action({
|
add_action({
|
||||||
type = 'change',
|
type = "change",
|
||||||
url = parent_url .. diff.name,
|
url = parent_url .. diff.name,
|
||||||
entry_type = diff.entry_type,
|
entry_type = diff.entry_type,
|
||||||
column = diff.column,
|
column = diff.column,
|
||||||
|
|
@ -151,7 +151,7 @@ M.create_actions_from_diffs = function(all_diffs)
|
||||||
for id, diffs in pairs(diff_by_id) do
|
for id, diffs in pairs(diff_by_id) do
|
||||||
local entry = cache.get_entry_by_id(id)
|
local entry = cache.get_entry_by_id(id)
|
||||||
if not entry then
|
if not entry then
|
||||||
error(string.format('Could not find entry %d', id))
|
error(string.format("Could not find entry %d", id))
|
||||||
end
|
end
|
||||||
---HACK: access the has_delete field on the list-like table of diffs
|
---HACK: access the has_delete field on the list-like table of diffs
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
|
@ -161,7 +161,7 @@ M.create_actions_from_diffs = function(all_diffs)
|
||||||
-- MOVE (+ optional copies) when has both creates and delete
|
-- MOVE (+ optional copies) when has both creates and delete
|
||||||
for i, diff in ipairs(diffs) do
|
for i, diff in ipairs(diffs) do
|
||||||
add_action({
|
add_action({
|
||||||
type = i == #diffs and 'move' or 'copy',
|
type = i == #diffs and "move" or "copy",
|
||||||
entry_type = entry[FIELD_TYPE],
|
entry_type = entry[FIELD_TYPE],
|
||||||
---HACK: access the dest field we set above
|
---HACK: access the dest field we set above
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
|
@ -172,7 +172,7 @@ M.create_actions_from_diffs = function(all_diffs)
|
||||||
else
|
else
|
||||||
-- DELETE when no create
|
-- DELETE when no create
|
||||||
add_action({
|
add_action({
|
||||||
type = 'delete',
|
type = "delete",
|
||||||
entry_type = entry[FIELD_TYPE],
|
entry_type = entry[FIELD_TYPE],
|
||||||
url = cache.get_parent_url(id) .. entry[FIELD_NAME],
|
url = cache.get_parent_url(id) .. entry[FIELD_NAME],
|
||||||
})
|
})
|
||||||
|
|
@ -181,7 +181,7 @@ M.create_actions_from_diffs = function(all_diffs)
|
||||||
-- COPY when create but no delete
|
-- COPY when create but no delete
|
||||||
for _, diff in ipairs(diffs) do
|
for _, diff in ipairs(diffs) do
|
||||||
add_action({
|
add_action({
|
||||||
type = 'copy',
|
type = "copy",
|
||||||
entry_type = entry[FIELD_TYPE],
|
entry_type = entry[FIELD_TYPE],
|
||||||
src_url = cache.get_parent_url(id) .. entry[FIELD_NAME],
|
src_url = cache.get_parent_url(id) .. entry[FIELD_NAME],
|
||||||
---HACK: access the dest field we set above
|
---HACK: access the dest field we set above
|
||||||
|
|
@ -201,9 +201,9 @@ M.enforce_action_order = function(actions)
|
||||||
local src_trie = Trie.new()
|
local src_trie = Trie.new()
|
||||||
local dest_trie = Trie.new()
|
local dest_trie = Trie.new()
|
||||||
for _, action in ipairs(actions) do
|
for _, action in ipairs(actions) do
|
||||||
if action.type == 'delete' or action.type == 'change' then
|
if action.type == "delete" or action.type == "change" then
|
||||||
src_trie:insert_action(action.url, action)
|
src_trie:insert_action(action.url, action)
|
||||||
elseif action.type == 'create' then
|
elseif action.type == "create" then
|
||||||
dest_trie:insert_action(action.url, action)
|
dest_trie:insert_action(action.url, action)
|
||||||
else
|
else
|
||||||
dest_trie:insert_action(action.dest_url, action)
|
dest_trie:insert_action(action.dest_url, action)
|
||||||
|
|
@ -223,18 +223,18 @@ M.enforce_action_order = function(actions)
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
local function get_deps(action)
|
local function get_deps(action)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
if action.type == 'delete' then
|
if action.type == "delete" then
|
||||||
src_trie:accum_children_of(action.url, ret)
|
src_trie:accum_children_of(action.url, ret)
|
||||||
elseif action.type == 'create' then
|
elseif action.type == "create" then
|
||||||
-- Finish operating on parents first
|
-- Finish operating on parents first
|
||||||
-- e.g. NEW /a BEFORE NEW /a/b
|
-- e.g. NEW /a BEFORE NEW /a/b
|
||||||
dest_trie:accum_first_parents_of(action.url, ret)
|
dest_trie:accum_first_parents_of(action.url, ret)
|
||||||
-- Process remove path before creating new path
|
-- Process remove path before creating new path
|
||||||
-- e.g. DELETE /a BEFORE NEW /a
|
-- e.g. DELETE /a BEFORE NEW /a
|
||||||
src_trie:accum_actions_at(action.url, ret, function(a)
|
src_trie:accum_actions_at(action.url, ret, function(a)
|
||||||
return a.type == 'move' or a.type == 'delete'
|
return a.type == "move" or a.type == "delete"
|
||||||
end)
|
end)
|
||||||
elseif action.type == 'change' then
|
elseif action.type == "change" then
|
||||||
-- Finish operating on parents first
|
-- Finish operating on parents first
|
||||||
-- e.g. NEW /a BEFORE CHANGE /a/b
|
-- e.g. NEW /a BEFORE CHANGE /a/b
|
||||||
dest_trie:accum_first_parents_of(action.url, ret)
|
dest_trie:accum_first_parents_of(action.url, ret)
|
||||||
|
|
@ -244,9 +244,9 @@ M.enforce_action_order = function(actions)
|
||||||
-- Finish copy from operations first
|
-- Finish copy from operations first
|
||||||
-- e.g. COPY /a -> /b BEFORE CHANGE /a
|
-- e.g. COPY /a -> /b BEFORE CHANGE /a
|
||||||
src_trie:accum_actions_at(action.url, ret, function(entry)
|
src_trie:accum_actions_at(action.url, ret, function(entry)
|
||||||
return entry.type == 'copy'
|
return entry.type == "copy"
|
||||||
end)
|
end)
|
||||||
elseif action.type == 'move' then
|
elseif action.type == "move" then
|
||||||
-- Finish operating on parents first
|
-- Finish operating on parents first
|
||||||
-- e.g. NEW /a BEFORE MOVE /z -> /a/b
|
-- e.g. NEW /a BEFORE MOVE /z -> /a/b
|
||||||
dest_trie:accum_first_parents_of(action.dest_url, ret)
|
dest_trie:accum_first_parents_of(action.dest_url, ret)
|
||||||
|
|
@ -260,9 +260,9 @@ M.enforce_action_order = function(actions)
|
||||||
-- Process remove path before moving to new path
|
-- Process remove path before moving to new path
|
||||||
-- e.g. MOVE /a -> /b BEFORE MOVE /c -> /a
|
-- e.g. MOVE /a -> /b BEFORE MOVE /c -> /a
|
||||||
src_trie:accum_actions_at(action.dest_url, ret, function(a)
|
src_trie:accum_actions_at(action.dest_url, ret, function(a)
|
||||||
return a.type == 'move' or a.type == 'delete'
|
return a.type == "move" or a.type == "delete"
|
||||||
end)
|
end)
|
||||||
elseif action.type == 'copy' then
|
elseif action.type == "copy" then
|
||||||
-- Finish operating on parents first
|
-- Finish operating on parents first
|
||||||
-- e.g. NEW /a BEFORE COPY /z -> /a/b
|
-- e.g. NEW /a BEFORE COPY /z -> /a/b
|
||||||
dest_trie:accum_first_parents_of(action.dest_url, ret)
|
dest_trie:accum_first_parents_of(action.dest_url, ret)
|
||||||
|
|
@ -272,7 +272,7 @@ M.enforce_action_order = function(actions)
|
||||||
-- Process remove path before copying to new path
|
-- Process remove path before copying to new path
|
||||||
-- e.g. MOVE /a -> /b BEFORE COPY /c -> /a
|
-- e.g. MOVE /a -> /b BEFORE COPY /c -> /a
|
||||||
src_trie:accum_actions_at(action.dest_url, ret, function(a)
|
src_trie:accum_actions_at(action.dest_url, ret, function(a)
|
||||||
return a.type == 'move' or a.type == 'delete'
|
return a.type == "move" or a.type == "delete"
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
|
|
@ -312,24 +312,24 @@ M.enforce_action_order = function(actions)
|
||||||
if selected then
|
if selected then
|
||||||
to_remove = selected
|
to_remove = selected
|
||||||
else
|
else
|
||||||
if loop_action and loop_action.type == 'move' then
|
if loop_action and loop_action.type == "move" then
|
||||||
-- If this is moving a parent into itself, that's an error
|
-- If this is moving a parent into itself, that's an error
|
||||||
if vim.startswith(loop_action.dest_url, loop_action.src_url) then
|
if vim.startswith(loop_action.dest_url, loop_action.src_url) then
|
||||||
error('Detected cycle in desired paths')
|
error("Detected cycle in desired paths")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- We've detected a move cycle (e.g. MOVE /a -> /b + MOVE /b -> /a)
|
-- We've detected a move cycle (e.g. MOVE /a -> /b + MOVE /b -> /a)
|
||||||
-- Split one of the moves and retry
|
-- Split one of the moves and retry
|
||||||
local intermediate_url =
|
local intermediate_url =
|
||||||
string.format('%s__oil_tmp_%05d', loop_action.src_url, math.random(999999))
|
string.format("%s__oil_tmp_%05d", loop_action.src_url, math.random(999999))
|
||||||
local move_1 = {
|
local move_1 = {
|
||||||
type = 'move',
|
type = "move",
|
||||||
entry_type = loop_action.entry_type,
|
entry_type = loop_action.entry_type,
|
||||||
src_url = loop_action.src_url,
|
src_url = loop_action.src_url,
|
||||||
dest_url = intermediate_url,
|
dest_url = intermediate_url,
|
||||||
}
|
}
|
||||||
local move_2 = {
|
local move_2 = {
|
||||||
type = 'move',
|
type = "move",
|
||||||
entry_type = loop_action.entry_type,
|
entry_type = loop_action.entry_type,
|
||||||
src_url = intermediate_url,
|
src_url = intermediate_url,
|
||||||
dest_url = loop_action.dest_url,
|
dest_url = loop_action.dest_url,
|
||||||
|
|
@ -340,16 +340,16 @@ M.enforce_action_order = function(actions)
|
||||||
dest_trie:insert_action(move_1.dest_url, move_1)
|
dest_trie:insert_action(move_1.dest_url, move_1)
|
||||||
src_trie:insert_action(move_1.src_url, move_1)
|
src_trie:insert_action(move_1.src_url, move_1)
|
||||||
else
|
else
|
||||||
error('Detected cycle in desired paths')
|
error("Detected cycle in desired paths")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if selected then
|
if selected then
|
||||||
if selected.type == 'move' or selected.type == 'copy' then
|
if selected.type == "move" or selected.type == "copy" then
|
||||||
if vim.startswith(selected.dest_url, selected.src_url .. '/') then
|
if vim.startswith(selected.dest_url, selected.src_url .. "/") then
|
||||||
error(
|
error(
|
||||||
string.format(
|
string.format(
|
||||||
'Cannot move or copy parent into itself: %s -> %s',
|
"Cannot move or copy parent into itself: %s -> %s",
|
||||||
selected.src_url,
|
selected.src_url,
|
||||||
selected.dest_url
|
selected.dest_url
|
||||||
)
|
)
|
||||||
|
|
@ -360,9 +360,9 @@ M.enforce_action_order = function(actions)
|
||||||
end
|
end
|
||||||
|
|
||||||
if to_remove then
|
if to_remove then
|
||||||
if to_remove.type == 'delete' or to_remove.type == 'change' then
|
if to_remove.type == "delete" or to_remove.type == "change" then
|
||||||
src_trie:remove_action(to_remove.url, to_remove)
|
src_trie:remove_action(to_remove.url, to_remove)
|
||||||
elseif to_remove.type == 'create' then
|
elseif to_remove.type == "create" then
|
||||||
dest_trie:remove_action(to_remove.url, to_remove)
|
dest_trie:remove_action(to_remove.url, to_remove)
|
||||||
else
|
else
|
||||||
dest_trie:remove_action(to_remove.dest_url, to_remove)
|
dest_trie:remove_action(to_remove.dest_url, to_remove)
|
||||||
|
|
@ -387,8 +387,8 @@ local progress
|
||||||
---@param cb fun(err: nil|string)
|
---@param cb fun(err: nil|string)
|
||||||
M.process_actions = function(actions, cb)
|
M.process_actions = function(actions, cb)
|
||||||
vim.api.nvim_exec_autocmds(
|
vim.api.nvim_exec_autocmds(
|
||||||
'User',
|
"User",
|
||||||
{ pattern = 'OilActionsPre', modeline = false, data = { actions = actions } }
|
{ pattern = "OilActionsPre", modeline = false, data = { actions = actions } }
|
||||||
)
|
)
|
||||||
|
|
||||||
local did_complete = nil
|
local did_complete = nil
|
||||||
|
|
@ -398,14 +398,14 @@ M.process_actions = function(actions, cb)
|
||||||
|
|
||||||
-- Convert some cross-adapter moves to a copy + delete
|
-- Convert some cross-adapter moves to a copy + delete
|
||||||
for _, action in ipairs(actions) do
|
for _, action in ipairs(actions) do
|
||||||
if action.type == 'move' then
|
if action.type == "move" then
|
||||||
local _, cross_action = util.get_adapter_for_action(action)
|
local _, cross_action = util.get_adapter_for_action(action)
|
||||||
-- Only do the conversion if the cross-adapter support is "copy"
|
-- Only do the conversion if the cross-adapter support is "copy"
|
||||||
if cross_action == 'copy' then
|
if cross_action == "copy" then
|
||||||
---@diagnostic disable-next-line: assign-type-mismatch
|
---@diagnostic disable-next-line: assign-type-mismatch
|
||||||
action.type = 'copy'
|
action.type = "copy"
|
||||||
table.insert(actions, {
|
table.insert(actions, {
|
||||||
type = 'delete',
|
type = "delete",
|
||||||
url = action.src_url,
|
url = action.src_url,
|
||||||
entry_type = action.entry_type,
|
entry_type = action.entry_type,
|
||||||
})
|
})
|
||||||
|
|
@ -421,8 +421,8 @@ M.process_actions = function(actions, cb)
|
||||||
progress:close()
|
progress:close()
|
||||||
progress = nil
|
progress = nil
|
||||||
vim.api.nvim_exec_autocmds(
|
vim.api.nvim_exec_autocmds(
|
||||||
'User',
|
"User",
|
||||||
{ pattern = 'OilActionsPost', modeline = false, data = { err = err, actions = actions } }
|
{ pattern = "OilActionsPost", modeline = false, data = { err = err, actions = actions } }
|
||||||
)
|
)
|
||||||
cb(err)
|
cb(err)
|
||||||
end
|
end
|
||||||
|
|
@ -435,7 +435,7 @@ M.process_actions = function(actions, cb)
|
||||||
-- TODO some actions are actually cancelable.
|
-- TODO some actions are actually cancelable.
|
||||||
-- We should stop them instead of stopping after the current action
|
-- We should stop them instead of stopping after the current action
|
||||||
cancel = function()
|
cancel = function()
|
||||||
finish('Canceled')
|
finish("Canceled")
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
@ -472,7 +472,7 @@ M.process_actions = function(actions, cb)
|
||||||
next_action()
|
next_action()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
if action.type == 'change' then
|
if action.type == "change" then
|
||||||
---@cast action oil.ChangeAction
|
---@cast action oil.ChangeAction
|
||||||
columns.perform_change_action(adapter, action, callback)
|
columns.perform_change_action(adapter, action, callback)
|
||||||
else
|
else
|
||||||
|
|
@ -502,7 +502,7 @@ M.try_write_changes = function(confirm, cb)
|
||||||
cb = function(_err) end
|
cb = function(_err) end
|
||||||
end
|
end
|
||||||
if mutation_in_progress then
|
if mutation_in_progress then
|
||||||
cb('Cannot perform mutation when already in progress')
|
cb("Cannot perform mutation when already in progress")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local current_buf = vim.api.nvim_get_current_buf()
|
local current_buf = vim.api.nvim_get_current_buf()
|
||||||
|
|
@ -537,7 +537,7 @@ M.try_write_changes = function(confirm, cb)
|
||||||
mutation_in_progress = false
|
mutation_in_progress = false
|
||||||
end
|
end
|
||||||
|
|
||||||
local ns = vim.api.nvim_create_namespace('Oil')
|
local ns = vim.api.nvim_create_namespace("Oil")
|
||||||
vim.diagnostic.reset(ns)
|
vim.diagnostic.reset(ns)
|
||||||
if not vim.tbl_isempty(all_errors) then
|
if not vim.tbl_isempty(all_errors) then
|
||||||
for bufnr, errors in pairs(all_errors) do
|
for bufnr, errors in pairs(all_errors) do
|
||||||
|
|
@ -564,7 +564,7 @@ M.try_write_changes = function(confirm, cb)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
unlock()
|
unlock()
|
||||||
cb('Error parsing oil buffers')
|
cb("Error parsing oil buffers")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -572,7 +572,7 @@ M.try_write_changes = function(confirm, cb)
|
||||||
confirmation.show(actions, confirm, function(proceed)
|
confirmation.show(actions, confirm, function(proceed)
|
||||||
if not proceed then
|
if not proceed then
|
||||||
unlock()
|
unlock()
|
||||||
cb('Canceled')
|
cb("Canceled")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -581,7 +581,7 @@ M.try_write_changes = function(confirm, cb)
|
||||||
vim.schedule_wrap(function(err)
|
vim.schedule_wrap(function(err)
|
||||||
view.unlock_buffers()
|
view.unlock_buffers()
|
||||||
if err then
|
if err then
|
||||||
err = string.format('[oil] Error applying actions: %s', err)
|
err = string.format("[oil] Error applying actions: %s", err)
|
||||||
view.rerender_all_oil_buffers(nil, function()
|
view.rerender_all_oil_buffers(nil, function()
|
||||||
cb(err)
|
cb(err)
|
||||||
end)
|
end)
|
||||||
|
|
@ -591,13 +591,13 @@ M.try_write_changes = function(confirm, cb)
|
||||||
-- get the entry under the cursor and make sure the cursor stays on it
|
-- get the entry under the cursor and make sure the cursor stays on it
|
||||||
view.set_last_cursor(
|
view.set_last_cursor(
|
||||||
vim.api.nvim_buf_get_name(0),
|
vim.api.nvim_buf_get_name(0),
|
||||||
vim.split(current_entry.parsed_name or current_entry.name, '/')[1]
|
vim.split(current_entry.parsed_name or current_entry.name, "/")[1]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
view.rerender_all_oil_buffers(nil, function(render_err)
|
view.rerender_all_oil_buffers(nil, function(render_err)
|
||||||
vim.api.nvim_exec_autocmds(
|
vim.api.nvim_exec_autocmds(
|
||||||
'User',
|
"User",
|
||||||
{ pattern = 'OilMutationComplete', modeline = false }
|
{ pattern = "OilMutationComplete", modeline = false }
|
||||||
)
|
)
|
||||||
cb(render_err)
|
cb(render_err)
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
local cache = require('oil.cache')
|
local cache = require("oil.cache")
|
||||||
local columns = require('oil.columns')
|
local columns = require("oil.columns")
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local view = require('oil.view')
|
local view = require("oil.view")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local FIELD_ID = constants.FIELD_ID
|
local FIELD_ID = constants.FIELD_ID
|
||||||
|
|
@ -37,7 +37,7 @@ local FIELD_META = constants.FIELD_META
|
||||||
---@return string
|
---@return string
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function parsedir(name)
|
local function parsedir(name)
|
||||||
local isdir = vim.endswith(name, '/') or (fs.is_windows and vim.endswith(name, '\\'))
|
local isdir = vim.endswith(name, "/") or (fs.is_windows and vim.endswith(name, "\\"))
|
||||||
if isdir then
|
if isdir then
|
||||||
name = name:sub(1, name:len() - 1)
|
name = name:sub(1, name:len() - 1)
|
||||||
end
|
end
|
||||||
|
|
@ -52,8 +52,8 @@ local function compare_link_target(meta, parsed_entry)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
-- Make sure we trim off any trailing path slashes from both sources
|
-- Make sure we trim off any trailing path slashes from both sources
|
||||||
local meta_name = meta.link:gsub('[/\\]$', '')
|
local meta_name = meta.link:gsub("[/\\]$", "")
|
||||||
local parsed_name = parsed_entry.link_target:gsub('[/\\]$', '')
|
local parsed_name = parsed_entry.link_target:gsub("[/\\]$", "")
|
||||||
return meta_name == parsed_name
|
return meta_name == parsed_name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -72,9 +72,9 @@ M.parse_line = function(adapter, line, column_defs)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
local ranges = {}
|
local ranges = {}
|
||||||
local start = 1
|
local start = 1
|
||||||
local value, rem = line:match('^/(%d+) (.+)$')
|
local value, rem = line:match("^/(%d+) (.+)$")
|
||||||
if not value then
|
if not value then
|
||||||
return nil, 'Malformed ID at start of line'
|
return nil, "Malformed ID at start of line"
|
||||||
end
|
end
|
||||||
ranges.id = { start, value:len() + 1 }
|
ranges.id = { start, value:len() + 1 }
|
||||||
start = ranges.id[2] + 1
|
start = ranges.id[2] + 1
|
||||||
|
|
@ -97,7 +97,7 @@ M.parse_line = function(adapter, line, column_defs)
|
||||||
local start_len = string.len(rem)
|
local start_len = string.len(rem)
|
||||||
value, rem = columns.parse_col(adapter, assert(rem), def)
|
value, rem = columns.parse_col(adapter, assert(rem), def)
|
||||||
if not rem then
|
if not rem then
|
||||||
return nil, string.format('Parsing %s failed', name)
|
return nil, string.format("Parsing %s failed", name)
|
||||||
end
|
end
|
||||||
ret[name] = value
|
ret[name] = value
|
||||||
range[2] = range[1] + start_len - string.len(rem) - 1
|
range[2] = range[1] + start_len - string.len(rem) - 1
|
||||||
|
|
@ -108,10 +108,10 @@ M.parse_line = function(adapter, line, column_defs)
|
||||||
if name then
|
if name then
|
||||||
local isdir
|
local isdir
|
||||||
name, isdir = parsedir(vim.trim(name))
|
name, isdir = parsedir(vim.trim(name))
|
||||||
if name ~= '' then
|
if name ~= "" then
|
||||||
ret.name = name
|
ret.name = name
|
||||||
end
|
end
|
||||||
ret._type = isdir and 'directory' or 'file'
|
ret._type = isdir and "directory" or "file"
|
||||||
end
|
end
|
||||||
local entry = cache.get_entry_by_id(ret.id)
|
local entry = cache.get_entry_by_id(ret.id)
|
||||||
ranges.name = { start, start + string.len(rem) - 1 }
|
ranges.name = { start, start + string.len(rem) - 1 }
|
||||||
|
|
@ -122,20 +122,20 @@ M.parse_line = function(adapter, line, column_defs)
|
||||||
-- Parse the symlink syntax
|
-- Parse the symlink syntax
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
local entry_type = entry[FIELD_TYPE]
|
local entry_type = entry[FIELD_TYPE]
|
||||||
if entry_type == 'link' and meta and meta.link then
|
if entry_type == "link" and meta and meta.link then
|
||||||
local name_pieces = vim.split(ret.name, ' -> ', { plain = true })
|
local name_pieces = vim.split(ret.name, " -> ", { plain = true })
|
||||||
if #name_pieces ~= 2 then
|
if #name_pieces ~= 2 then
|
||||||
ret.name = ''
|
ret.name = ""
|
||||||
return { data = ret, ranges = ranges }
|
return { data = ret, ranges = ranges }
|
||||||
end
|
end
|
||||||
ranges.name = { start, start + string.len(name_pieces[1]) - 1 }
|
ranges.name = { start, start + string.len(name_pieces[1]) - 1 }
|
||||||
ret.name = parsedir(vim.trim(name_pieces[1]))
|
ret.name = parsedir(vim.trim(name_pieces[1]))
|
||||||
ret.link_target = name_pieces[2]
|
ret.link_target = name_pieces[2]
|
||||||
ret._type = 'link'
|
ret._type = "link"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try to keep the same file type
|
-- Try to keep the same file type
|
||||||
if entry_type ~= 'directory' and entry_type ~= 'file' and ret._type ~= 'directory' then
|
if entry_type ~= "directory" and entry_type ~= "file" and ret._type ~= "directory" then
|
||||||
ret._type = entry[FIELD_TYPE]
|
ret._type = entry[FIELD_TYPE]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -187,7 +187,7 @@ M.parse = function(bufnr)
|
||||||
name = name:lower()
|
name = name:lower()
|
||||||
end
|
end
|
||||||
if seen_names[name] then
|
if seen_names[name] then
|
||||||
table.insert(errors, { message = 'Duplicate filename', lnum = i - 1, end_lnum = i, col = 0 })
|
table.insert(errors, { message = "Duplicate filename", lnum = i - 1, end_lnum = i, col = 0 })
|
||||||
else
|
else
|
||||||
seen_names[name] = true
|
seen_names[name] = true
|
||||||
end
|
end
|
||||||
|
|
@ -197,7 +197,7 @@ M.parse = function(bufnr)
|
||||||
-- hack to be compatible with Lua 5.1
|
-- hack to be compatible with Lua 5.1
|
||||||
-- use return instead of goto
|
-- use return instead of goto
|
||||||
(function()
|
(function()
|
||||||
if line:match('^/%d+') then
|
if line:match("^/%d+") then
|
||||||
-- Parse the line for an existing entry
|
-- Parse the line for an existing entry
|
||||||
local result, err = M.parse_line(adapter, line, column_defs)
|
local result, err = M.parse_line(adapter, line, column_defs)
|
||||||
if not result or err then
|
if not result or err then
|
||||||
|
|
@ -217,11 +217,11 @@ M.parse = function(bufnr)
|
||||||
|
|
||||||
local err_message
|
local err_message
|
||||||
if not parsed_entry.name then
|
if not parsed_entry.name then
|
||||||
err_message = 'No filename found'
|
err_message = "No filename found"
|
||||||
elseif not entry then
|
elseif not entry then
|
||||||
err_message = 'Could not find existing entry (was the ID changed?)'
|
err_message = "Could not find existing entry (was the ID changed?)"
|
||||||
elseif parsed_entry.name:match('/') or parsed_entry.name:match(fs.sep) then
|
elseif parsed_entry.name:match("/") or parsed_entry.name:match(fs.sep) then
|
||||||
err_message = 'Filename cannot contain path separator'
|
err_message = "Filename cannot contain path separator"
|
||||||
end
|
end
|
||||||
if err_message then
|
if err_message then
|
||||||
table.insert(errors, {
|
table.insert(errors, {
|
||||||
|
|
@ -237,16 +237,16 @@ M.parse = function(bufnr)
|
||||||
check_dupe(parsed_entry.name, i)
|
check_dupe(parsed_entry.name, i)
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
if original_entries[parsed_entry.name] == parsed_entry.id then
|
if original_entries[parsed_entry.name] == parsed_entry.id then
|
||||||
if entry[FIELD_TYPE] == 'link' and not compare_link_target(meta, parsed_entry) then
|
if entry[FIELD_TYPE] == "link" and not compare_link_target(meta, parsed_entry) then
|
||||||
table.insert(diffs, {
|
table.insert(diffs, {
|
||||||
type = 'new',
|
type = "new",
|
||||||
name = parsed_entry.name,
|
name = parsed_entry.name,
|
||||||
entry_type = 'link',
|
entry_type = "link",
|
||||||
link = parsed_entry.link_target,
|
link = parsed_entry.link_target,
|
||||||
})
|
})
|
||||||
elseif entry[FIELD_TYPE] ~= parsed_entry._type then
|
elseif entry[FIELD_TYPE] ~= parsed_entry._type then
|
||||||
table.insert(diffs, {
|
table.insert(diffs, {
|
||||||
type = 'new',
|
type = "new",
|
||||||
name = parsed_entry.name,
|
name = parsed_entry.name,
|
||||||
entry_type = parsed_entry._type,
|
entry_type = parsed_entry._type,
|
||||||
})
|
})
|
||||||
|
|
@ -255,7 +255,7 @@ M.parse = function(bufnr)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
table.insert(diffs, {
|
table.insert(diffs, {
|
||||||
type = 'new',
|
type = "new",
|
||||||
name = parsed_entry.name,
|
name = parsed_entry.name,
|
||||||
entry_type = parsed_entry._type,
|
entry_type = parsed_entry._type,
|
||||||
id = parsed_entry.id,
|
id = parsed_entry.id,
|
||||||
|
|
@ -267,7 +267,7 @@ M.parse = function(bufnr)
|
||||||
local col_name = util.split_config(col_def)
|
local col_name = util.split_config(col_def)
|
||||||
if columns.compare(adapter, col_name, entry, parsed_entry[col_name]) then
|
if columns.compare(adapter, col_name, entry, parsed_entry[col_name]) then
|
||||||
table.insert(diffs, {
|
table.insert(diffs, {
|
||||||
type = 'change',
|
type = "change",
|
||||||
name = parsed_entry.name,
|
name = parsed_entry.name,
|
||||||
entry_type = entry[FIELD_TYPE],
|
entry_type = entry[FIELD_TYPE],
|
||||||
column = col_name,
|
column = col_name,
|
||||||
|
|
@ -278,7 +278,7 @@ M.parse = function(bufnr)
|
||||||
else
|
else
|
||||||
-- Parse a new entry
|
-- Parse a new entry
|
||||||
local name, isdir = parsedir(vim.trim(line))
|
local name, isdir = parsedir(vim.trim(line))
|
||||||
if vim.startswith(name, '/') then
|
if vim.startswith(name, "/") then
|
||||||
table.insert(errors, {
|
table.insert(errors, {
|
||||||
message = "Paths cannot start with '/'",
|
message = "Paths cannot start with '/'",
|
||||||
lnum = i - 1,
|
lnum = i - 1,
|
||||||
|
|
@ -287,17 +287,17 @@ M.parse = function(bufnr)
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if name ~= '' then
|
if name ~= "" then
|
||||||
local link_pieces = vim.split(name, ' -> ', { plain = true })
|
local link_pieces = vim.split(name, " -> ", { plain = true })
|
||||||
local entry_type = isdir and 'directory' or 'file'
|
local entry_type = isdir and "directory" or "file"
|
||||||
local link
|
local link
|
||||||
if #link_pieces == 2 then
|
if #link_pieces == 2 then
|
||||||
entry_type = 'link'
|
entry_type = "link"
|
||||||
name, link = unpack(link_pieces)
|
name, link = unpack(link_pieces)
|
||||||
end
|
end
|
||||||
check_dupe(name, i)
|
check_dupe(name, i)
|
||||||
table.insert(diffs, {
|
table.insert(diffs, {
|
||||||
type = 'new',
|
type = "new",
|
||||||
name = name,
|
name = name,
|
||||||
entry_type = entry_type,
|
entry_type = entry_type,
|
||||||
link = link,
|
link = link,
|
||||||
|
|
@ -309,7 +309,7 @@ M.parse = function(bufnr)
|
||||||
|
|
||||||
for name, child_id in pairs(original_entries) do
|
for name, child_id in pairs(original_entries) do
|
||||||
table.insert(diffs, {
|
table.insert(diffs, {
|
||||||
type = 'delete',
|
type = "delete",
|
||||||
name = name,
|
name = name,
|
||||||
id = child_id,
|
id = child_id,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
local columns = require('oil.columns')
|
local columns = require("oil.columns")
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local layout = require('oil.layout')
|
local layout = require("oil.layout")
|
||||||
local loading = require('oil.loading')
|
local loading = require("oil.loading")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local Progress = {}
|
local Progress = {}
|
||||||
|
|
||||||
local FPS = 20
|
local FPS = 20
|
||||||
|
|
||||||
function Progress.new()
|
function Progress.new()
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
lines = { '', '' },
|
lines = { "", "" },
|
||||||
count = '',
|
count = "",
|
||||||
spinner = '',
|
spinner = "",
|
||||||
bufnr = nil,
|
bufnr = nil,
|
||||||
winid = nil,
|
winid = nil,
|
||||||
min_bufnr = nil,
|
min_bufnr = nil,
|
||||||
|
|
@ -40,18 +40,18 @@ function Progress:show(opts)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local bufnr = vim.api.nvim_create_buf(false, true)
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
vim.bo[bufnr].bufhidden = 'wipe'
|
vim.bo[bufnr].bufhidden = "wipe"
|
||||||
self.bufnr = bufnr
|
self.bufnr = bufnr
|
||||||
self.cancel = opts.cancel or self.cancel
|
self.cancel = opts.cancel or self.cancel
|
||||||
local loading_iter = loading.get_bar_iter()
|
local loading_iter = loading.get_bar_iter()
|
||||||
local spinner = loading.get_iter('dots')
|
local spinner = loading.get_iter("dots")
|
||||||
if not self.timer then
|
if not self.timer then
|
||||||
self.timer = vim.loop.new_timer()
|
self.timer = vim.loop.new_timer()
|
||||||
self.timer:start(
|
self.timer:start(
|
||||||
0,
|
0,
|
||||||
math.floor(1000 / FPS),
|
math.floor(1000 / FPS),
|
||||||
vim.schedule_wrap(function()
|
vim.schedule_wrap(function()
|
||||||
self.lines[2] = string.format('%s %s', self.count, loading_iter())
|
self.lines[2] = string.format("%s %s", self.count, loading_iter())
|
||||||
self.spinner = spinner()
|
self.spinner = spinner()
|
||||||
self:_render()
|
self:_render()
|
||||||
end)
|
end)
|
||||||
|
|
@ -59,22 +59,22 @@ function Progress:show(opts)
|
||||||
end
|
end
|
||||||
local width, height = layout.calculate_dims(120, 10, config.progress)
|
local width, height = layout.calculate_dims(120, 10, config.progress)
|
||||||
self.winid = vim.api.nvim_open_win(self.bufnr, true, {
|
self.winid = vim.api.nvim_open_win(self.bufnr, true, {
|
||||||
relative = 'editor',
|
relative = "editor",
|
||||||
width = width,
|
width = width,
|
||||||
height = height,
|
height = height,
|
||||||
row = math.floor((layout.get_editor_height() - height) / 2),
|
row = math.floor((layout.get_editor_height() - height) / 2),
|
||||||
col = math.floor((layout.get_editor_width() - width) / 2),
|
col = math.floor((layout.get_editor_width() - width) / 2),
|
||||||
zindex = 152, -- render on top of the floating window title
|
zindex = 152, -- render on top of the floating window title
|
||||||
style = 'minimal',
|
style = "minimal",
|
||||||
border = config.progress.border,
|
border = config.progress.border,
|
||||||
})
|
})
|
||||||
vim.bo[self.bufnr].filetype = 'oil_progress'
|
vim.bo[self.bufnr].filetype = "oil_progress"
|
||||||
for k, v in pairs(config.progress.win_options) do
|
for k, v in pairs(config.progress.win_options) do
|
||||||
vim.api.nvim_set_option_value(k, v, { scope = 'local', win = self.winid })
|
vim.api.nvim_set_option_value(k, v, { scope = "local", win = self.winid })
|
||||||
end
|
end
|
||||||
table.insert(
|
table.insert(
|
||||||
self.autocmds,
|
self.autocmds,
|
||||||
vim.api.nvim_create_autocmd('VimResized', {
|
vim.api.nvim_create_autocmd("VimResized", {
|
||||||
callback = function()
|
callback = function()
|
||||||
self:_reposition()
|
self:_reposition()
|
||||||
end,
|
end,
|
||||||
|
|
@ -82,7 +82,7 @@ function Progress:show(opts)
|
||||||
)
|
)
|
||||||
table.insert(
|
table.insert(
|
||||||
self.autocmds,
|
self.autocmds,
|
||||||
vim.api.nvim_create_autocmd('WinLeave', {
|
vim.api.nvim_create_autocmd("WinLeave", {
|
||||||
callback = function()
|
callback = function()
|
||||||
self:minimize()
|
self:minimize()
|
||||||
end,
|
end,
|
||||||
|
|
@ -94,17 +94,17 @@ function Progress:show(opts)
|
||||||
vim.api.nvim_win_close(self.winid, true)
|
vim.api.nvim_win_close(self.winid, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.keymap.set('n', 'c', cancel, { buffer = self.bufnr, nowait = true })
|
vim.keymap.set("n", "c", cancel, { buffer = self.bufnr, nowait = true })
|
||||||
vim.keymap.set('n', 'C', cancel, { buffer = self.bufnr, nowait = true })
|
vim.keymap.set("n", "C", cancel, { buffer = self.bufnr, nowait = true })
|
||||||
vim.keymap.set('n', 'm', minimize, { buffer = self.bufnr, nowait = true })
|
vim.keymap.set("n", "m", minimize, { buffer = self.bufnr, nowait = true })
|
||||||
vim.keymap.set('n', 'M', minimize, { buffer = self.bufnr, nowait = true })
|
vim.keymap.set("n", "M", minimize, { buffer = self.bufnr, nowait = true })
|
||||||
end
|
end
|
||||||
|
|
||||||
function Progress:restore()
|
function Progress:restore()
|
||||||
if self.closing then
|
if self.closing then
|
||||||
return
|
return
|
||||||
elseif not self:is_minimized() then
|
elseif not self:is_minimized() then
|
||||||
error('Cannot restore progress window: not minimized')
|
error("Cannot restore progress window: not minimized")
|
||||||
end
|
end
|
||||||
self:_cleanup_minimized_win()
|
self:_cleanup_minimized_win()
|
||||||
self:show()
|
self:show()
|
||||||
|
|
@ -115,14 +115,14 @@ function Progress:_render()
|
||||||
util.render_text(
|
util.render_text(
|
||||||
self.bufnr,
|
self.bufnr,
|
||||||
self.lines,
|
self.lines,
|
||||||
{ winid = self.winid, actions = { '[M]inimize', '[C]ancel' } }
|
{ winid = self.winid, actions = { "[M]inimize", "[C]ancel" } }
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
if self.min_bufnr and vim.api.nvim_buf_is_valid(self.min_bufnr) then
|
if self.min_bufnr and vim.api.nvim_buf_is_valid(self.min_bufnr) then
|
||||||
util.render_text(
|
util.render_text(
|
||||||
self.min_bufnr,
|
self.min_bufnr,
|
||||||
{ string.format('%sOil: %s', self.spinner, self.count) },
|
{ string.format("%sOil: %s", self.spinner, self.count) },
|
||||||
{ winid = self.min_winid, h_align = 'left' }
|
{ winid = self.min_winid, h_align = "left" }
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -136,7 +136,7 @@ function Progress:_reposition()
|
||||||
end
|
end
|
||||||
local width, height = layout.calculate_dims(min_width, 10, config.progress)
|
local width, height = layout.calculate_dims(min_width, 10, config.progress)
|
||||||
vim.api.nvim_win_set_config(self.winid, {
|
vim.api.nvim_win_set_config(self.winid, {
|
||||||
relative = 'editor',
|
relative = "editor",
|
||||||
width = width,
|
width = width,
|
||||||
height = height,
|
height = height,
|
||||||
row = math.floor((layout.get_editor_height() - height) / 2),
|
row = math.floor((layout.get_editor_height() - height) / 2),
|
||||||
|
|
@ -174,22 +174,22 @@ function Progress:minimize()
|
||||||
end
|
end
|
||||||
self:_cleanup_main_win()
|
self:_cleanup_main_win()
|
||||||
local bufnr = vim.api.nvim_create_buf(false, true)
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
vim.bo[bufnr].bufhidden = 'wipe'
|
vim.bo[bufnr].bufhidden = "wipe"
|
||||||
local winid = vim.api.nvim_open_win(bufnr, false, {
|
local winid = vim.api.nvim_open_win(bufnr, false, {
|
||||||
relative = 'editor',
|
relative = "editor",
|
||||||
width = 16,
|
width = 16,
|
||||||
height = 1,
|
height = 1,
|
||||||
anchor = 'SE',
|
anchor = "SE",
|
||||||
row = layout.get_editor_height(),
|
row = layout.get_editor_height(),
|
||||||
col = layout.get_editor_width(),
|
col = layout.get_editor_width(),
|
||||||
zindex = 152, -- render on top of the floating window title
|
zindex = 152, -- render on top of the floating window title
|
||||||
style = 'minimal',
|
style = "minimal",
|
||||||
border = config.progress.minimized_border,
|
border = config.progress.minimized_border,
|
||||||
})
|
})
|
||||||
self.min_bufnr = bufnr
|
self.min_bufnr = bufnr
|
||||||
self.min_winid = winid
|
self.min_winid = winid
|
||||||
self:_render()
|
self:_render()
|
||||||
vim.notify_once('Restore progress window with :Oil --progress')
|
vim.notify_once("Restore progress window with :Oil --progress")
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param action oil.Action
|
---@param action oil.Action
|
||||||
|
|
@ -198,14 +198,14 @@ end
|
||||||
function Progress:set_action(action, idx, total)
|
function Progress:set_action(action, idx, total)
|
||||||
local adapter = util.get_adapter_for_action(action)
|
local adapter = util.get_adapter_for_action(action)
|
||||||
local change_line
|
local change_line
|
||||||
if action.type == 'change' then
|
if action.type == "change" then
|
||||||
---@cast action oil.ChangeAction
|
---@cast action oil.ChangeAction
|
||||||
change_line = columns.render_change_action(adapter, action)
|
change_line = columns.render_change_action(adapter, action)
|
||||||
else
|
else
|
||||||
change_line = adapter.render_action(action)
|
change_line = adapter.render_action(action)
|
||||||
end
|
end
|
||||||
self.lines[1] = change_line
|
self.lines[1] = change_line
|
||||||
self.count = string.format('%d/%d', idx, total)
|
self.count = string.format("%d/%d", idx, total)
|
||||||
self:_reposition()
|
self:_reposition()
|
||||||
self:_render()
|
self:_render()
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
|
|
||||||
---@class (exact) oil.Trie
|
---@class (exact) oil.Trie
|
||||||
---@field new fun(): oil.Trie
|
---@field new fun(): oil.Trie
|
||||||
|
|
@ -20,7 +20,7 @@ end
|
||||||
function Trie:_url_to_path_pieces(url)
|
function Trie:_url_to_path_pieces(url)
|
||||||
local scheme, path = util.parse_url(url)
|
local scheme, path = util.parse_url(url)
|
||||||
assert(path)
|
assert(path)
|
||||||
local pieces = vim.split(path, '/')
|
local pieces = vim.split(path, "/")
|
||||||
table.insert(pieces, 1, scheme)
|
table.insert(pieces, 1, scheme)
|
||||||
return pieces
|
return pieces
|
||||||
end
|
end
|
||||||
|
|
@ -75,7 +75,7 @@ function Trie:remove(path_pieces, value)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
error('Value not present in trie: ' .. vim.inspect(value))
|
error("Value not present in trie: " .. vim.inspect(value))
|
||||||
end
|
end
|
||||||
|
|
||||||
---Add the first action that affects a parent path of the url
|
---Add the first action that affects a parent path of the url
|
||||||
|
|
|
||||||
|
|
@ -3,26 +3,26 @@ local M = {}
|
||||||
---@param path string
|
---@param path string
|
||||||
---@return string
|
---@return string
|
||||||
M.parent = function(path)
|
M.parent = function(path)
|
||||||
if path == '/' then
|
if path == "/" then
|
||||||
return '/'
|
return "/"
|
||||||
elseif path == '' then
|
elseif path == "" then
|
||||||
return ''
|
return ""
|
||||||
elseif vim.endswith(path, '/') then
|
elseif vim.endswith(path, "/") then
|
||||||
return path:match('^(.*/)[^/]*/$') or ''
|
return path:match("^(.*/)[^/]*/$") or ""
|
||||||
else
|
else
|
||||||
return path:match('^(.*/)[^/]*$') or ''
|
return path:match("^(.*/)[^/]*$") or ""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param path string
|
---@param path string
|
||||||
---@return nil|string
|
---@return nil|string
|
||||||
M.basename = function(path)
|
M.basename = function(path)
|
||||||
if path == '/' or path == '' then
|
if path == "/" or path == "" then
|
||||||
return
|
return
|
||||||
elseif vim.endswith(path, '/') then
|
elseif vim.endswith(path, "/") then
|
||||||
return path:match('^.*/([^/]*)/$')
|
return path:match("^.*/([^/]*)/$")
|
||||||
else
|
else
|
||||||
return path:match('^.*/([^/]*)$')
|
return path:match("^.*/([^/]*)$")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,11 @@ end
|
||||||
|
|
||||||
---@return string
|
---@return string
|
||||||
function Ringbuf:as_str()
|
function Ringbuf:as_str()
|
||||||
local postfix = ''
|
local postfix = ""
|
||||||
for i = 1, self.tail, 1 do
|
for i = 1, self.tail, 1 do
|
||||||
postfix = postfix .. self.buf[i]
|
postfix = postfix .. self.buf[i]
|
||||||
end
|
end
|
||||||
local prefix = ''
|
local prefix = ""
|
||||||
for i = self.tail + 1, #self.buf, 1 do
|
for i = self.tail + 1, #self.buf, 1 do
|
||||||
prefix = prefix .. self.buf[i]
|
prefix = prefix .. self.buf[i]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ M.run = function(cmd, opts, callback)
|
||||||
local stderr = {}
|
local stderr = {}
|
||||||
local jid = vim.fn.jobstart(
|
local jid = vim.fn.jobstart(
|
||||||
cmd,
|
cmd,
|
||||||
vim.tbl_deep_extend('keep', opts, {
|
vim.tbl_deep_extend("keep", opts, {
|
||||||
stdout_buffered = true,
|
stdout_buffered = true,
|
||||||
stderr_buffered = true,
|
stderr_buffered = true,
|
||||||
on_stdout = function(j, output)
|
on_stdout = function(j, output)
|
||||||
|
|
@ -22,19 +22,19 @@ M.run = function(cmd, opts, callback)
|
||||||
if code == 0 then
|
if code == 0 then
|
||||||
callback(nil, stdout)
|
callback(nil, stdout)
|
||||||
else
|
else
|
||||||
local err = table.concat(stderr, '\n')
|
local err = table.concat(stderr, "\n")
|
||||||
if err == '' then
|
if err == "" then
|
||||||
err = 'Unknown error'
|
err = "Unknown error"
|
||||||
end
|
end
|
||||||
local cmd_str = type(cmd) == 'string' and cmd or table.concat(cmd, ' ')
|
local cmd_str = type(cmd) == "string" and cmd or table.concat(cmd, " ")
|
||||||
callback(string.format("Error running command '%s'\n%s", cmd_str, err))
|
callback(string.format("Error running command '%s'\n%s", cmd_str, err))
|
||||||
end
|
end
|
||||||
end),
|
end),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
local exe
|
local exe
|
||||||
if type(cmd) == 'string' then
|
if type(cmd) == "string" then
|
||||||
exe = vim.split(cmd, '%s+')[1]
|
exe = vim.split(cmd, "%s+")[1]
|
||||||
else
|
else
|
||||||
exe = cmd[1]
|
exe = cmd[1]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
363
lua/oil/util.lua
363
lua/oil/util.lua
|
|
@ -1,5 +1,5 @@
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ local FIELD_META = constants.FIELD_META
|
||||||
---@return nil|string
|
---@return nil|string
|
||||||
---@return nil|string
|
---@return nil|string
|
||||||
M.parse_url = function(url)
|
M.parse_url = function(url)
|
||||||
return url:match('^(.*://)(.*)$')
|
return url:match("^(.*://)(.*)$")
|
||||||
end
|
end
|
||||||
|
|
||||||
---Escapes a filename for use in :edit
|
---Escapes a filename for use in :edit
|
||||||
|
|
@ -26,51 +26,51 @@ M.escape_filename = function(filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
local _url_escape_to_char = {
|
local _url_escape_to_char = {
|
||||||
['20'] = ' ',
|
["20"] = " ",
|
||||||
['22'] = '“',
|
["22"] = "“",
|
||||||
['23'] = '#',
|
["23"] = "#",
|
||||||
['24'] = '$',
|
["24"] = "$",
|
||||||
['25'] = '%',
|
["25"] = "%",
|
||||||
['26'] = '&',
|
["26"] = "&",
|
||||||
['27'] = '‘',
|
["27"] = "‘",
|
||||||
['2B'] = '+',
|
["2B"] = "+",
|
||||||
['2C'] = ',',
|
["2C"] = ",",
|
||||||
['2F'] = '/',
|
["2F"] = "/",
|
||||||
['3A'] = ':',
|
["3A"] = ":",
|
||||||
['3B'] = ';',
|
["3B"] = ";",
|
||||||
['3C'] = '<',
|
["3C"] = "<",
|
||||||
['3D'] = '=',
|
["3D"] = "=",
|
||||||
['3E'] = '>',
|
["3E"] = ">",
|
||||||
['3F'] = '?',
|
["3F"] = "?",
|
||||||
['40'] = '@',
|
["40"] = "@",
|
||||||
['5B'] = '[',
|
["5B"] = "[",
|
||||||
['5C'] = '\\',
|
["5C"] = "\\",
|
||||||
['5D'] = ']',
|
["5D"] = "]",
|
||||||
['5E'] = '^',
|
["5E"] = "^",
|
||||||
['60'] = '`',
|
["60"] = "`",
|
||||||
['7B'] = '{',
|
["7B"] = "{",
|
||||||
['7C'] = '|',
|
["7C"] = "|",
|
||||||
['7D'] = '}',
|
["7D"] = "}",
|
||||||
['7E'] = '~',
|
["7E"] = "~",
|
||||||
}
|
}
|
||||||
local _char_to_url_escape = {}
|
local _char_to_url_escape = {}
|
||||||
for k, v in pairs(_url_escape_to_char) do
|
for k, v in pairs(_url_escape_to_char) do
|
||||||
_char_to_url_escape[v] = '%' .. k
|
_char_to_url_escape[v] = "%" .. k
|
||||||
end
|
end
|
||||||
-- TODO this uri escape handling is very incomplete
|
-- TODO this uri escape handling is very incomplete
|
||||||
|
|
||||||
---@param string string
|
---@param string string
|
||||||
---@return string
|
---@return string
|
||||||
M.url_escape = function(string)
|
M.url_escape = function(string)
|
||||||
return (string:gsub('.', _char_to_url_escape))
|
return (string:gsub(".", _char_to_url_escape))
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param string string
|
---@param string string
|
||||||
---@return string
|
---@return string
|
||||||
M.url_unescape = function(string)
|
M.url_unescape = function(string)
|
||||||
return (
|
return (
|
||||||
string:gsub('%%([0-9A-Fa-f][0-9A-Fa-f])', function(seq)
|
string:gsub("%%([0-9A-Fa-f][0-9A-Fa-f])", function(seq)
|
||||||
return _url_escape_to_char[seq:upper()] or ('%' .. seq)
|
return _url_escape_to_char[seq:upper()] or ("%" .. seq)
|
||||||
end)
|
end)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
@ -105,14 +105,14 @@ M.pad_align = function(text, width, align)
|
||||||
return text, 0
|
return text, 0
|
||||||
end
|
end
|
||||||
|
|
||||||
if align == 'right' then
|
if align == "right" then
|
||||||
return string.rep(' ', total_pad) .. text, total_pad
|
return string.rep(" ", total_pad) .. text, total_pad
|
||||||
elseif align == 'center' then
|
elseif align == "center" then
|
||||||
local left_pad = math.floor(total_pad / 2)
|
local left_pad = math.floor(total_pad / 2)
|
||||||
local right_pad = total_pad - left_pad
|
local right_pad = total_pad - left_pad
|
||||||
return string.rep(' ', left_pad) .. text .. string.rep(' ', right_pad), left_pad
|
return string.rep(" ", left_pad) .. text .. string.rep(" ", right_pad), left_pad
|
||||||
else
|
else
|
||||||
return text .. string.rep(' ', total_pad), 0
|
return text .. string.rep(" ", total_pad), 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -150,7 +150,7 @@ end
|
||||||
---@param dest_buf_name string
|
---@param dest_buf_name string
|
||||||
---@return boolean True if the buffer was replaced instead of renamed
|
---@return boolean True if the buffer was replaced instead of renamed
|
||||||
M.rename_buffer = function(src_bufnr, dest_buf_name)
|
M.rename_buffer = function(src_bufnr, dest_buf_name)
|
||||||
if type(src_bufnr) == 'string' then
|
if type(src_bufnr) == "string" then
|
||||||
src_bufnr = vim.fn.bufadd(src_bufnr)
|
src_bufnr = vim.fn.bufadd(src_bufnr)
|
||||||
if not vim.api.nvim_buf_is_loaded(src_bufnr) then
|
if not vim.api.nvim_buf_is_loaded(src_bufnr) then
|
||||||
vim.api.nvim_buf_delete(src_bufnr, {})
|
vim.api.nvim_buf_delete(src_bufnr, {})
|
||||||
|
|
@ -164,7 +164,7 @@ M.rename_buffer = function(src_bufnr, dest_buf_name)
|
||||||
-- think that the new buffer conflicts with the file next time it tries to save.
|
-- think that the new buffer conflicts with the file next time it tries to save.
|
||||||
if not vim.loop.fs_stat(dest_buf_name) then
|
if not vim.loop.fs_stat(dest_buf_name) then
|
||||||
---@diagnostic disable-next-line: param-type-mismatch
|
---@diagnostic disable-next-line: param-type-mismatch
|
||||||
local altbuf = vim.fn.bufnr('#')
|
local altbuf = vim.fn.bufnr("#")
|
||||||
-- This will fail if the dest buf name already exists
|
-- This will fail if the dest buf name already exists
|
||||||
local ok = pcall(vim.api.nvim_buf_set_name, src_bufnr, dest_buf_name)
|
local ok = pcall(vim.api.nvim_buf_set_name, src_bufnr, dest_buf_name)
|
||||||
if ok then
|
if ok then
|
||||||
|
|
@ -173,7 +173,7 @@ M.rename_buffer = function(src_bufnr, dest_buf_name)
|
||||||
-- where Neovim doesn't allow buffer modifications.
|
-- where Neovim doesn't allow buffer modifications.
|
||||||
pcall(vim.api.nvim_buf_delete, vim.fn.bufadd(bufname), {})
|
pcall(vim.api.nvim_buf_delete, vim.fn.bufadd(bufname), {})
|
||||||
if altbuf and vim.api.nvim_buf_is_valid(altbuf) then
|
if altbuf and vim.api.nvim_buf_is_valid(altbuf) then
|
||||||
vim.fn.setreg('#', altbuf)
|
vim.fn.setreg("#", altbuf)
|
||||||
end
|
end
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|
@ -229,7 +229,6 @@ end
|
||||||
M.cb_collect = function(count, cb)
|
M.cb_collect = function(count, cb)
|
||||||
return function(err)
|
return function(err)
|
||||||
if err then
|
if err then
|
||||||
-- selene: allow(mismatched_arg_count)
|
|
||||||
cb(err)
|
cb(err)
|
||||||
cb = function() end
|
cb = function() end
|
||||||
else
|
else
|
||||||
|
|
@ -244,9 +243,9 @@ end
|
||||||
---@param url string
|
---@param url string
|
||||||
---@return string[]
|
---@return string[]
|
||||||
local function get_possible_buffer_names_from_url(url)
|
local function get_possible_buffer_names_from_url(url)
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local scheme, path = M.parse_url(url)
|
local scheme, path = M.parse_url(url)
|
||||||
if config.adapters[scheme] == 'files' then
|
if config.adapters[scheme] == "files" then
|
||||||
assert(path)
|
assert(path)
|
||||||
return { fs.posix_to_os_path(path) }
|
return { fs.posix_to_os_path(path) }
|
||||||
end
|
end
|
||||||
|
|
@ -259,7 +258,7 @@ end
|
||||||
M.update_moved_buffers = function(entry_type, src_url, dest_url)
|
M.update_moved_buffers = function(entry_type, src_url, dest_url)
|
||||||
local src_buf_names = get_possible_buffer_names_from_url(src_url)
|
local src_buf_names = get_possible_buffer_names_from_url(src_url)
|
||||||
local dest_buf_name = get_possible_buffer_names_from_url(dest_url)[1]
|
local dest_buf_name = get_possible_buffer_names_from_url(dest_url)[1]
|
||||||
if entry_type ~= 'directory' then
|
if entry_type ~= "directory" then
|
||||||
for _, src_buf_name in ipairs(src_buf_names) do
|
for _, src_buf_name in ipairs(src_buf_names) do
|
||||||
M.rename_buffer(src_buf_name, dest_buf_name)
|
M.rename_buffer(src_buf_name, dest_buf_name)
|
||||||
end
|
end
|
||||||
|
|
@ -273,13 +272,13 @@ M.update_moved_buffers = function(entry_type, src_url, dest_url)
|
||||||
if vim.startswith(bufname, src_url) then
|
if vim.startswith(bufname, src_url) then
|
||||||
-- Handle oil directory buffers
|
-- Handle oil directory buffers
|
||||||
vim.api.nvim_buf_set_name(bufnr, dest_url .. bufname:sub(src_url:len() + 1))
|
vim.api.nvim_buf_set_name(bufnr, dest_url .. bufname:sub(src_url:len() + 1))
|
||||||
elseif bufname ~= '' and vim.bo[bufnr].buftype == '' then
|
elseif bufname ~= "" and vim.bo[bufnr].buftype == "" then
|
||||||
-- Handle regular buffers
|
-- Handle regular buffers
|
||||||
local scheme = M.parse_url(bufname)
|
local scheme = M.parse_url(bufname)
|
||||||
|
|
||||||
-- If the buffer is a local file, make sure we're using the absolute path
|
-- If the buffer is a local file, make sure we're using the absolute path
|
||||||
if not scheme then
|
if not scheme then
|
||||||
bufname = vim.fn.fnamemodify(bufname, ':p')
|
bufname = vim.fn.fnamemodify(bufname, ":p")
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, src_buf_name in ipairs(src_buf_names) do
|
for _, src_buf_name in ipairs(src_buf_names) do
|
||||||
|
|
@ -297,13 +296,13 @@ end
|
||||||
---@return string
|
---@return string
|
||||||
---@return table|nil
|
---@return table|nil
|
||||||
M.split_config = function(name_or_config)
|
M.split_config = function(name_or_config)
|
||||||
if type(name_or_config) == 'string' then
|
if type(name_or_config) == "string" then
|
||||||
return name_or_config, nil
|
return name_or_config, nil
|
||||||
else
|
else
|
||||||
if not name_or_config[1] and name_or_config['1'] then
|
if not name_or_config[1] and name_or_config["1"] then
|
||||||
-- This was likely loaded from json, so the first element got coerced to a string key
|
-- This was likely loaded from json, so the first element got coerced to a string key
|
||||||
name_or_config[1] = name_or_config['1']
|
name_or_config[1] = name_or_config["1"]
|
||||||
name_or_config['1'] = nil
|
name_or_config["1"] = nil
|
||||||
end
|
end
|
||||||
return name_or_config[1], name_or_config
|
return name_or_config[1], name_or_config
|
||||||
end
|
end
|
||||||
|
|
@ -325,7 +324,7 @@ M.render_table = function(lines, col_width, col_align)
|
||||||
local pieces = {}
|
local pieces = {}
|
||||||
for i, chunk in ipairs(cols) do
|
for i, chunk in ipairs(cols) do
|
||||||
local text, hl
|
local text, hl
|
||||||
if type(chunk) == 'table' then
|
if type(chunk) == "table" then
|
||||||
text = chunk[1]
|
text = chunk[1]
|
||||||
hl = chunk[2]
|
hl = chunk[2]
|
||||||
else
|
else
|
||||||
|
|
@ -334,11 +333,11 @@ M.render_table = function(lines, col_width, col_align)
|
||||||
|
|
||||||
local unpadded_len = text:len()
|
local unpadded_len = text:len()
|
||||||
local padding
|
local padding
|
||||||
text, padding = M.pad_align(text, col_width[i], col_align[i] or 'left')
|
text, padding = M.pad_align(text, col_width[i], col_align[i] or "left")
|
||||||
|
|
||||||
table.insert(pieces, text)
|
table.insert(pieces, text)
|
||||||
if hl then
|
if hl then
|
||||||
if type(hl) == 'table' then
|
if type(hl) == "table" then
|
||||||
-- hl has the form { [1]: hl_name, [2]: col_start, [3]: col_end }[]
|
-- hl has the form { [1]: hl_name, [2]: col_start, [3]: col_end }[]
|
||||||
-- Notice that col_start and col_end are relative position inside
|
-- Notice that col_start and col_end are relative position inside
|
||||||
-- that col, so we need to add the offset to them
|
-- that col, so we need to add the offset to them
|
||||||
|
|
@ -356,7 +355,7 @@ M.render_table = function(lines, col_width, col_align)
|
||||||
end
|
end
|
||||||
col = col + text:len() + 1
|
col = col + text:len() + 1
|
||||||
end
|
end
|
||||||
table.insert(str_lines, table.concat(pieces, ' '))
|
table.insert(str_lines, table.concat(pieces, " "))
|
||||||
end
|
end
|
||||||
return str_lines, highlights
|
return str_lines, highlights
|
||||||
end
|
end
|
||||||
|
|
@ -364,7 +363,7 @@ end
|
||||||
---@param bufnr integer
|
---@param bufnr integer
|
||||||
---@param highlights any[][] List of highlights {group, lnum, col_start, col_end}
|
---@param highlights any[][] List of highlights {group, lnum, col_start, col_end}
|
||||||
M.set_highlights = function(bufnr, highlights)
|
M.set_highlights = function(bufnr, highlights)
|
||||||
local ns = vim.api.nvim_create_namespace('Oil')
|
local ns = vim.api.nvim_create_namespace("Oil")
|
||||||
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
|
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
|
||||||
for _, hl in ipairs(highlights) do
|
for _, hl in ipairs(highlights) do
|
||||||
local group, line, col_start, col_end = unpack(hl)
|
local group, line, col_start, col_end = unpack(hl)
|
||||||
|
|
@ -380,12 +379,12 @@ end
|
||||||
---@param os_slash? boolean use os filesystem slash instead of posix slash
|
---@param os_slash? boolean use os filesystem slash instead of posix slash
|
||||||
---@return string
|
---@return string
|
||||||
M.addslash = function(path, os_slash)
|
M.addslash = function(path, os_slash)
|
||||||
local slash = '/'
|
local slash = "/"
|
||||||
if os_slash and require('oil.fs').is_windows then
|
if os_slash and require("oil.fs").is_windows then
|
||||||
slash = '\\'
|
slash = "\\"
|
||||||
end
|
end
|
||||||
|
|
||||||
local endslash = path:match(slash .. '$')
|
local endslash = path:match(slash .. "$")
|
||||||
if not endslash then
|
if not endslash then
|
||||||
return path .. slash
|
return path .. slash
|
||||||
else
|
else
|
||||||
|
|
@ -396,7 +395,7 @@ end
|
||||||
---@param winid nil|integer
|
---@param winid nil|integer
|
||||||
---@return boolean
|
---@return boolean
|
||||||
M.is_floating_win = function(winid)
|
M.is_floating_win = function(winid)
|
||||||
return vim.api.nvim_win_get_config(winid or 0).relative ~= ''
|
return vim.api.nvim_win_get_config(winid or 0).relative ~= ""
|
||||||
end
|
end
|
||||||
|
|
||||||
---Recalculate the window title for the current buffer
|
---Recalculate the window title for the current buffer
|
||||||
|
|
@ -411,10 +410,10 @@ M.get_title = function(winid)
|
||||||
local title = vim.api.nvim_buf_get_name(src_buf)
|
local title = vim.api.nvim_buf_get_name(src_buf)
|
||||||
local scheme, path = M.parse_url(title)
|
local scheme, path = M.parse_url(title)
|
||||||
|
|
||||||
if config.adapters[scheme] == 'files' then
|
if config.adapters[scheme] == "files" then
|
||||||
assert(path)
|
assert(path)
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
title = vim.fn.fnamemodify(fs.posix_to_os_path(path), ':~')
|
title = vim.fn.fnamemodify(fs.posix_to_os_path(path), ":~")
|
||||||
end
|
end
|
||||||
return title
|
return title
|
||||||
end
|
end
|
||||||
|
|
@ -422,7 +421,7 @@ end
|
||||||
local winid_map = {}
|
local winid_map = {}
|
||||||
M.add_title_to_win = function(winid, opts)
|
M.add_title_to_win = function(winid, opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
opts.align = opts.align or 'left'
|
opts.align = opts.align or "left"
|
||||||
if not vim.api.nvim_win_is_valid(winid) then
|
if not vim.api.nvim_win_is_valid(winid) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -439,18 +438,18 @@ M.add_title_to_win = function(winid, opts)
|
||||||
else
|
else
|
||||||
bufnr = vim.api.nvim_create_buf(false, true)
|
bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
local col = 1
|
local col = 1
|
||||||
if opts.align == 'center' then
|
if opts.align == "center" then
|
||||||
col = math.floor((vim.api.nvim_win_get_width(winid) - width) / 2)
|
col = math.floor((vim.api.nvim_win_get_width(winid) - width) / 2)
|
||||||
elseif opts.align == 'right' then
|
elseif opts.align == "right" then
|
||||||
col = vim.api.nvim_win_get_width(winid) - 1 - width
|
col = vim.api.nvim_win_get_width(winid) - 1 - width
|
||||||
elseif opts.align ~= 'left' then
|
elseif opts.align ~= "left" then
|
||||||
vim.notify(
|
vim.notify(
|
||||||
string.format("Unknown oil window title alignment: '%s'", opts.align),
|
string.format("Unknown oil window title alignment: '%s'", opts.align),
|
||||||
vim.log.levels.ERROR
|
vim.log.levels.ERROR
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
title_winid = vim.api.nvim_open_win(bufnr, false, {
|
title_winid = vim.api.nvim_open_win(bufnr, false, {
|
||||||
relative = 'win',
|
relative = "win",
|
||||||
win = winid,
|
win = winid,
|
||||||
width = width,
|
width = width,
|
||||||
height = 1,
|
height = 1,
|
||||||
|
|
@ -458,20 +457,20 @@ M.add_title_to_win = function(winid, opts)
|
||||||
col = col,
|
col = col,
|
||||||
focusable = false,
|
focusable = false,
|
||||||
zindex = 151,
|
zindex = 151,
|
||||||
style = 'minimal',
|
style = "minimal",
|
||||||
noautocmd = true,
|
noautocmd = true,
|
||||||
})
|
})
|
||||||
winid_map[winid] = title_winid
|
winid_map[winid] = title_winid
|
||||||
vim.api.nvim_set_option_value(
|
vim.api.nvim_set_option_value(
|
||||||
'winblend',
|
"winblend",
|
||||||
vim.wo[winid].winblend,
|
vim.wo[winid].winblend,
|
||||||
{ scope = 'local', win = title_winid }
|
{ scope = "local", win = title_winid }
|
||||||
)
|
)
|
||||||
vim.bo[bufnr].bufhidden = 'wipe'
|
vim.bo[bufnr].bufhidden = "wipe"
|
||||||
|
|
||||||
local update_autocmd = vim.api.nvim_create_autocmd('BufWinEnter', {
|
local update_autocmd = vim.api.nvim_create_autocmd("BufWinEnter", {
|
||||||
desc = 'Update oil floating window title when buffer changes',
|
desc = "Update oil floating window title when buffer changes",
|
||||||
pattern = '*',
|
pattern = "*",
|
||||||
callback = function(params)
|
callback = function(params)
|
||||||
local winbuf = params.buf
|
local winbuf = params.buf
|
||||||
if vim.api.nvim_win_get_buf(winid) ~= winbuf then
|
if vim.api.nvim_win_get_buf(winid) ~= winbuf then
|
||||||
|
|
@ -480,17 +479,17 @@ M.add_title_to_win = function(winid, opts)
|
||||||
local new_title = M.get_title(winid)
|
local new_title = M.get_title(winid)
|
||||||
local new_width =
|
local new_width =
|
||||||
math.min(vim.api.nvim_win_get_width(winid) - 4, 2 + vim.api.nvim_strwidth(new_title))
|
math.min(vim.api.nvim_win_get_width(winid) - 4, 2 + vim.api.nvim_strwidth(new_title))
|
||||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, { ' ' .. new_title .. ' ' })
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, { " " .. new_title .. " " })
|
||||||
vim.bo[bufnr].modified = false
|
vim.bo[bufnr].modified = false
|
||||||
vim.api.nvim_win_set_width(title_winid, new_width)
|
vim.api.nvim_win_set_width(title_winid, new_width)
|
||||||
local new_col = 1
|
local new_col = 1
|
||||||
if opts.align == 'center' then
|
if opts.align == "center" then
|
||||||
new_col = math.floor((vim.api.nvim_win_get_width(winid) - new_width) / 2)
|
new_col = math.floor((vim.api.nvim_win_get_width(winid) - new_width) / 2)
|
||||||
elseif opts.align == 'right' then
|
elseif opts.align == "right" then
|
||||||
new_col = vim.api.nvim_win_get_width(winid) - 1 - new_width
|
new_col = vim.api.nvim_win_get_width(winid) - 1 - new_width
|
||||||
end
|
end
|
||||||
vim.api.nvim_win_set_config(title_winid, {
|
vim.api.nvim_win_set_config(title_winid, {
|
||||||
relative = 'win',
|
relative = "win",
|
||||||
win = winid,
|
win = winid,
|
||||||
row = -1,
|
row = -1,
|
||||||
col = new_col,
|
col = new_col,
|
||||||
|
|
@ -499,8 +498,8 @@ M.add_title_to_win = function(winid, opts)
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
vim.api.nvim_create_autocmd('WinClosed', {
|
vim.api.nvim_create_autocmd("WinClosed", {
|
||||||
desc = 'Close oil floating window title when floating window closes',
|
desc = "Close oil floating window title when floating window closes",
|
||||||
pattern = tostring(winid),
|
pattern = tostring(winid),
|
||||||
callback = function()
|
callback = function()
|
||||||
if title_winid and vim.api.nvim_win_is_valid(title_winid) then
|
if title_winid and vim.api.nvim_win_is_valid(title_winid) then
|
||||||
|
|
@ -513,12 +512,12 @@ M.add_title_to_win = function(winid, opts)
|
||||||
nested = true,
|
nested = true,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, { ' ' .. title .. ' ' })
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, { " " .. title .. " " })
|
||||||
vim.bo[bufnr].modified = false
|
vim.bo[bufnr].modified = false
|
||||||
vim.api.nvim_set_option_value(
|
vim.api.nvim_set_option_value(
|
||||||
'winhighlight',
|
"winhighlight",
|
||||||
'Normal:FloatTitle,NormalFloat:FloatTitle',
|
"Normal:FloatTitle,NormalFloat:FloatTitle",
|
||||||
{ scope = 'local', win = title_winid }
|
{ scope = "local", win = title_winid }
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -543,7 +542,7 @@ M.get_adapter_for_action = function(action)
|
||||||
else
|
else
|
||||||
error(
|
error(
|
||||||
string.format(
|
string.format(
|
||||||
'Cannot copy files from %s -> %s; no cross-adapter transfer method found',
|
"Cannot copy files from %s -> %s; no cross-adapter transfer method found",
|
||||||
action.src_url,
|
action.src_url,
|
||||||
action.dest_url
|
action.dest_url
|
||||||
)
|
)
|
||||||
|
|
@ -560,12 +559,12 @@ end
|
||||||
---@return string
|
---@return string
|
||||||
---@return integer
|
---@return integer
|
||||||
M.h_align = function(str, align, width)
|
M.h_align = function(str, align, width)
|
||||||
if align == 'center' then
|
if align == "center" then
|
||||||
local padding = math.floor((width - vim.api.nvim_strwidth(str)) / 2)
|
local padding = math.floor((width - vim.api.nvim_strwidth(str)) / 2)
|
||||||
return string.rep(' ', padding) .. str, padding
|
return string.rep(" ", padding) .. str, padding
|
||||||
elseif align == 'right' then
|
elseif align == "right" then
|
||||||
local padding = width - vim.api.nvim_strwidth(str)
|
local padding = width - vim.api.nvim_strwidth(str)
|
||||||
return string.rep(' ', padding) .. str, padding
|
return string.rep(" ", padding) .. str, padding
|
||||||
else
|
else
|
||||||
return str, 0
|
return str, 0
|
||||||
end
|
end
|
||||||
|
|
@ -579,15 +578,15 @@ end
|
||||||
--- actions nil|string[]
|
--- actions nil|string[]
|
||||||
--- winid nil|integer
|
--- winid nil|integer
|
||||||
M.render_text = function(bufnr, text, opts)
|
M.render_text = function(bufnr, text, opts)
|
||||||
opts = vim.tbl_deep_extend('keep', opts or {}, {
|
opts = vim.tbl_deep_extend("keep", opts or {}, {
|
||||||
h_align = 'center',
|
h_align = "center",
|
||||||
v_align = 'center',
|
v_align = "center",
|
||||||
})
|
})
|
||||||
---@cast opts -nil
|
---@cast opts -nil
|
||||||
if not vim.api.nvim_buf_is_valid(bufnr) then
|
if not vim.api.nvim_buf_is_valid(bufnr) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if type(text) == 'string' then
|
if type(text) == "string" then
|
||||||
text = { text }
|
text = { text }
|
||||||
end
|
end
|
||||||
local height = 40
|
local height = 40
|
||||||
|
|
@ -609,17 +608,17 @@ M.render_text = function(bufnr, text, opts)
|
||||||
local lines = {}
|
local lines = {}
|
||||||
|
|
||||||
-- Add vertical spacing for vertical alignment
|
-- Add vertical spacing for vertical alignment
|
||||||
if opts.v_align == 'center' then
|
if opts.v_align == "center" then
|
||||||
for _ = 1, (height / 2) - (#text / 2) do
|
for _ = 1, (height / 2) - (#text / 2) do
|
||||||
table.insert(lines, '')
|
table.insert(lines, "")
|
||||||
end
|
end
|
||||||
elseif opts.v_align == 'bottom' then
|
elseif opts.v_align == "bottom" then
|
||||||
local num_lines = height
|
local num_lines = height
|
||||||
if opts.actions then
|
if opts.actions then
|
||||||
num_lines = num_lines - 2
|
num_lines = num_lines - 2
|
||||||
end
|
end
|
||||||
while #lines + #text < num_lines do
|
while #lines + #text < num_lines do
|
||||||
table.insert(lines, '')
|
table.insert(lines, "")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -633,12 +632,12 @@ M.render_text = function(bufnr, text, opts)
|
||||||
local highlights = {}
|
local highlights = {}
|
||||||
if opts.actions then
|
if opts.actions then
|
||||||
while #lines < height - 1 do
|
while #lines < height - 1 do
|
||||||
table.insert(lines, '')
|
table.insert(lines, "")
|
||||||
end
|
end
|
||||||
local last_line, padding = M.h_align(table.concat(opts.actions, ' '), 'center', width)
|
local last_line, padding = M.h_align(table.concat(opts.actions, " "), "center", width)
|
||||||
local col = padding
|
local col = padding
|
||||||
for _, action in ipairs(opts.actions) do
|
for _, action in ipairs(opts.actions) do
|
||||||
table.insert(highlights, { 'Special', #lines, col, col + 3 })
|
table.insert(highlights, { "Special", #lines, col, col + 3 })
|
||||||
col = padding + action:len() + 4
|
col = padding + action:len() + 4
|
||||||
end
|
end
|
||||||
table.insert(lines, last_line)
|
table.insert(lines, last_line)
|
||||||
|
|
@ -657,10 +656,10 @@ end
|
||||||
M.run_in_fullscreen_win = function(bufnr, callback)
|
M.run_in_fullscreen_win = function(bufnr, callback)
|
||||||
if not bufnr then
|
if not bufnr then
|
||||||
bufnr = vim.api.nvim_create_buf(false, true)
|
bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
vim.bo[bufnr].bufhidden = 'wipe'
|
vim.bo[bufnr].bufhidden = "wipe"
|
||||||
end
|
end
|
||||||
local winid = vim.api.nvim_open_win(bufnr, false, {
|
local winid = vim.api.nvim_open_win(bufnr, false, {
|
||||||
relative = 'editor',
|
relative = "editor",
|
||||||
width = vim.o.columns,
|
width = vim.o.columns,
|
||||||
height = vim.o.lines,
|
height = vim.o.lines,
|
||||||
row = 0,
|
row = 0,
|
||||||
|
|
@ -668,7 +667,7 @@ M.run_in_fullscreen_win = function(bufnr, callback)
|
||||||
noautocmd = true,
|
noautocmd = true,
|
||||||
})
|
})
|
||||||
local winnr = vim.api.nvim_win_get_number(winid)
|
local winnr = vim.api.nvim_win_get_number(winid)
|
||||||
vim.cmd.wincmd({ count = winnr, args = { 'w' }, mods = { noautocmd = true } })
|
vim.cmd.wincmd({ count = winnr, args = { "w" }, mods = { noautocmd = true } })
|
||||||
callback()
|
callback()
|
||||||
vim.cmd.close({ count = winnr, mods = { noautocmd = true, emsg_silent = true } })
|
vim.cmd.close({ count = winnr, mods = { noautocmd = true, emsg_silent = true } })
|
||||||
end
|
end
|
||||||
|
|
@ -677,9 +676,9 @@ end
|
||||||
---@return boolean
|
---@return boolean
|
||||||
M.is_oil_bufnr = function(bufnr)
|
M.is_oil_bufnr = function(bufnr)
|
||||||
local filetype = vim.bo[bufnr].filetype
|
local filetype = vim.bo[bufnr].filetype
|
||||||
if filetype == 'oil' then
|
if filetype == "oil" then
|
||||||
return true
|
return true
|
||||||
elseif filetype ~= '' then
|
elseif filetype ~= "" then
|
||||||
-- If the filetype is set and is NOT "oil", then it's not an oil buffer
|
-- If the filetype is set and is NOT "oil", then it's not an oil buffer
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
@ -694,10 +693,10 @@ M.hack_around_termopen_autocmd = function(prev_mode)
|
||||||
vim.defer_fn(function()
|
vim.defer_fn(function()
|
||||||
local new_mode = vim.api.nvim_get_mode().mode
|
local new_mode = vim.api.nvim_get_mode().mode
|
||||||
if new_mode ~= prev_mode then
|
if new_mode ~= prev_mode then
|
||||||
if string.find(new_mode, 'i') == 1 then
|
if string.find(new_mode, "i") == 1 then
|
||||||
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<ESC>', true, true, true), 'n', false)
|
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<ESC>", true, true, true), "n", false)
|
||||||
if string.find(prev_mode, 'v') == 1 or string.find(prev_mode, 'V') == 1 then
|
if string.find(prev_mode, "v") == 1 or string.find(prev_mode, "V") == 1 then
|
||||||
vim.cmd.normal({ bang = true, args = { 'gv' } })
|
vim.cmd.normal({ bang = true, args = { "gv" } })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -713,7 +712,7 @@ M.get_preview_win = function(opts)
|
||||||
if
|
if
|
||||||
vim.api.nvim_win_is_valid(winid)
|
vim.api.nvim_win_is_valid(winid)
|
||||||
and vim.wo[winid].previewwindow
|
and vim.wo[winid].previewwindow
|
||||||
and (opts.include_not_owned or vim.w[winid]['oil_preview'])
|
and (opts.include_not_owned or vim.w[winid]["oil_preview"])
|
||||||
then
|
then
|
||||||
return winid
|
return winid
|
||||||
end
|
end
|
||||||
|
|
@ -722,13 +721,13 @@ end
|
||||||
|
|
||||||
---@return fun() restore Function that restores the cursor
|
---@return fun() restore Function that restores the cursor
|
||||||
M.hide_cursor = function()
|
M.hide_cursor = function()
|
||||||
vim.api.nvim_set_hl(0, 'OilPreviewCursor', { nocombine = true, blend = 100 })
|
vim.api.nvim_set_hl(0, "OilPreviewCursor", { nocombine = true, blend = 100 })
|
||||||
local original_guicursor = vim.go.guicursor
|
local original_guicursor = vim.go.guicursor
|
||||||
vim.go.guicursor = 'a:OilPreviewCursor/OilPreviewCursor'
|
vim.go.guicursor = "a:OilPreviewCursor/OilPreviewCursor"
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
-- HACK: see https://github.com/neovim/neovim/issues/21018
|
-- HACK: see https://github.com/neovim/neovim/issues/21018
|
||||||
vim.go.guicursor = 'a:'
|
vim.go.guicursor = "a:"
|
||||||
vim.cmd.redrawstatus()
|
vim.cmd.redrawstatus()
|
||||||
vim.go.guicursor = original_guicursor
|
vim.go.guicursor = original_guicursor
|
||||||
end
|
end
|
||||||
|
|
@ -763,7 +762,7 @@ end
|
||||||
---@param opts {columns?: string[], no_cache?: boolean}
|
---@param opts {columns?: string[], no_cache?: boolean}
|
||||||
---@param callback fun(err: nil|string, entries: nil|oil.InternalEntry[])
|
---@param callback fun(err: nil|string, entries: nil|oil.InternalEntry[])
|
||||||
M.adapter_list_all = function(adapter, url, opts, callback)
|
M.adapter_list_all = function(adapter, url, opts, callback)
|
||||||
local cache = require('oil.cache')
|
local cache = require("oil.cache")
|
||||||
if not opts.no_cache then
|
if not opts.no_cache then
|
||||||
local entries = cache.list_url(url)
|
local entries = cache.list_url(url)
|
||||||
if not vim.tbl_isempty(entries) then
|
if not vim.tbl_isempty(entries) then
|
||||||
|
|
@ -791,23 +790,23 @@ end
|
||||||
---based on the provided options.
|
---based on the provided options.
|
||||||
---@param opts {target?: "qflist"|"loclist", action?: "r"|"a", only_matching_search?: boolean}
|
---@param opts {target?: "qflist"|"loclist", action?: "r"|"a", only_matching_search?: boolean}
|
||||||
M.send_to_quickfix = function(opts)
|
M.send_to_quickfix = function(opts)
|
||||||
if type(opts) ~= 'table' then
|
if type(opts) ~= "table" then
|
||||||
opts = {}
|
opts = {}
|
||||||
end
|
end
|
||||||
local oil = require('oil')
|
local oil = require("oil")
|
||||||
local dir = oil.get_current_dir()
|
local dir = oil.get_current_dir()
|
||||||
if type(dir) ~= 'string' then
|
if type(dir) ~= "string" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local range = M.get_visual_range()
|
local range = M.get_visual_range()
|
||||||
if not range then
|
if not range then
|
||||||
range = { start_lnum = 1, end_lnum = vim.fn.line('$') }
|
range = { start_lnum = 1, end_lnum = vim.fn.line("$") }
|
||||||
end
|
end
|
||||||
local match_all = not opts.only_matching_search
|
local match_all = not opts.only_matching_search
|
||||||
local qf_entries = {}
|
local qf_entries = {}
|
||||||
for i = range.start_lnum, range.end_lnum do
|
for i = range.start_lnum, range.end_lnum do
|
||||||
local entry = oil.get_entry_on_line(0, i)
|
local entry = oil.get_entry_on_line(0, i)
|
||||||
if entry and entry.type == 'file' and (match_all or M.is_matching(entry)) then
|
if entry and entry.type == "file" and (match_all or M.is_matching(entry)) then
|
||||||
local qf_entry = {
|
local qf_entry = {
|
||||||
filename = dir .. entry.name,
|
filename = dir .. entry.name,
|
||||||
lnum = 1,
|
lnum = 1,
|
||||||
|
|
@ -818,26 +817,26 @@ M.send_to_quickfix = function(opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if #qf_entries == 0 then
|
if #qf_entries == 0 then
|
||||||
vim.notify('[oil] No entries found to send to quickfix', vim.log.levels.WARN)
|
vim.notify("[oil] No entries found to send to quickfix", vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
vim.api.nvim_exec_autocmds('QuickFixCmdPre', {})
|
vim.api.nvim_exec_autocmds("QuickFixCmdPre", {})
|
||||||
local qf_title = 'oil files'
|
local qf_title = "oil files"
|
||||||
local action = opts.action == 'a' and 'a' or 'r'
|
local action = opts.action == "a" and "a" or "r"
|
||||||
if opts.target == 'loclist' then
|
if opts.target == "loclist" then
|
||||||
vim.fn.setloclist(0, {}, action, { title = qf_title, items = qf_entries })
|
vim.fn.setloclist(0, {}, action, { title = qf_title, items = qf_entries })
|
||||||
vim.cmd.lopen()
|
vim.cmd.lopen()
|
||||||
else
|
else
|
||||||
vim.fn.setqflist({}, action, { title = qf_title, items = qf_entries })
|
vim.fn.setqflist({}, action, { title = qf_title, items = qf_entries })
|
||||||
vim.cmd.copen()
|
vim.cmd.copen()
|
||||||
end
|
end
|
||||||
vim.api.nvim_exec_autocmds('QuickFixCmdPost', {})
|
vim.api.nvim_exec_autocmds("QuickFixCmdPost", {})
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return boolean
|
---@return boolean
|
||||||
M.is_visual_mode = function()
|
M.is_visual_mode = function()
|
||||||
local mode = vim.api.nvim_get_mode().mode
|
local mode = vim.api.nvim_get_mode().mode
|
||||||
return mode:match('^[vV]') ~= nil
|
return mode:match("^[vV]") ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get the current visual selection range. If not in visual mode, return nil.
|
---Get the current visual selection range. If not in visual mode, return nil.
|
||||||
|
|
@ -848,7 +847,7 @@ M.get_visual_range = function()
|
||||||
end
|
end
|
||||||
-- This is the best way to get the visual selection at the moment
|
-- This is the best way to get the visual selection at the moment
|
||||||
-- https://github.com/neovim/neovim/pull/13896
|
-- https://github.com/neovim/neovim/pull/13896
|
||||||
local _, start_lnum, _, _ = unpack(vim.fn.getpos('v'))
|
local _, start_lnum, _, _ = unpack(vim.fn.getpos("v"))
|
||||||
local _, end_lnum, _, _, _ = unpack(vim.fn.getcurpos())
|
local _, end_lnum, _, _, _ = unpack(vim.fn.getcurpos())
|
||||||
if start_lnum > end_lnum then
|
if start_lnum > end_lnum then
|
||||||
start_lnum, end_lnum = end_lnum, start_lnum
|
start_lnum, end_lnum = end_lnum, start_lnum
|
||||||
|
|
@ -864,7 +863,7 @@ M.is_matching = function(entry)
|
||||||
if search_highlighting_is_off then
|
if search_highlighting_is_off then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
local pattern = vim.fn.getreg('/')
|
local pattern = vim.fn.getreg("/")
|
||||||
local position_of_match = vim.fn.match(entry.name, pattern)
|
local position_of_match = vim.fn.match(entry.name, pattern)
|
||||||
return position_of_match ~= -1
|
return position_of_match ~= -1
|
||||||
end
|
end
|
||||||
|
|
@ -878,8 +877,8 @@ M.run_after_load = function(bufnr, callback)
|
||||||
if vim.b[bufnr].oil_ready then
|
if vim.b[bufnr].oil_ready then
|
||||||
callback()
|
callback()
|
||||||
else
|
else
|
||||||
vim.api.nvim_create_autocmd('User', {
|
vim.api.nvim_create_autocmd("User", {
|
||||||
pattern = 'OilEnter',
|
pattern = "OilEnter",
|
||||||
callback = function(args)
|
callback = function(args)
|
||||||
if args.data.buf == bufnr then
|
if args.data.buf == bufnr then
|
||||||
vim.api.nvim_buf_call(bufnr, callback)
|
vim.api.nvim_buf_call(bufnr, callback)
|
||||||
|
|
@ -893,12 +892,12 @@ end
|
||||||
---@param entry oil.Entry
|
---@param entry oil.Entry
|
||||||
---@return boolean
|
---@return boolean
|
||||||
M.is_directory = function(entry)
|
M.is_directory = function(entry)
|
||||||
local is_directory = entry.type == 'directory'
|
local is_directory = entry.type == "directory"
|
||||||
or (
|
or (
|
||||||
entry.type == 'link'
|
entry.type == "link"
|
||||||
and entry.meta
|
and entry.meta
|
||||||
and entry.meta.link_stat
|
and entry.meta.link_stat
|
||||||
and entry.meta.link_stat.type == 'directory'
|
and entry.meta.link_stat.type == "directory"
|
||||||
)
|
)
|
||||||
return is_directory == true
|
return is_directory == true
|
||||||
end
|
end
|
||||||
|
|
@ -908,7 +907,7 @@ end
|
||||||
---@param entry oil.Entry
|
---@param entry oil.Entry
|
||||||
---@param callback fun(normalized_url: string)
|
---@param callback fun(normalized_url: string)
|
||||||
M.get_edit_path = function(bufnr, entry, callback)
|
M.get_edit_path = function(bufnr, entry, callback)
|
||||||
local pathutil = require('oil.pathutil')
|
local pathutil = require("oil.pathutil")
|
||||||
|
|
||||||
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
||||||
local scheme, dir = M.parse_url(bufname)
|
local scheme, dir = M.parse_url(bufname)
|
||||||
|
|
@ -917,10 +916,10 @@ M.get_edit_path = function(bufnr, entry, callback)
|
||||||
|
|
||||||
local url = scheme .. dir .. entry.name
|
local url = scheme .. dir .. entry.name
|
||||||
if M.is_directory(entry) then
|
if M.is_directory(entry) then
|
||||||
url = url .. '/'
|
url = url .. "/"
|
||||||
end
|
end
|
||||||
|
|
||||||
if entry.name == '..' then
|
if entry.name == ".." then
|
||||||
callback(scheme .. pathutil.parent(dir))
|
callback(scheme .. pathutil.parent(dir))
|
||||||
elseif adapter.get_entry_path then
|
elseif adapter.get_entry_path then
|
||||||
adapter.get_entry_path(url, entry, callback)
|
adapter.get_entry_path(url, entry, callback)
|
||||||
|
|
@ -933,64 +932,35 @@ end
|
||||||
---@return (oil.IconProvider)?
|
---@return (oil.IconProvider)?
|
||||||
M.get_icon_provider = function()
|
M.get_icon_provider = function()
|
||||||
-- prefer mini.icons
|
-- prefer mini.icons
|
||||||
local _, mini_icons = pcall(require, 'mini.icons')
|
local _, mini_icons = pcall(require, "mini.icons")
|
||||||
-- selene: allow(global_usage)
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
if _G.MiniIcons then
|
if _G.MiniIcons then -- `_G.MiniIcons` is a better check to see if the module is setup
|
||||||
return function(type, name, conf, ft)
|
return function(type, name, conf, ft)
|
||||||
if ft then
|
if ft then
|
||||||
return mini_icons.get('filetype', ft)
|
return mini_icons.get("filetype", ft)
|
||||||
end
|
end
|
||||||
return mini_icons.get(type == 'directory' and 'directory' or 'file', name)
|
return mini_icons.get(type == "directory" and "directory" or "file", name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local has_nonicons, nonicons = pcall(require, 'nonicons')
|
-- fallback to `nvim-web-devicons`
|
||||||
if has_nonicons and nonicons.get_icon then
|
local has_devicons, devicons = pcall(require, "nvim-web-devicons")
|
||||||
local has_devicons, devicons = pcall(require, 'nvim-web-devicons')
|
if has_devicons then
|
||||||
if not has_devicons then
|
|
||||||
devicons = nil
|
|
||||||
end
|
|
||||||
return function(type, name, conf, ft)
|
return function(type, name, conf, ft)
|
||||||
if type == 'directory' then
|
if type == "directory" then
|
||||||
local icon, hl = nonicons.get('file-directory-fill')
|
return conf and conf.directory or "", "OilDirIcon"
|
||||||
return icon or (conf and conf.directory or ''), hl or 'OilDirIcon'
|
else
|
||||||
end
|
if ft then
|
||||||
if ft then
|
local ft_icon, ft_hl = devicons.get_icon_by_filetype(ft)
|
||||||
local ft_icon, ft_hl = nonicons.get_icon_by_filetype(ft)
|
if ft_icon and ft_icon ~= "" then
|
||||||
if ft_icon then
|
return ft_icon, ft_hl
|
||||||
return ft_icon, ft_hl or 'OilFileIcon'
|
end
|
||||||
end
|
end
|
||||||
|
local icon, hl = devicons.get_icon(name)
|
||||||
|
hl = hl or "OilFileIcon"
|
||||||
|
icon = icon or (conf and conf.default_file or "")
|
||||||
|
return icon, hl
|
||||||
end
|
end
|
||||||
local icon, hl = nonicons.get_icon(name)
|
|
||||||
if icon then
|
|
||||||
return icon, hl or 'OilFileIcon'
|
|
||||||
end
|
|
||||||
local fallback, fallback_hl = nonicons.get('file')
|
|
||||||
return fallback or (conf and conf.default_file or ''), fallback_hl or 'OilFileIcon'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local has_devicons, devicons = pcall(require, 'nvim-web-devicons')
|
|
||||||
|
|
||||||
if not has_devicons then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
return function(type, name, conf, ft)
|
|
||||||
if type == 'directory' then
|
|
||||||
return conf and conf.directory or '', 'OilDirIcon'
|
|
||||||
else
|
|
||||||
if ft then
|
|
||||||
local ft_icon, ft_hl = devicons.get_icon_by_filetype(ft)
|
|
||||||
if ft_icon and ft_icon ~= '' then
|
|
||||||
return ft_icon, ft_hl
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local icon, hl = devicons.get_icon(name)
|
|
||||||
hl = hl or 'OilFileIcon'
|
|
||||||
icon = icon or (conf and conf.default_file or '')
|
|
||||||
return icon, hl
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -1005,25 +975,24 @@ M.read_file_to_scratch_buffer = function(path, preview_method)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.bo[bufnr].bufhidden = 'wipe'
|
vim.bo[bufnr].bufhidden = "wipe"
|
||||||
vim.bo[bufnr].buftype = 'nofile'
|
vim.bo[bufnr].buftype = "nofile"
|
||||||
|
|
||||||
local has_lines, read_res
|
local has_lines, read_res
|
||||||
if preview_method == 'fast_scratch' then
|
if preview_method == "fast_scratch" then
|
||||||
has_lines, read_res = pcall(vim.fn.readfile, path, '', vim.o.lines)
|
has_lines, read_res = pcall(vim.fn.readfile, path, "", vim.o.lines)
|
||||||
else
|
else
|
||||||
has_lines, read_res = pcall(vim.fn.readfile, path)
|
has_lines, read_res = pcall(vim.fn.readfile, path)
|
||||||
end
|
end
|
||||||
local lines = has_lines and vim.split(table.concat(read_res, '\n'), '\n') or {}
|
local lines = has_lines and vim.split(table.concat(read_res, "\n"), "\n") or {}
|
||||||
|
|
||||||
local ok = pcall(vim.api.nvim_buf_set_lines, bufnr, 0, -1, false, lines)
|
local ok = pcall(vim.api.nvim_buf_set_lines, bufnr, 0, -1, false, lines)
|
||||||
if not ok then
|
if not ok then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local ft = vim.filetype.match({ filename = path, buf = bufnr })
|
local ft = vim.filetype.match({ filename = path, buf = bufnr })
|
||||||
if ft and ft ~= '' and vim.treesitter.language.get_lang then
|
if ft and ft ~= "" and vim.treesitter.language.get_lang then
|
||||||
local lang = vim.treesitter.language.get_lang(ft)
|
local lang = vim.treesitter.language.get_lang(ft)
|
||||||
-- selene: allow(empty_if)
|
|
||||||
if not pcall(vim.treesitter.start, bufnr, lang) then
|
if not pcall(vim.treesitter.start, bufnr, lang) then
|
||||||
vim.bo[bufnr].syntax = ft
|
vim.bo[bufnr].syntax = ft
|
||||||
else
|
else
|
||||||
|
|
@ -1031,8 +1000,8 @@ M.read_file_to_scratch_buffer = function(path, preview_method)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Replace the scratch buffer with a real buffer if we enter it
|
-- Replace the scratch buffer with a real buffer if we enter it
|
||||||
vim.api.nvim_create_autocmd('BufEnter', {
|
vim.api.nvim_create_autocmd("BufEnter", {
|
||||||
desc = 'oil.nvim replace scratch buffer with real buffer',
|
desc = "oil.nvim replace scratch buffer with real buffer",
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
callback = function()
|
callback = function()
|
||||||
local winid = vim.api.nvim_get_current_win()
|
local winid = vim.api.nvim_get_current_win()
|
||||||
|
|
@ -1044,7 +1013,7 @@ M.read_file_to_scratch_buffer = function(path, preview_method)
|
||||||
-- If we're still in a preview window, make sure this buffer still gets treated as a
|
-- If we're still in a preview window, make sure this buffer still gets treated as a
|
||||||
-- preview
|
-- preview
|
||||||
if vim.wo.previewwindow then
|
if vim.wo.previewwindow then
|
||||||
vim.bo.bufhidden = 'wipe'
|
vim.bo.bufhidden = "wipe"
|
||||||
vim.b.oil_preview_buffer = true
|
vim.b.oil_preview_buffer = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -1061,7 +1030,7 @@ local _regcache = {}
|
||||||
---@return boolean
|
---@return boolean
|
||||||
M.file_matches_bufreadcmd = function(filename)
|
M.file_matches_bufreadcmd = function(filename)
|
||||||
local autocmds = vim.api.nvim_get_autocmds({
|
local autocmds = vim.api.nvim_get_autocmds({
|
||||||
event = 'BufReadCmd',
|
event = "BufReadCmd",
|
||||||
})
|
})
|
||||||
for _, au in ipairs(autocmds) do
|
for _, au in ipairs(autocmds) do
|
||||||
local pat = _regcache[au.pattern]
|
local pat = _regcache[au.pattern]
|
||||||
|
|
|
||||||
195
lua/oil/view.lua
195
lua/oil/view.lua
|
|
@ -1,12 +1,12 @@
|
||||||
local uv = vim.uv or vim.loop
|
local uv = vim.uv or vim.loop
|
||||||
local cache = require('oil.cache')
|
local cache = require("oil.cache")
|
||||||
local columns = require('oil.columns')
|
local columns = require("oil.columns")
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
local constants = require('oil.constants')
|
local constants = require("oil.constants")
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local keymap_util = require('oil.keymap_util')
|
local keymap_util = require("oil.keymap_util")
|
||||||
local loading = require('oil.loading')
|
local loading = require("oil.loading")
|
||||||
local util = require('oil.util')
|
local util = require("oil.util")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local FIELD_ID = constants.FIELD_ID
|
local FIELD_ID = constants.FIELD_ID
|
||||||
|
|
@ -41,7 +41,7 @@ end
|
||||||
|
|
||||||
---Set the cursor to the last_cursor_entry if one exists
|
---Set the cursor to the last_cursor_entry if one exists
|
||||||
M.maybe_set_cursor = function()
|
M.maybe_set_cursor = function()
|
||||||
local oil = require('oil')
|
local oil = require("oil")
|
||||||
local bufname = vim.api.nvim_buf_get_name(0)
|
local bufname = vim.api.nvim_buf_get_name(0)
|
||||||
local entry_name = last_cursor_entry[bufname]
|
local entry_name = last_cursor_entry[bufname]
|
||||||
if not entry_name then
|
if not entry_name then
|
||||||
|
|
@ -52,7 +52,7 @@ M.maybe_set_cursor = function()
|
||||||
local entry = oil.get_entry_on_line(0, lnum)
|
local entry = oil.get_entry_on_line(0, lnum)
|
||||||
if entry and entry.name == entry_name then
|
if entry and entry.name == entry_name then
|
||||||
local line = vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, true)[1]
|
local line = vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, true)[1]
|
||||||
local id_str = line:match('^/(%d+)')
|
local id_str = line:match("^/(%d+)")
|
||||||
local col = line:find(entry_name, 1, true) or (id_str:len() + 1)
|
local col = line:find(entry_name, 1, true) or (id_str:len() + 1)
|
||||||
vim.api.nvim_win_set_cursor(0, { lnum, col - 1 })
|
vim.api.nvim_win_set_cursor(0, { lnum, col - 1 })
|
||||||
M.set_last_cursor(bufname, nil)
|
M.set_last_cursor(bufname, nil)
|
||||||
|
|
@ -78,14 +78,14 @@ local function are_any_modified()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function is_unix_executable(entry)
|
local function is_unix_executable(entry)
|
||||||
if entry[FIELD_TYPE] == 'directory' then
|
if entry[FIELD_TYPE] == "directory" then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
if not meta or not meta.stat then
|
if not meta or not meta.stat then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if meta.stat.type == 'directory' then
|
if meta.stat.type == "directory" then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -98,7 +98,7 @@ end
|
||||||
M.toggle_hidden = function()
|
M.toggle_hidden = function()
|
||||||
local any_modified = are_any_modified()
|
local any_modified = are_any_modified()
|
||||||
if any_modified then
|
if any_modified then
|
||||||
vim.notify('Cannot toggle hidden files when you have unsaved changes', vim.log.levels.WARN)
|
vim.notify("Cannot toggle hidden files when you have unsaved changes", vim.log.levels.WARN)
|
||||||
else
|
else
|
||||||
config.view_options.show_hidden = not config.view_options.show_hidden
|
config.view_options.show_hidden = not config.view_options.show_hidden
|
||||||
M.rerender_all_oil_buffers({ refetch = false })
|
M.rerender_all_oil_buffers({ refetch = false })
|
||||||
|
|
@ -109,7 +109,7 @@ end
|
||||||
M.set_is_hidden_file = function(is_hidden_file)
|
M.set_is_hidden_file = function(is_hidden_file)
|
||||||
local any_modified = are_any_modified()
|
local any_modified = are_any_modified()
|
||||||
if any_modified then
|
if any_modified then
|
||||||
vim.notify('Cannot change is_hidden_file when you have unsaved changes', vim.log.levels.WARN)
|
vim.notify("Cannot change is_hidden_file when you have unsaved changes", vim.log.levels.WARN)
|
||||||
else
|
else
|
||||||
config.view_options.is_hidden_file = is_hidden_file
|
config.view_options.is_hidden_file = is_hidden_file
|
||||||
M.rerender_all_oil_buffers({ refetch = false })
|
M.rerender_all_oil_buffers({ refetch = false })
|
||||||
|
|
@ -119,7 +119,7 @@ end
|
||||||
M.set_columns = function(cols)
|
M.set_columns = function(cols)
|
||||||
local any_modified = are_any_modified()
|
local any_modified = are_any_modified()
|
||||||
if any_modified then
|
if any_modified then
|
||||||
vim.notify('Cannot change columns when you have unsaved changes', vim.log.levels.WARN)
|
vim.notify("Cannot change columns when you have unsaved changes", vim.log.levels.WARN)
|
||||||
else
|
else
|
||||||
config.columns = cols
|
config.columns = cols
|
||||||
-- TODO only refetch if we don't have all the necessary data for the columns
|
-- TODO only refetch if we don't have all the necessary data for the columns
|
||||||
|
|
@ -130,7 +130,7 @@ end
|
||||||
M.set_sort = function(new_sort)
|
M.set_sort = function(new_sort)
|
||||||
local any_modified = are_any_modified()
|
local any_modified = are_any_modified()
|
||||||
if any_modified then
|
if any_modified then
|
||||||
vim.notify('Cannot change sorting when you have unsaved changes', vim.log.levels.WARN)
|
vim.notify("Cannot change sorting when you have unsaved changes", vim.log.levels.WARN)
|
||||||
else
|
else
|
||||||
config.view_options.sort = new_sort
|
config.view_options.sort = new_sort
|
||||||
-- TODO only refetch if we don't have all the necessary data for the columns
|
-- TODO only refetch if we don't have all the necessary data for the columns
|
||||||
|
|
@ -207,14 +207,14 @@ M.set_win_options = function()
|
||||||
local winid = vim.api.nvim_get_current_win()
|
local winid = vim.api.nvim_get_current_win()
|
||||||
|
|
||||||
-- work around https://github.com/neovim/neovim/pull/27422
|
-- work around https://github.com/neovim/neovim/pull/27422
|
||||||
vim.api.nvim_set_option_value('foldmethod', 'manual', { scope = 'local', win = winid })
|
vim.api.nvim_set_option_value("foldmethod", "manual", { scope = "local", win = winid })
|
||||||
|
|
||||||
for k, v in pairs(config.win_options) do
|
for k, v in pairs(config.win_options) do
|
||||||
vim.api.nvim_set_option_value(k, v, { scope = 'local', win = winid })
|
vim.api.nvim_set_option_value(k, v, { scope = "local", win = winid })
|
||||||
end
|
end
|
||||||
if vim.wo[winid].previewwindow then -- apply preview window options last
|
if vim.wo[winid].previewwindow then -- apply preview window options last
|
||||||
for k, v in pairs(config.preview_win.win_options) do
|
for k, v in pairs(config.preview_win.win_options) do
|
||||||
vim.api.nvim_set_option_value(k, v, { scope = 'local', win = winid })
|
vim.api.nvim_set_option_value(k, v, { scope = "local", win = winid })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -251,7 +251,7 @@ M.delete_hidden_buffers = function()
|
||||||
not visible_buffers
|
not visible_buffers
|
||||||
or not hidden_buffers
|
or not hidden_buffers
|
||||||
or not vim.tbl_isempty(visible_buffers)
|
or not vim.tbl_isempty(visible_buffers)
|
||||||
or vim.fn.win_gettype() == 'command'
|
or vim.fn.win_gettype() == "command"
|
||||||
then
|
then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -283,15 +283,15 @@ end
|
||||||
--- @param cur integer[]
|
--- @param cur integer[]
|
||||||
--- @return integer[] | nil
|
--- @return integer[] | nil
|
||||||
local function calc_constrained_cursor_pos(bufnr, adapter, mode, cur)
|
local function calc_constrained_cursor_pos(bufnr, adapter, mode, cur)
|
||||||
local parser = require('oil.mutator.parser')
|
local parser = require("oil.mutator.parser")
|
||||||
local line = vim.api.nvim_buf_get_lines(bufnr, cur[1] - 1, cur[1], true)[1]
|
local line = vim.api.nvim_buf_get_lines(bufnr, cur[1] - 1, cur[1], true)[1]
|
||||||
local column_defs = columns.get_supported_columns(adapter)
|
local column_defs = columns.get_supported_columns(adapter)
|
||||||
local result = parser.parse_line(adapter, line, column_defs)
|
local result = parser.parse_line(adapter, line, column_defs)
|
||||||
if result and result.ranges then
|
if result and result.ranges then
|
||||||
local min_col
|
local min_col
|
||||||
if mode == 'editable' then
|
if mode == "editable" then
|
||||||
min_col = get_first_mutable_column_col(adapter, result.ranges)
|
min_col = get_first_mutable_column_col(adapter, result.ranges)
|
||||||
elseif mode == 'name' then
|
elseif mode == "name" then
|
||||||
min_col = result.ranges.name[1]
|
min_col = result.ranges.name[1]
|
||||||
else
|
else
|
||||||
error(string.format('Unexpected value "%s" for option constrain_cursor', mode))
|
error(string.format('Unexpected value "%s" for option constrain_cursor', mode))
|
||||||
|
|
@ -318,7 +318,7 @@ local function constrain_cursor(bufnr, mode)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local mc = package.loaded['multicursor-nvim']
|
local mc = package.loaded["multicursor-nvim"]
|
||||||
if mc then
|
if mc then
|
||||||
mc.onSafeState(function()
|
mc.onSafeState(function()
|
||||||
mc.action(function(ctx)
|
mc.action(function(ctx)
|
||||||
|
|
@ -346,14 +346,14 @@ local function redraw_trash_virtual_text(bufnr)
|
||||||
if not vim.api.nvim_buf_is_valid(bufnr) or not vim.api.nvim_buf_is_loaded(bufnr) then
|
if not vim.api.nvim_buf_is_valid(bufnr) or not vim.api.nvim_buf_is_loaded(bufnr) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local parser = require('oil.mutator.parser')
|
local parser = require("oil.mutator.parser")
|
||||||
local adapter = util.get_adapter(bufnr, true)
|
local adapter = util.get_adapter(bufnr, true)
|
||||||
if not adapter or adapter.name ~= 'trash' then
|
if not adapter or adapter.name ~= "trash" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local _, buf_path = util.parse_url(vim.api.nvim_buf_get_name(bufnr))
|
local _, buf_path = util.parse_url(vim.api.nvim_buf_get_name(bufnr))
|
||||||
local os_path = fs.posix_to_os_path(assert(buf_path))
|
local os_path = fs.posix_to_os_path(assert(buf_path))
|
||||||
local ns = vim.api.nvim_create_namespace('OilVtext')
|
local ns = vim.api.nvim_create_namespace("OilVtext")
|
||||||
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
|
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
|
||||||
local column_defs = columns.get_supported_columns(adapter)
|
local column_defs = columns.get_supported_columns(adapter)
|
||||||
for lnum, line in ipairs(vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)) do
|
for lnum, line in ipairs(vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)) do
|
||||||
|
|
@ -367,8 +367,8 @@ local function redraw_trash_virtual_text(bufnr)
|
||||||
vim.api.nvim_buf_set_extmark(bufnr, ns, lnum - 1, 0, {
|
vim.api.nvim_buf_set_extmark(bufnr, ns, lnum - 1, 0, {
|
||||||
virt_text = {
|
virt_text = {
|
||||||
{
|
{
|
||||||
'➜ ' .. fs.shorten_path(trash_info.original_path, os_path),
|
"➜ " .. fs.shorten_path(trash_info.original_path, os_path),
|
||||||
'OilTrashSourcePath',
|
"OilTrashSourcePath",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
@ -387,13 +387,13 @@ M.initialize = function(bufnr)
|
||||||
end
|
end
|
||||||
vim.api.nvim_clear_autocmds({
|
vim.api.nvim_clear_autocmds({
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
group = 'Oil',
|
group = "Oil",
|
||||||
})
|
})
|
||||||
vim.bo[bufnr].buftype = 'acwrite'
|
vim.bo[bufnr].buftype = "acwrite"
|
||||||
vim.bo[bufnr].readonly = false
|
vim.bo[bufnr].readonly = false
|
||||||
vim.bo[bufnr].swapfile = false
|
vim.bo[bufnr].swapfile = false
|
||||||
vim.bo[bufnr].syntax = 'oil'
|
vim.bo[bufnr].syntax = "oil"
|
||||||
vim.bo[bufnr].filetype = 'oil'
|
vim.bo[bufnr].filetype = "oil"
|
||||||
vim.b[bufnr].EditorConfig_disable = 1
|
vim.b[bufnr].EditorConfig_disable = 1
|
||||||
session[bufnr] = session[bufnr] or {}
|
session[bufnr] = session[bufnr] or {}
|
||||||
for k, v in pairs(config.buf_options) do
|
for k, v in pairs(config.buf_options) do
|
||||||
|
|
@ -401,9 +401,9 @@ M.initialize = function(bufnr)
|
||||||
end
|
end
|
||||||
vim.api.nvim_buf_call(bufnr, M.set_win_options)
|
vim.api.nvim_buf_call(bufnr, M.set_win_options)
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd('BufHidden', {
|
vim.api.nvim_create_autocmd("BufHidden", {
|
||||||
desc = 'Delete oil buffers when no longer in use',
|
desc = "Delete oil buffers when no longer in use",
|
||||||
group = 'Oil',
|
group = "Oil",
|
||||||
nested = true,
|
nested = true,
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
callback = function()
|
callback = function()
|
||||||
|
|
@ -413,7 +413,7 @@ M.initialize = function(bufnr)
|
||||||
-- Only delete oil buffers if none of them are visible
|
-- Only delete oil buffers if none of them are visible
|
||||||
if visible_buffers and vim.tbl_isempty(visible_buffers) then
|
if visible_buffers and vim.tbl_isempty(visible_buffers) then
|
||||||
-- Check if cleanup is enabled
|
-- Check if cleanup is enabled
|
||||||
if type(config.cleanup_delay_ms) == 'number' then
|
if type(config.cleanup_delay_ms) == "number" then
|
||||||
if config.cleanup_delay_ms > 0 then
|
if config.cleanup_delay_ms > 0 then
|
||||||
vim.defer_fn(function()
|
vim.defer_fn(function()
|
||||||
M.delete_hidden_buffers()
|
M.delete_hidden_buffers()
|
||||||
|
|
@ -426,8 +426,8 @@ M.initialize = function(bufnr)
|
||||||
end, 100)
|
end, 100)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
vim.api.nvim_create_autocmd('BufUnload', {
|
vim.api.nvim_create_autocmd("BufUnload", {
|
||||||
group = 'Oil',
|
group = "Oil",
|
||||||
nested = true,
|
nested = true,
|
||||||
once = true,
|
once = true,
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
|
|
@ -439,8 +439,8 @@ M.initialize = function(bufnr)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
vim.api.nvim_create_autocmd('BufEnter', {
|
vim.api.nvim_create_autocmd("BufEnter", {
|
||||||
group = 'Oil',
|
group = "Oil",
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
callback = function(args)
|
callback = function(args)
|
||||||
local opts = vim.b[args.buf].oil_dirty
|
local opts = vim.b[args.buf].oil_dirty
|
||||||
|
|
@ -451,9 +451,9 @@ M.initialize = function(bufnr)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
local timer
|
local timer
|
||||||
vim.api.nvim_create_autocmd('InsertEnter', {
|
vim.api.nvim_create_autocmd("InsertEnter", {
|
||||||
desc = 'Constrain oil cursor position',
|
desc = "Constrain oil cursor position",
|
||||||
group = 'Oil',
|
group = "Oil",
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
callback = function()
|
callback = function()
|
||||||
-- For some reason the cursor bounces back to its original position,
|
-- For some reason the cursor bounces back to its original position,
|
||||||
|
|
@ -461,12 +461,12 @@ M.initialize = function(bufnr)
|
||||||
vim.schedule_wrap(constrain_cursor)(bufnr, config.constrain_cursor)
|
vim.schedule_wrap(constrain_cursor)(bufnr, config.constrain_cursor)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
vim.api.nvim_create_autocmd({ 'CursorMoved', 'ModeChanged' }, {
|
vim.api.nvim_create_autocmd({ "CursorMoved", "ModeChanged" }, {
|
||||||
desc = 'Update oil preview window',
|
desc = "Update oil preview window",
|
||||||
group = 'Oil',
|
group = "Oil",
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
callback = function()
|
callback = function()
|
||||||
local oil = require('oil')
|
local oil = require("oil")
|
||||||
if vim.wo.previewwindow then
|
if vim.wo.previewwindow then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -513,7 +513,7 @@ M.initialize = function(bufnr)
|
||||||
-- Set up a watcher that will refresh the directory
|
-- Set up a watcher that will refresh the directory
|
||||||
if
|
if
|
||||||
adapter
|
adapter
|
||||||
and adapter.name == 'files'
|
and adapter.name == "files"
|
||||||
and config.watch_for_changes
|
and config.watch_for_changes
|
||||||
and not session[bufnr].fs_event
|
and not session[bufnr].fs_event
|
||||||
then
|
then
|
||||||
|
|
@ -532,7 +532,7 @@ M.initialize = function(bufnr)
|
||||||
fs_event:stop()
|
fs_event:stop()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local mutator = require('oil.mutator')
|
local mutator = require("oil.mutator")
|
||||||
if err or vim.bo[bufnr].modified or vim.b[bufnr].oil_dirty or mutator.is_mutating() then
|
if err or vim.bo[bufnr].modified or vim.b[bufnr].oil_dirty or mutator.is_mutating() then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -553,11 +553,11 @@ M.initialize = function(bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Watch for TextChanged and update the trash original path extmarks
|
-- Watch for TextChanged and update the trash original path extmarks
|
||||||
if adapter and adapter.name == 'trash' then
|
if adapter and adapter.name == "trash" then
|
||||||
local debounce_timer = assert(uv.new_timer())
|
local debounce_timer = assert(uv.new_timer())
|
||||||
local pending = false
|
local pending = false
|
||||||
vim.api.nvim_create_autocmd('TextChanged', {
|
vim.api.nvim_create_autocmd("TextChanged", {
|
||||||
desc = 'Update oil virtual text of original path',
|
desc = "Update oil virtual text of original path",
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
callback = function()
|
callback = function()
|
||||||
-- Respond immediately to prevent flickering, the set the timer for a "cooldown period"
|
-- Respond immediately to prevent flickering, the set the timer for a "cooldown period"
|
||||||
|
|
@ -583,14 +583,14 @@ M.initialize = function(bufnr)
|
||||||
M.render_buffer_async(bufnr, {}, function(err)
|
M.render_buffer_async(bufnr, {}, function(err)
|
||||||
if err then
|
if err then
|
||||||
vim.notify(
|
vim.notify(
|
||||||
string.format('Error rendering oil buffer %s: %s', vim.api.nvim_buf_get_name(bufnr), err),
|
string.format("Error rendering oil buffer %s: %s", vim.api.nvim_buf_get_name(bufnr), err),
|
||||||
vim.log.levels.ERROR
|
vim.log.levels.ERROR
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
vim.b[bufnr].oil_ready = true
|
vim.b[bufnr].oil_ready = true
|
||||||
vim.api.nvim_exec_autocmds(
|
vim.api.nvim_exec_autocmds(
|
||||||
'User',
|
"User",
|
||||||
{ pattern = 'OilEnter', modeline = false, data = { buf = bufnr } }
|
{ pattern = "OilEnter", modeline = false, data = { buf = bufnr } }
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
@ -606,12 +606,12 @@ local function get_sort_function(adapter, num_entries)
|
||||||
|
|
||||||
-- If empty, default to type + name sorting
|
-- If empty, default to type + name sorting
|
||||||
if vim.tbl_isempty(sort_config) then
|
if vim.tbl_isempty(sort_config) then
|
||||||
sort_config = { { 'type', 'asc' }, { 'name', 'asc' } }
|
sort_config = { { "type", "asc" }, { "name", "asc" } }
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, sort_pair in ipairs(sort_config) do
|
for _, sort_pair in ipairs(sort_config) do
|
||||||
local col_name, order = unpack(sort_pair)
|
local col_name, order = unpack(sort_pair)
|
||||||
if order ~= 'asc' and order ~= 'desc' then
|
if order ~= "asc" and order ~= "desc" then
|
||||||
vim.notify_once(
|
vim.notify_once(
|
||||||
string.format(
|
string.format(
|
||||||
"Column '%s' has invalid sort order '%s'. Should be either 'asc' or 'desc'",
|
"Column '%s' has invalid sort order '%s'. Should be either 'asc' or 'desc'",
|
||||||
|
|
@ -639,7 +639,7 @@ local function get_sort_function(adapter, num_entries)
|
||||||
local a_val = get_sort_value(a)
|
local a_val = get_sort_value(a)
|
||||||
local b_val = get_sort_value(b)
|
local b_val = get_sort_value(b)
|
||||||
if a_val ~= b_val then
|
if a_val ~= b_val then
|
||||||
if order == 'desc' then
|
if order == "desc" then
|
||||||
return a_val > b_val
|
return a_val > b_val
|
||||||
else
|
else
|
||||||
return a_val < b_val
|
return a_val < b_val
|
||||||
|
|
@ -663,7 +663,7 @@ local function render_buffer(bufnr, opts)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
local bufname = vim.api.nvim_buf_get_name(bufnr)
|
||||||
opts = vim.tbl_extend('keep', opts or {}, {
|
opts = vim.tbl_extend("keep", opts or {}, {
|
||||||
jump = false,
|
jump = false,
|
||||||
jump_first = false,
|
jump_first = false,
|
||||||
})
|
})
|
||||||
|
|
@ -693,10 +693,10 @@ local function render_buffer(bufnr, opts)
|
||||||
for i, col_def in ipairs(column_defs) do
|
for i, col_def in ipairs(column_defs) do
|
||||||
col_width[i + 1] = 1
|
col_width[i + 1] = 1
|
||||||
local _, conf = util.split_config(col_def)
|
local _, conf = util.split_config(col_def)
|
||||||
col_align[i + 1] = conf and conf.align or 'left'
|
col_align[i + 1] = conf and conf.align or "left"
|
||||||
end
|
end
|
||||||
|
|
||||||
local parent_entry = { 0, '..', 'directory' }
|
local parent_entry = { 0, "..", "directory" }
|
||||||
if M.should_display(bufnr, parent_entry) then
|
if M.should_display(bufnr, parent_entry) then
|
||||||
local cols = M.format_entry_cols(parent_entry, column_defs, col_width, adapter, true, bufnr)
|
local cols = M.format_entry_cols(parent_entry, column_defs, col_width, adapter, true, bufnr)
|
||||||
table.insert(line_table, cols)
|
table.insert(line_table, cols)
|
||||||
|
|
@ -732,7 +732,7 @@ local function render_buffer(bufnr, opts)
|
||||||
if jump_idx then
|
if jump_idx then
|
||||||
local lnum = jump_idx
|
local lnum = jump_idx
|
||||||
local line = vim.api.nvim_buf_get_lines(bufnr, lnum - 1, lnum, true)[1]
|
local line = vim.api.nvim_buf_get_lines(bufnr, lnum - 1, lnum, true)[1]
|
||||||
local id_str = line:match('^/(%d+)')
|
local id_str = line:match("^/(%d+)")
|
||||||
local id = tonumber(id_str)
|
local id = tonumber(id_str)
|
||||||
if id then
|
if id then
|
||||||
local entry = cache.get_entry_by_id(id)
|
local entry = cache.get_entry_by_id(id)
|
||||||
|
|
@ -745,7 +745,7 @@ local function render_buffer(bufnr, opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
constrain_cursor(bufnr, 'name')
|
constrain_cursor(bufnr, "name")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
@ -760,13 +760,13 @@ end
|
||||||
local function get_link_text(name, meta)
|
local function get_link_text(name, meta)
|
||||||
local link_text
|
local link_text
|
||||||
if meta then
|
if meta then
|
||||||
if meta.link_stat and meta.link_stat.type == 'directory' then
|
if meta.link_stat and meta.link_stat.type == "directory" then
|
||||||
name = name .. '/'
|
name = name .. "/"
|
||||||
end
|
end
|
||||||
|
|
||||||
if meta.link then
|
if meta.link then
|
||||||
link_text = '-> ' .. meta.link:gsub('\n', '')
|
link_text = "-> " .. meta.link:gsub("\n", "")
|
||||||
if meta.link_stat and meta.link_stat.type == 'directory' then
|
if meta.link_stat and meta.link_stat.type == "directory" then
|
||||||
link_text = util.addslash(link_text)
|
link_text = util.addslash(link_text)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -786,15 +786,15 @@ end
|
||||||
M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden, bufnr)
|
M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden, bufnr)
|
||||||
local name = entry[FIELD_NAME]
|
local name = entry[FIELD_NAME]
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
local hl_suffix = ''
|
local hl_suffix = ""
|
||||||
if is_hidden then
|
if is_hidden then
|
||||||
hl_suffix = 'Hidden'
|
hl_suffix = "Hidden"
|
||||||
end
|
end
|
||||||
if meta and meta.display_name then
|
if meta and meta.display_name then
|
||||||
name = meta.display_name
|
name = meta.display_name
|
||||||
end
|
end
|
||||||
-- We can't handle newlines in filenames (and shame on you for doing that)
|
-- We can't handle newlines in filenames (and shame on you for doing that)
|
||||||
name = name:gsub('\n', '')
|
name = name:gsub("\n", "")
|
||||||
-- First put the unique ID
|
-- First put the unique ID
|
||||||
local cols = {}
|
local cols = {}
|
||||||
local id_key = cache.format_id(entry[FIELD_ID])
|
local id_key = cache.format_id(entry[FIELD_ID])
|
||||||
|
|
@ -803,7 +803,7 @@ M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden
|
||||||
-- Then add all the configured columns
|
-- Then add all the configured columns
|
||||||
for i, column in ipairs(column_defs) do
|
for i, column in ipairs(column_defs) do
|
||||||
local chunk = columns.render_col(adapter, column, entry, bufnr)
|
local chunk = columns.render_col(adapter, column, entry, bufnr)
|
||||||
local text = type(chunk) == 'table' and chunk[1] or chunk
|
local text = type(chunk) == "table" and chunk[1] or chunk
|
||||||
---@cast text string
|
---@cast text string
|
||||||
col_width[i + 1] = math.max(col_width[i + 1], vim.api.nvim_strwidth(text))
|
col_width[i + 1] = math.max(col_width[i + 1], vim.api.nvim_strwidth(text))
|
||||||
table.insert(cols, chunk)
|
table.insert(cols, chunk)
|
||||||
|
|
@ -816,7 +816,7 @@ M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden
|
||||||
if get_custom_hl then
|
if get_custom_hl then
|
||||||
local external_entry = util.export_entry(entry)
|
local external_entry = util.export_entry(entry)
|
||||||
|
|
||||||
if entry_type == 'link' then
|
if entry_type == "link" then
|
||||||
link_name, link_target = get_link_text(name, meta)
|
link_name, link_target = get_link_text(name, meta)
|
||||||
local is_orphan = not (meta and meta.link_stat)
|
local is_orphan = not (meta and meta.link_stat)
|
||||||
link_name_hl = get_custom_hl(external_entry, is_hidden, false, is_orphan, bufnr)
|
link_name_hl = get_custom_hl(external_entry, is_hidden, false, is_orphan, bufnr)
|
||||||
|
|
@ -830,8 +830,8 @@ M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden
|
||||||
local hl = get_custom_hl(external_entry, is_hidden, false, false, bufnr)
|
local hl = get_custom_hl(external_entry, is_hidden, false, false, bufnr)
|
||||||
if hl then
|
if hl then
|
||||||
-- Add the trailing / if this is a directory, this is important
|
-- Add the trailing / if this is a directory, this is important
|
||||||
if entry_type == 'directory' then
|
if entry_type == "directory" then
|
||||||
name = name .. '/'
|
name = name .. "/"
|
||||||
end
|
end
|
||||||
table.insert(cols, { name, hl })
|
table.insert(cols, { name, hl })
|
||||||
return cols
|
return cols
|
||||||
|
|
@ -840,50 +840,49 @@ M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden
|
||||||
end
|
end
|
||||||
|
|
||||||
local highlight_as_executable = false
|
local highlight_as_executable = false
|
||||||
if entry_type ~= 'directory' then
|
if entry_type ~= "directory" then
|
||||||
local lower = name:lower()
|
local lower = name:lower()
|
||||||
if
|
if
|
||||||
lower:match('%.exe$')
|
lower:match("%.exe$")
|
||||||
or lower:match('%.bat$')
|
or lower:match("%.bat$")
|
||||||
or lower:match('%.cmd$')
|
or lower:match("%.cmd$")
|
||||||
or lower:match('%.com$')
|
or lower:match("%.com$")
|
||||||
or lower:match('%.ps1$')
|
or lower:match("%.ps1$")
|
||||||
then
|
then
|
||||||
highlight_as_executable = true
|
highlight_as_executable = true
|
||||||
-- selene: allow(if_same_then_else)
|
|
||||||
elseif is_unix_executable(entry) then
|
elseif is_unix_executable(entry) then
|
||||||
highlight_as_executable = true
|
highlight_as_executable = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if entry_type == 'directory' then
|
if entry_type == "directory" then
|
||||||
table.insert(cols, { name .. '/', 'OilDir' .. hl_suffix })
|
table.insert(cols, { name .. "/", "OilDir" .. hl_suffix })
|
||||||
elseif entry_type == 'socket' then
|
elseif entry_type == "socket" then
|
||||||
table.insert(cols, { name, 'OilSocket' .. hl_suffix })
|
table.insert(cols, { name, "OilSocket" .. hl_suffix })
|
||||||
elseif entry_type == 'link' then
|
elseif entry_type == "link" then
|
||||||
if not link_name then
|
if not link_name then
|
||||||
link_name, link_target = get_link_text(name, meta)
|
link_name, link_target = get_link_text(name, meta)
|
||||||
end
|
end
|
||||||
local is_orphan = not (meta and meta.link_stat)
|
local is_orphan = not (meta and meta.link_stat)
|
||||||
if not link_name_hl then
|
if not link_name_hl then
|
||||||
if highlight_as_executable then
|
if highlight_as_executable then
|
||||||
link_name_hl = 'OilExecutable' .. hl_suffix
|
link_name_hl = "OilExecutable" .. hl_suffix
|
||||||
else
|
else
|
||||||
link_name_hl = (is_orphan and 'OilOrphanLink' or 'OilLink') .. hl_suffix
|
link_name_hl = (is_orphan and "OilOrphanLink" or "OilLink") .. hl_suffix
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.insert(cols, { link_name, link_name_hl })
|
table.insert(cols, { link_name, link_name_hl })
|
||||||
|
|
||||||
if link_target then
|
if link_target then
|
||||||
if not link_target_hl then
|
if not link_target_hl then
|
||||||
link_target_hl = (is_orphan and 'OilOrphanLinkTarget' or 'OilLinkTarget') .. hl_suffix
|
link_target_hl = (is_orphan and "OilOrphanLinkTarget" or "OilLinkTarget") .. hl_suffix
|
||||||
end
|
end
|
||||||
table.insert(cols, { link_target, link_target_hl })
|
table.insert(cols, { link_target, link_target_hl })
|
||||||
end
|
end
|
||||||
elseif highlight_as_executable then
|
elseif highlight_as_executable then
|
||||||
table.insert(cols, { name, 'OilExecutable' .. hl_suffix })
|
table.insert(cols, { name, "OilExecutable" .. hl_suffix })
|
||||||
else
|
else
|
||||||
table.insert(cols, { name, 'OilFile' .. hl_suffix })
|
table.insert(cols, { name, "OilFile" .. hl_suffix })
|
||||||
end
|
end
|
||||||
|
|
||||||
return cols
|
return cols
|
||||||
|
|
@ -915,8 +914,8 @@ M.render_buffer_async = function(bufnr, opts, caller_callback)
|
||||||
local function callback(err)
|
local function callback(err)
|
||||||
if not err then
|
if not err then
|
||||||
vim.api.nvim_exec_autocmds(
|
vim.api.nvim_exec_autocmds(
|
||||||
'User',
|
"User",
|
||||||
{ pattern = 'OilReadPost', modeline = false, data = { buf = bufnr } }
|
{ pattern = "OilReadPost", modeline = false, data = { buf = bufnr } }
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
if caller_callback then
|
if caller_callback then
|
||||||
|
|
@ -924,7 +923,7 @@ M.render_buffer_async = function(bufnr, opts, caller_callback)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
opts = vim.tbl_deep_extend('keep', opts or {}, {
|
opts = vim.tbl_deep_extend("keep", opts or {}, {
|
||||||
refetch = true,
|
refetch = true,
|
||||||
})
|
})
|
||||||
---@cast opts -nil
|
---@cast opts -nil
|
||||||
|
|
@ -950,8 +949,8 @@ M.render_buffer_async = function(bufnr, opts, caller_callback)
|
||||||
vim.bo[bufnr].undolevels = -1
|
vim.bo[bufnr].undolevels = -1
|
||||||
local handle_error = vim.schedule_wrap(function(message)
|
local handle_error = vim.schedule_wrap(function(message)
|
||||||
vim.b[bufnr].oil_rendering = false
|
vim.b[bufnr].oil_rendering = false
|
||||||
vim.bo[bufnr].undolevels = vim.api.nvim_get_option_value('undolevels', { scope = 'global' })
|
vim.bo[bufnr].undolevels = vim.api.nvim_get_option_value("undolevels", { scope = "global" })
|
||||||
util.render_text(bufnr, { 'Error: ' .. message })
|
util.render_text(bufnr, { "Error: " .. message })
|
||||||
if pending_renders[bufnr] then
|
if pending_renders[bufnr] then
|
||||||
for _, cb in ipairs(pending_renders[bufnr]) do
|
for _, cb in ipairs(pending_renders[bufnr]) do
|
||||||
cb(message)
|
cb(message)
|
||||||
|
|
@ -988,7 +987,7 @@ M.render_buffer_async = function(bufnr, opts, caller_callback)
|
||||||
loading.set_loading(bufnr, false)
|
loading.set_loading(bufnr, false)
|
||||||
render_buffer(bufnr, { jump = true })
|
render_buffer(bufnr, { jump = true })
|
||||||
M.set_last_cursor(bufname, nil)
|
M.set_last_cursor(bufname, nil)
|
||||||
vim.bo[bufnr].undolevels = vim.api.nvim_get_option_value('undolevels', { scope = 'global' })
|
vim.bo[bufnr].undolevels = vim.api.nvim_get_option_value("undolevels", { scope = "global" })
|
||||||
vim.bo[bufnr].modifiable = not buffers_locked and adapter.is_modifiable(bufnr)
|
vim.bo[bufnr].modifiable = not buffers_locked and adapter.is_modifiable(bufnr)
|
||||||
if callback then
|
if callback then
|
||||||
callback()
|
callback()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
M.is_win_supported = function(winid, bufnr)
|
M.is_win_supported = function(winid, bufnr)
|
||||||
return vim.bo[bufnr].filetype == 'oil'
|
return vim.bo[bufnr].filetype == "oil"
|
||||||
end
|
end
|
||||||
|
|
||||||
M.save_win = function(winid)
|
M.save_win = function(winid)
|
||||||
|
|
@ -11,7 +11,7 @@ M.save_win = function(winid)
|
||||||
end
|
end
|
||||||
|
|
||||||
M.load_win = function(winid, config)
|
M.load_win = function(winid, config)
|
||||||
require('oil').open(config.bufname)
|
require("oil").open(config.bufname)
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
vim.opt.runtimepath:prepend('scripts/benchmark.nvim')
|
vim.opt.runtimepath:prepend("scripts/benchmark.nvim")
|
||||||
vim.opt.runtimepath:prepend('.')
|
vim.opt.runtimepath:prepend(".")
|
||||||
|
|
||||||
local bm = require('benchmark')
|
local bm = require("benchmark")
|
||||||
bm.sandbox()
|
bm.sandbox()
|
||||||
|
|
||||||
---@module 'oil'
|
---@module 'oil'
|
||||||
|
|
@ -14,53 +14,50 @@ local DIR_SIZE = tonumber(vim.env.DIR_SIZE) or 100000
|
||||||
local ITERATIONS = tonumber(vim.env.ITERATIONS) or 10
|
local ITERATIONS = tonumber(vim.env.ITERATIONS) or 10
|
||||||
local WARM_UP = tonumber(vim.env.WARM_UP) or 1
|
local WARM_UP = tonumber(vim.env.WARM_UP) or 1
|
||||||
local OUTLIERS = tonumber(vim.env.OUTLIERS) or math.floor(ITERATIONS / 10)
|
local OUTLIERS = tonumber(vim.env.OUTLIERS) or math.floor(ITERATIONS / 10)
|
||||||
local TEST_DIR = 'perf/tmp/test_' .. DIR_SIZE
|
local TEST_DIR = "perf/tmp/test_" .. DIR_SIZE
|
||||||
|
|
||||||
vim.fn.mkdir(TEST_DIR, 'p')
|
vim.fn.mkdir(TEST_DIR, "p")
|
||||||
require('benchmark.files').create_files(TEST_DIR, 'file %d.txt', DIR_SIZE)
|
require("benchmark.files").create_files(TEST_DIR, "file %d.txt", DIR_SIZE)
|
||||||
|
|
||||||
-- selene: allow(global_usage)
|
|
||||||
function _G.jit_profile()
|
function _G.jit_profile()
|
||||||
require('oil').setup(setup_opts)
|
require("oil").setup(setup_opts)
|
||||||
local finish = bm.jit_profile({ filename = TEST_DIR .. '/profile.txt' })
|
local finish = bm.jit_profile({ filename = TEST_DIR .. "/profile.txt" })
|
||||||
bm.wait_for_user_event('OilEnter', function()
|
bm.wait_for_user_event("OilEnter", function()
|
||||||
finish()
|
finish()
|
||||||
end)
|
end)
|
||||||
require('oil').open(TEST_DIR)
|
require("oil").open(TEST_DIR)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- selene: allow(global_usage)
|
|
||||||
function _G.flame_profile()
|
function _G.flame_profile()
|
||||||
local start, stop = bm.flame_profile({
|
local start, stop = bm.flame_profile({
|
||||||
pattern = 'oil*',
|
pattern = "oil*",
|
||||||
filename = 'profile.json',
|
filename = "profile.json",
|
||||||
})
|
})
|
||||||
require('oil').setup(setup_opts)
|
require("oil").setup(setup_opts)
|
||||||
start()
|
start()
|
||||||
bm.wait_for_user_event('OilEnter', function()
|
bm.wait_for_user_event("OilEnter", function()
|
||||||
stop(function()
|
stop(function()
|
||||||
vim.cmd.qall({ mods = { silent = true } })
|
vim.cmd.qall({ mods = { silent = true } })
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
require('oil').open(TEST_DIR)
|
require("oil").open(TEST_DIR)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- selene: allow(global_usage)
|
|
||||||
function _G.benchmark()
|
function _G.benchmark()
|
||||||
require('oil').setup(setup_opts)
|
require("oil").setup(setup_opts)
|
||||||
bm.run({ title = 'oil.nvim', iterations = ITERATIONS, warm_up = WARM_UP }, function(callback)
|
bm.run({ title = "oil.nvim", iterations = ITERATIONS, warm_up = WARM_UP }, function(callback)
|
||||||
bm.wait_for_user_event('OilEnter', callback)
|
bm.wait_for_user_event("OilEnter", callback)
|
||||||
require('oil').open(TEST_DIR)
|
require("oil").open(TEST_DIR)
|
||||||
end, function(times)
|
end, function(times)
|
||||||
local avg = bm.avg(times, { trim_outliers = OUTLIERS })
|
local avg = bm.avg(times, { trim_outliers = OUTLIERS })
|
||||||
local std_dev = bm.std_dev(times, { trim_outliers = OUTLIERS })
|
local std_dev = bm.std_dev(times, { trim_outliers = OUTLIERS })
|
||||||
local lines = {
|
local lines = {
|
||||||
table.concat(vim.tbl_map(bm.format_time, times), ' '),
|
table.concat(vim.tbl_map(bm.format_time, times), " "),
|
||||||
string.format('Average: %s', bm.format_time(avg)),
|
string.format("Average: %s", bm.format_time(avg)),
|
||||||
string.format('Std deviation: %s', bm.format_time(std_dev)),
|
string.format("Std deviation: %s", bm.format_time(std_dev)),
|
||||||
}
|
}
|
||||||
|
|
||||||
vim.fn.writefile(lines, 'perf/tmp/benchmark.txt')
|
vim.fn.writefile(lines, "perf/tmp/benchmark.txt")
|
||||||
vim.cmd.qall({ mods = { silent = true } })
|
vim.cmd.qall({ mods = { silent = true } })
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
if vim.g.oil ~= nil then
|
if vim.g.oil ~= nil then
|
||||||
require('oil').setup()
|
require("oil").setup()
|
||||||
end
|
end
|
||||||
|
|
|
||||||
24
run_tests.sh
Executable file
24
run_tests.sh
Executable file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
mkdir -p ".testenv/config/nvim"
|
||||||
|
mkdir -p ".testenv/data/nvim"
|
||||||
|
mkdir -p ".testenv/state/nvim"
|
||||||
|
mkdir -p ".testenv/run/nvim"
|
||||||
|
mkdir -p ".testenv/cache/nvim"
|
||||||
|
PLUGINS=".testenv/data/nvim/site/pack/plugins/start"
|
||||||
|
|
||||||
|
if [ ! -e "$PLUGINS/plenary.nvim" ]; then
|
||||||
|
git clone --depth=1 https://github.com/nvim-lua/plenary.nvim.git "$PLUGINS/plenary.nvim"
|
||||||
|
else
|
||||||
|
(cd "$PLUGINS/plenary.nvim" && git pull)
|
||||||
|
fi
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME=".testenv/config" \
|
||||||
|
XDG_DATA_HOME=".testenv/data" \
|
||||||
|
XDG_STATE_HOME=".testenv/state" \
|
||||||
|
XDG_RUNTIME_DIR=".testenv/run" \
|
||||||
|
XDG_CACHE_HOME=".testenv/cache" \
|
||||||
|
nvim --headless -u tests/minimal_init.lua \
|
||||||
|
-c "PlenaryBustedDirectory ${1-tests} { minimal_init = './tests/minimal_init.lua' }"
|
||||||
|
echo "Success"
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
nix develop --command stylua --check lua spec
|
|
||||||
git ls-files '*.lua' | xargs nix develop --command selene --display-style quiet
|
|
||||||
nix develop --command prettier --check .
|
|
||||||
nix fmt
|
|
||||||
git diff --exit-code -- '*.nix'
|
|
||||||
nix develop --command busted
|
|
||||||
|
|
@ -1,7 +1 @@
|
||||||
std = 'vim'
|
std = 'vim'
|
||||||
exclude = [".direnv/*"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
mixed_table = 'allow'
|
|
||||||
unused_variable = 'allow'
|
|
||||||
bad_string_escape = 'allow'
|
|
||||||
|
|
|
||||||
|
|
@ -1,150 +0,0 @@
|
||||||
local fs = require('oil.fs')
|
|
||||||
local oil = require('oil')
|
|
||||||
local test_util = require('spec.test_util')
|
|
||||||
|
|
||||||
describe('Alternate buffer', function()
|
|
||||||
after_each(function()
|
|
||||||
test_util.reset_editor()
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('sets previous buffer as alternate', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
vim.cmd.edit({ args = { 'bar' } })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('sets previous buffer as alternate when editing url file', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
local readme = fs.join(vim.fn.getcwd(), 'README.md')
|
|
||||||
vim.cmd.edit({ args = { 'oil://' .. fs.os_to_posix_path(readme) } })
|
|
||||||
test_util.wait_for_autocmd('BufEnter')
|
|
||||||
assert.equals(readme, vim.api.nvim_buf_get_name(0))
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('sets previous buffer as alternate when editing oil://', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
vim.cmd.edit({ args = { 'oil://' .. fs.os_to_posix_path(vim.fn.getcwd()) } })
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
vim.cmd.edit({ args = { 'bar' } })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('preserves alternate buffer if editing the same file', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
vim.cmd.edit({ args = { 'bar' } })
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
vim.cmd.edit({ args = { 'bar' } })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('preserves alternate buffer if discarding changes', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
vim.cmd.edit({ args = { 'bar' } })
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
oil.close()
|
|
||||||
assert.equals('bar', vim.fn.expand('%'))
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('sets previous buffer as alternate after multi-dir hops', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
vim.cmd.edit({ args = { 'bar' } })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('sets previous buffer as alternate when inside oil buffer', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
vim.cmd.edit({ args = { 'bar' } })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
oil.open()
|
|
||||||
assert.equals('bar', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('preserves alternate when traversing oil dirs', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
vim.wait(1000, function()
|
|
||||||
return oil.get_cursor_entry()
|
|
||||||
end, 10)
|
|
||||||
vim.api.nvim_win_set_cursor(0, { 1, 1 })
|
|
||||||
oil.select()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('preserves alternate when opening preview', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
vim.wait(1000, function()
|
|
||||||
return oil.get_cursor_entry()
|
|
||||||
end, 10)
|
|
||||||
vim.api.nvim_win_set_cursor(0, { 1, 1 })
|
|
||||||
oil.open_preview()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
describe('floating window', function()
|
|
||||||
it('sets previous buffer as alternate', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
oil.open_float()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
vim.api.nvim_win_close(0, true)
|
|
||||||
vim.cmd.edit({ args = { 'bar' } })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('preserves alternate buffer if editing the same file', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
vim.cmd.edit({ args = { 'bar' } })
|
|
||||||
oil.open_float()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
vim.api.nvim_win_close(0, true)
|
|
||||||
vim.cmd.edit({ args = { 'bar' } })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('preserves alternate buffer if discarding changes', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
vim.cmd.edit({ args = { 'bar' } })
|
|
||||||
oil.open_float()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
oil.close()
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('preserves alternate when traversing to a new file', function()
|
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
|
||||||
oil.open_float()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
test_util.feedkeys({ '/LICENSE<CR>' }, 10)
|
|
||||||
oil.select()
|
|
||||||
test_util.wait_for_autocmd('BufEnter')
|
|
||||||
assert.equals('LICENSE', vim.fn.expand('%:.'))
|
|
||||||
assert.equals('foo', vim.fn.expand('#'))
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
local oil = require('oil')
|
|
||||||
local test_util = require('spec.test_util')
|
|
||||||
|
|
||||||
describe('close', function()
|
|
||||||
before_each(function()
|
|
||||||
test_util.reset_editor()
|
|
||||||
end)
|
|
||||||
after_each(function()
|
|
||||||
test_util.reset_editor()
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('does not close buffer from visual mode', function()
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
assert.equals('oil', vim.bo.filetype)
|
|
||||||
test_util.feedkeys({ 'V' }, 10)
|
|
||||||
oil.close()
|
|
||||||
assert.equals('oil', vim.bo.filetype)
|
|
||||||
test_util.feedkeys({ '<Esc>' }, 10)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('does not close buffer from operator-pending mode', function()
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
assert.equals('oil', vim.bo.filetype)
|
|
||||||
vim.api.nvim_feedkeys('d', 'n', false)
|
|
||||||
vim.wait(20)
|
|
||||||
local mode = vim.api.nvim_get_mode().mode
|
|
||||||
if mode:match('^no') then
|
|
||||||
oil.close()
|
|
||||||
assert.equals('oil', vim.bo.filetype)
|
|
||||||
end
|
|
||||||
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Esc>', true, true, true), 'n', false)
|
|
||||||
vim.wait(20)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('closes buffer from normal mode', function()
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
assert.equals('oil', vim.bo.filetype)
|
|
||||||
oil.close()
|
|
||||||
assert.not_equals('oil', vim.bo.filetype)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
local TmpDir = require('spec.tmpdir')
|
|
||||||
local files = require('oil.adapters.files')
|
|
||||||
local test_util = require('spec.test_util')
|
|
||||||
|
|
||||||
describe('files adapter', function()
|
|
||||||
local tmpdir
|
|
||||||
before_each(function()
|
|
||||||
tmpdir = TmpDir.new()
|
|
||||||
end)
|
|
||||||
after_each(function()
|
|
||||||
if tmpdir then
|
|
||||||
tmpdir:dispose()
|
|
||||||
end
|
|
||||||
test_util.reset_editor()
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('tmpdir creates files and asserts they exist', function()
|
|
||||||
tmpdir:create({ 'a.txt', 'foo/b.txt', 'foo/c.txt', 'bar/' })
|
|
||||||
tmpdir:assert_fs({
|
|
||||||
['a.txt'] = 'a.txt',
|
|
||||||
['foo/b.txt'] = 'foo/b.txt',
|
|
||||||
['foo/c.txt'] = 'foo/c.txt',
|
|
||||||
['bar/'] = true,
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Creates files', function()
|
|
||||||
local err = test_util.await(files.perform_action, 2, {
|
|
||||||
url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a.txt',
|
|
||||||
entry_type = 'file',
|
|
||||||
type = 'create',
|
|
||||||
})
|
|
||||||
assert.is_nil(err)
|
|
||||||
tmpdir:assert_fs({
|
|
||||||
['a.txt'] = '',
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Creates directories', function()
|
|
||||||
local err = test_util.await(files.perform_action, 2, {
|
|
||||||
url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a',
|
|
||||||
entry_type = 'directory',
|
|
||||||
type = 'create',
|
|
||||||
})
|
|
||||||
assert.is_nil(err)
|
|
||||||
tmpdir:assert_fs({
|
|
||||||
['a/'] = true,
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Deletes files', function()
|
|
||||||
tmpdir:create({ 'a.txt' })
|
|
||||||
local url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a.txt'
|
|
||||||
local err = test_util.await(files.perform_action, 2, {
|
|
||||||
url = url,
|
|
||||||
entry_type = 'file',
|
|
||||||
type = 'delete',
|
|
||||||
})
|
|
||||||
assert.is_nil(err)
|
|
||||||
tmpdir:assert_fs({})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Deletes directories', function()
|
|
||||||
tmpdir:create({ 'a/' })
|
|
||||||
local url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a'
|
|
||||||
local err = test_util.await(files.perform_action, 2, {
|
|
||||||
url = url,
|
|
||||||
entry_type = 'directory',
|
|
||||||
type = 'delete',
|
|
||||||
})
|
|
||||||
assert.is_nil(err)
|
|
||||||
tmpdir:assert_fs({})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Moves files', function()
|
|
||||||
tmpdir:create({ 'a.txt' })
|
|
||||||
local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a.txt'
|
|
||||||
local dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b.txt'
|
|
||||||
local err = test_util.await(files.perform_action, 2, {
|
|
||||||
src_url = src_url,
|
|
||||||
dest_url = dest_url,
|
|
||||||
entry_type = 'file',
|
|
||||||
type = 'move',
|
|
||||||
})
|
|
||||||
assert.is_nil(err)
|
|
||||||
tmpdir:assert_fs({
|
|
||||||
['b.txt'] = 'a.txt',
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Moves directories', function()
|
|
||||||
tmpdir:create({ 'a/a.txt' })
|
|
||||||
local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a'
|
|
||||||
local dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b'
|
|
||||||
local err = test_util.await(files.perform_action, 2, {
|
|
||||||
src_url = src_url,
|
|
||||||
dest_url = dest_url,
|
|
||||||
entry_type = 'directory',
|
|
||||||
type = 'move',
|
|
||||||
})
|
|
||||||
assert.is_nil(err)
|
|
||||||
tmpdir:assert_fs({
|
|
||||||
['b/a.txt'] = 'a/a.txt',
|
|
||||||
['b/'] = true,
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Copies files', function()
|
|
||||||
tmpdir:create({ 'a.txt' })
|
|
||||||
local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a.txt'
|
|
||||||
local dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b.txt'
|
|
||||||
local err = test_util.await(files.perform_action, 2, {
|
|
||||||
src_url = src_url,
|
|
||||||
dest_url = dest_url,
|
|
||||||
entry_type = 'file',
|
|
||||||
type = 'copy',
|
|
||||||
})
|
|
||||||
assert.is_nil(err)
|
|
||||||
tmpdir:assert_fs({
|
|
||||||
['a.txt'] = 'a.txt',
|
|
||||||
['b.txt'] = 'a.txt',
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Recursively copies directories', function()
|
|
||||||
tmpdir:create({ 'a/a.txt' })
|
|
||||||
local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a'
|
|
||||||
local dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b'
|
|
||||||
local err = test_util.await(files.perform_action, 2, {
|
|
||||||
src_url = src_url,
|
|
||||||
dest_url = dest_url,
|
|
||||||
entry_type = 'directory',
|
|
||||||
type = 'copy',
|
|
||||||
})
|
|
||||||
assert.is_nil(err)
|
|
||||||
tmpdir:assert_fs({
|
|
||||||
['b/a.txt'] = 'a/a.txt',
|
|
||||||
['b/'] = true,
|
|
||||||
['a/a.txt'] = 'a/a.txt',
|
|
||||||
['a/'] = true,
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Editing a new oil://path/ creates an oil buffer', function()
|
|
||||||
local tmpdir_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. '/'
|
|
||||||
vim.cmd.edit({ args = { tmpdir_url } })
|
|
||||||
test_util.wait_oil_ready()
|
|
||||||
local new_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'newdir'
|
|
||||||
vim.cmd.edit({ args = { new_url } })
|
|
||||||
test_util.wait_oil_ready()
|
|
||||||
assert.equals('oil', vim.bo.filetype)
|
|
||||||
assert.equals(new_url .. '/', vim.api.nvim_buf_get_name(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Editing a new oil://file.rb creates a normal buffer', function()
|
|
||||||
local tmpdir_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. '/'
|
|
||||||
vim.cmd.edit({ args = { tmpdir_url } })
|
|
||||||
test_util.wait_for_autocmd('BufReadPost')
|
|
||||||
local new_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'file.rb'
|
|
||||||
vim.cmd.edit({ args = { new_url } })
|
|
||||||
test_util.wait_for_autocmd('BufReadPost')
|
|
||||||
assert.equals('ruby', vim.bo.filetype)
|
|
||||||
assert.equals(vim.fn.fnamemodify(tmpdir.path, ':p') .. 'file.rb', vim.api.nvim_buf_get_name(0))
|
|
||||||
assert.equals(tmpdir.path .. '/file.rb', vim.fn.bufname())
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
vim.cmd([[set runtimepath=$VIMRUNTIME]])
|
|
||||||
vim.opt.runtimepath:append('.')
|
|
||||||
vim.opt.packpath = {}
|
|
||||||
vim.o.swapfile = false
|
|
||||||
vim.cmd('filetype on')
|
|
||||||
vim.fn.mkdir(vim.fn.stdpath('cache'), 'p')
|
|
||||||
vim.fn.mkdir(vim.fn.stdpath('data'), 'p')
|
|
||||||
vim.fn.mkdir(vim.fn.stdpath('state'), 'p')
|
|
||||||
require('spec.test_util').reset_editor()
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
local fs = require('oil.fs')
|
|
||||||
local test_util = require('spec.test_util')
|
|
||||||
local util = require('oil.util')
|
|
||||||
|
|
||||||
describe('update_moved_buffers', function()
|
|
||||||
after_each(function()
|
|
||||||
test_util.reset_editor()
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Renames moved buffers', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/bar.txt' } })
|
|
||||||
util.update_moved_buffers('file', 'oil-test:///foo/bar.txt', 'oil-test:///foo/baz.txt')
|
|
||||||
assert.equals('oil-test:///foo/baz.txt', vim.api.nvim_buf_get_name(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Renames moved buffers when they are normal files', function()
|
|
||||||
local tmpdir = fs.join(vim.loop.fs_realpath(vim.fn.stdpath('cache')), 'oil', 'test')
|
|
||||||
local testfile = fs.join(tmpdir, 'foo.txt')
|
|
||||||
vim.cmd.edit({ args = { testfile } })
|
|
||||||
util.update_moved_buffers(
|
|
||||||
'file',
|
|
||||||
'oil://' .. fs.os_to_posix_path(testfile),
|
|
||||||
'oil://' .. fs.os_to_posix_path(fs.join(tmpdir, 'bar.txt'))
|
|
||||||
)
|
|
||||||
assert.equals(fs.join(tmpdir, 'bar.txt'), vim.api.nvim_buf_get_name(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Renames directories', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
util.update_moved_buffers('directory', 'oil-test:///foo/', 'oil-test:///bar/')
|
|
||||||
assert.equals('oil-test:///bar/', vim.api.nvim_buf_get_name(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Renames subdirectories', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/bar/' } })
|
|
||||||
util.update_moved_buffers('directory', 'oil-test:///foo/', 'oil-test:///baz/')
|
|
||||||
assert.equals('oil-test:///baz/bar/', vim.api.nvim_buf_get_name(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Renames subfiles', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/bar.txt' } })
|
|
||||||
util.update_moved_buffers('directory', 'oil-test:///foo/', 'oil-test:///baz/')
|
|
||||||
assert.equals('oil-test:///baz/bar.txt', vim.api.nvim_buf_get_name(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Renames subfiles when they are normal files', function()
|
|
||||||
local tmpdir = fs.join(vim.loop.fs_realpath(vim.fn.stdpath('cache')), 'oil', 'test')
|
|
||||||
local foo = fs.join(tmpdir, 'foo')
|
|
||||||
local bar = fs.join(tmpdir, 'bar')
|
|
||||||
local testfile = fs.join(foo, 'foo.txt')
|
|
||||||
vim.cmd.edit({ args = { testfile } })
|
|
||||||
util.update_moved_buffers(
|
|
||||||
'directory',
|
|
||||||
'oil://' .. fs.os_to_posix_path(foo),
|
|
||||||
'oil://' .. fs.os_to_posix_path(bar)
|
|
||||||
)
|
|
||||||
assert.equals(fs.join(bar, 'foo.txt'), vim.api.nvim_buf_get_name(0))
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
@ -1,419 +0,0 @@
|
||||||
local cache = require('oil.cache')
|
|
||||||
local constants = require('oil.constants')
|
|
||||||
local mutator = require('oil.mutator')
|
|
||||||
local test_adapter = require('oil.adapters.test')
|
|
||||||
local test_util = require('spec.test_util')
|
|
||||||
|
|
||||||
local FIELD_ID = constants.FIELD_ID
|
|
||||||
local FIELD_NAME = constants.FIELD_NAME
|
|
||||||
local FIELD_TYPE = constants.FIELD_TYPE
|
|
||||||
|
|
||||||
describe('mutator', function()
|
|
||||||
after_each(function()
|
|
||||||
test_util.reset_editor()
|
|
||||||
end)
|
|
||||||
|
|
||||||
describe('build actions', function()
|
|
||||||
it('empty diffs produce no actions', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
local actions = mutator.create_actions_from_diffs({
|
|
||||||
[bufnr] = {},
|
|
||||||
})
|
|
||||||
assert.are.same({}, actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('constructs CREATE actions', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
local diffs = {
|
|
||||||
{ type = 'new', name = 'a.txt', entry_type = 'file' },
|
|
||||||
}
|
|
||||||
local actions = mutator.create_actions_from_diffs({
|
|
||||||
[bufnr] = diffs,
|
|
||||||
})
|
|
||||||
assert.are.same({
|
|
||||||
{
|
|
||||||
type = 'create',
|
|
||||||
entry_type = 'file',
|
|
||||||
url = 'oil-test:///foo/a.txt',
|
|
||||||
},
|
|
||||||
}, actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('constructs DELETE actions', function()
|
|
||||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
local diffs = {
|
|
||||||
{ type = 'delete', name = 'a.txt', id = file[FIELD_ID] },
|
|
||||||
}
|
|
||||||
local actions = mutator.create_actions_from_diffs({
|
|
||||||
[bufnr] = diffs,
|
|
||||||
})
|
|
||||||
assert.are.same({
|
|
||||||
{
|
|
||||||
type = 'delete',
|
|
||||||
entry_type = 'file',
|
|
||||||
url = 'oil-test:///foo/a.txt',
|
|
||||||
},
|
|
||||||
}, actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('constructs COPY actions', function()
|
|
||||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
local diffs = {
|
|
||||||
{ type = 'new', name = 'b.txt', entry_type = 'file', id = file[FIELD_ID] },
|
|
||||||
}
|
|
||||||
local actions = mutator.create_actions_from_diffs({
|
|
||||||
[bufnr] = diffs,
|
|
||||||
})
|
|
||||||
assert.are.same({
|
|
||||||
{
|
|
||||||
type = 'copy',
|
|
||||||
entry_type = 'file',
|
|
||||||
src_url = 'oil-test:///foo/a.txt',
|
|
||||||
dest_url = 'oil-test:///foo/b.txt',
|
|
||||||
},
|
|
||||||
}, actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('constructs MOVE actions', function()
|
|
||||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
local diffs = {
|
|
||||||
{ type = 'delete', name = 'a.txt', id = file[FIELD_ID] },
|
|
||||||
{ type = 'new', name = 'b.txt', entry_type = 'file', id = file[FIELD_ID] },
|
|
||||||
}
|
|
||||||
local actions = mutator.create_actions_from_diffs({
|
|
||||||
[bufnr] = diffs,
|
|
||||||
})
|
|
||||||
assert.are.same({
|
|
||||||
{
|
|
||||||
type = 'move',
|
|
||||||
entry_type = 'file',
|
|
||||||
src_url = 'oil-test:///foo/a.txt',
|
|
||||||
dest_url = 'oil-test:///foo/b.txt',
|
|
||||||
},
|
|
||||||
}, actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('correctly orders MOVE + CREATE', function()
|
|
||||||
local file = test_adapter.test_set('/a.txt', 'file')
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
local diffs = {
|
|
||||||
{ type = 'delete', name = 'a.txt', id = file[FIELD_ID] },
|
|
||||||
{ type = 'new', name = 'b.txt', entry_type = 'file', id = file[FIELD_ID] },
|
|
||||||
{ type = 'new', name = 'a.txt', entry_type = 'file' },
|
|
||||||
}
|
|
||||||
local actions = mutator.create_actions_from_diffs({
|
|
||||||
[bufnr] = diffs,
|
|
||||||
})
|
|
||||||
assert.are.same({
|
|
||||||
{
|
|
||||||
type = 'move',
|
|
||||||
entry_type = 'file',
|
|
||||||
src_url = 'oil-test:///a.txt',
|
|
||||||
dest_url = 'oil-test:///b.txt',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type = 'create',
|
|
||||||
entry_type = 'file',
|
|
||||||
url = 'oil-test:///a.txt',
|
|
||||||
},
|
|
||||||
}, actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('resolves MOVE loops', function()
|
|
||||||
local afile = test_adapter.test_set('/a.txt', 'file')
|
|
||||||
local bfile = test_adapter.test_set('/b.txt', 'file')
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
local diffs = {
|
|
||||||
{ type = 'delete', name = 'a.txt', id = afile[FIELD_ID] },
|
|
||||||
{ type = 'new', name = 'b.txt', entry_type = 'file', id = afile[FIELD_ID] },
|
|
||||||
{ type = 'delete', name = 'b.txt', id = bfile[FIELD_ID] },
|
|
||||||
{ type = 'new', name = 'a.txt', entry_type = 'file', id = bfile[FIELD_ID] },
|
|
||||||
}
|
|
||||||
math.randomseed(2983982)
|
|
||||||
local actions = mutator.create_actions_from_diffs({
|
|
||||||
[bufnr] = diffs,
|
|
||||||
})
|
|
||||||
local tmp_url = 'oil-test:///a.txt__oil_tmp_510852'
|
|
||||||
assert.are.same({
|
|
||||||
{
|
|
||||||
type = 'move',
|
|
||||||
entry_type = 'file',
|
|
||||||
src_url = 'oil-test:///a.txt',
|
|
||||||
dest_url = tmp_url,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type = 'move',
|
|
||||||
entry_type = 'file',
|
|
||||||
src_url = 'oil-test:///b.txt',
|
|
||||||
dest_url = 'oil-test:///a.txt',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type = 'move',
|
|
||||||
entry_type = 'file',
|
|
||||||
src_url = tmp_url,
|
|
||||||
dest_url = 'oil-test:///b.txt',
|
|
||||||
},
|
|
||||||
}, actions)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
describe('order actions', function()
|
|
||||||
it('Creates files inside dir before move', function()
|
|
||||||
local move = {
|
|
||||||
type = 'move',
|
|
||||||
src_url = 'oil-test:///a',
|
|
||||||
dest_url = 'oil-test:///b',
|
|
||||||
entry_type = 'directory',
|
|
||||||
}
|
|
||||||
local create = { type = 'create', url = 'oil-test:///a/hi.txt', entry_type = 'file' }
|
|
||||||
local actions = { move, create }
|
|
||||||
local ordered_actions = mutator.enforce_action_order(actions)
|
|
||||||
assert.are.same({ create, move }, ordered_actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Moves file out of parent before deleting parent', function()
|
|
||||||
local move = {
|
|
||||||
type = 'move',
|
|
||||||
src_url = 'oil-test:///a/b.txt',
|
|
||||||
dest_url = 'oil-test:///b.txt',
|
|
||||||
entry_type = 'file',
|
|
||||||
}
|
|
||||||
local delete = { type = 'delete', url = 'oil-test:///a', entry_type = 'directory' }
|
|
||||||
local actions = { delete, move }
|
|
||||||
local ordered_actions = mutator.enforce_action_order(actions)
|
|
||||||
assert.are.same({ move, delete }, ordered_actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Handles parent child move ordering', function()
|
|
||||||
local move1 = {
|
|
||||||
type = 'move',
|
|
||||||
src_url = 'oil-test:///a/b',
|
|
||||||
dest_url = 'oil-test:///b',
|
|
||||||
entry_type = 'directory',
|
|
||||||
}
|
|
||||||
local move2 = {
|
|
||||||
type = 'move',
|
|
||||||
src_url = 'oil-test:///a',
|
|
||||||
dest_url = 'oil-test:///b/a',
|
|
||||||
entry_type = 'directory',
|
|
||||||
}
|
|
||||||
local actions = { move2, move1 }
|
|
||||||
local ordered_actions = mutator.enforce_action_order(actions)
|
|
||||||
assert.are.same({ move1, move2 }, ordered_actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Handles a delete inside a moved folder', function()
|
|
||||||
local del = {
|
|
||||||
type = 'delete',
|
|
||||||
url = 'oil-test:///a/b.txt',
|
|
||||||
entry_type = 'file',
|
|
||||||
}
|
|
||||||
local move = {
|
|
||||||
type = 'move',
|
|
||||||
src_url = 'oil-test:///a',
|
|
||||||
dest_url = 'oil-test:///b',
|
|
||||||
entry_type = 'directory',
|
|
||||||
}
|
|
||||||
local actions = { move, del }
|
|
||||||
local ordered_actions = mutator.enforce_action_order(actions)
|
|
||||||
assert.are.same({ del, move }, ordered_actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Detects move directory loops', function()
|
|
||||||
local move = {
|
|
||||||
type = 'move',
|
|
||||||
src_url = 'oil-test:///a',
|
|
||||||
dest_url = 'oil-test:///a/b',
|
|
||||||
entry_type = 'directory',
|
|
||||||
}
|
|
||||||
assert.has_error(function()
|
|
||||||
mutator.enforce_action_order({ move })
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Detects copy directory loops', function()
|
|
||||||
local move = {
|
|
||||||
type = 'copy',
|
|
||||||
src_url = 'oil-test:///a',
|
|
||||||
dest_url = 'oil-test:///a/b',
|
|
||||||
entry_type = 'directory',
|
|
||||||
}
|
|
||||||
assert.has_error(function()
|
|
||||||
mutator.enforce_action_order({ move })
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Detects nested copy directory loops', function()
|
|
||||||
local move = {
|
|
||||||
type = 'copy',
|
|
||||||
src_url = 'oil-test:///a',
|
|
||||||
dest_url = 'oil-test:///a/b/a',
|
|
||||||
entry_type = 'directory',
|
|
||||||
}
|
|
||||||
assert.has_error(function()
|
|
||||||
mutator.enforce_action_order({ move })
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
describe('change', function()
|
|
||||||
it('applies CHANGE after CREATE', function()
|
|
||||||
local create = { type = 'create', url = 'oil-test:///a/hi.txt', entry_type = 'file' }
|
|
||||||
local change = {
|
|
||||||
type = 'change',
|
|
||||||
url = 'oil-test:///a/hi.txt',
|
|
||||||
entry_type = 'file',
|
|
||||||
column = 'TEST',
|
|
||||||
value = 'TEST',
|
|
||||||
}
|
|
||||||
local actions = { change, create }
|
|
||||||
local ordered_actions = mutator.enforce_action_order(actions)
|
|
||||||
assert.are.same({ create, change }, ordered_actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('applies CHANGE after COPY src', function()
|
|
||||||
local copy = {
|
|
||||||
type = 'copy',
|
|
||||||
src_url = 'oil-test:///a/hi.txt',
|
|
||||||
dest_url = 'oil-test:///b.txt',
|
|
||||||
entry_type = 'file',
|
|
||||||
}
|
|
||||||
local change = {
|
|
||||||
type = 'change',
|
|
||||||
url = 'oil-test:///a/hi.txt',
|
|
||||||
entry_type = 'file',
|
|
||||||
column = 'TEST',
|
|
||||||
value = 'TEST',
|
|
||||||
}
|
|
||||||
local actions = { change, copy }
|
|
||||||
local ordered_actions = mutator.enforce_action_order(actions)
|
|
||||||
assert.are.same({ copy, change }, ordered_actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('applies CHANGE after COPY dest', function()
|
|
||||||
local copy = {
|
|
||||||
type = 'copy',
|
|
||||||
src_url = 'oil-test:///b.txt',
|
|
||||||
dest_url = 'oil-test:///a/hi.txt',
|
|
||||||
entry_type = 'file',
|
|
||||||
}
|
|
||||||
local change = {
|
|
||||||
type = 'change',
|
|
||||||
url = 'oil-test:///a/hi.txt',
|
|
||||||
entry_type = 'file',
|
|
||||||
column = 'TEST',
|
|
||||||
value = 'TEST',
|
|
||||||
}
|
|
||||||
local actions = { change, copy }
|
|
||||||
local ordered_actions = mutator.enforce_action_order(actions)
|
|
||||||
assert.are.same({ copy, change }, ordered_actions)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('applies CHANGE after MOVE dest', function()
|
|
||||||
local move = {
|
|
||||||
type = 'move',
|
|
||||||
src_url = 'oil-test:///b.txt',
|
|
||||||
dest_url = 'oil-test:///a/hi.txt',
|
|
||||||
entry_type = 'file',
|
|
||||||
}
|
|
||||||
local change = {
|
|
||||||
type = 'change',
|
|
||||||
url = 'oil-test:///a/hi.txt',
|
|
||||||
entry_type = 'file',
|
|
||||||
column = 'TEST',
|
|
||||||
value = 'TEST',
|
|
||||||
}
|
|
||||||
local actions = { change, move }
|
|
||||||
local ordered_actions = mutator.enforce_action_order(actions)
|
|
||||||
assert.are.same({ move, change }, ordered_actions)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
describe('perform actions', function()
|
|
||||||
it('creates new entries', function()
|
|
||||||
local actions = {
|
|
||||||
{ type = 'create', url = 'oil-test:///a.txt', entry_type = 'file' },
|
|
||||||
}
|
|
||||||
test_util.await(mutator.process_actions, 2, actions)
|
|
||||||
local files = cache.list_url('oil-test:///')
|
|
||||||
assert.are.same({
|
|
||||||
['a.txt'] = {
|
|
||||||
[FIELD_ID] = 1,
|
|
||||||
[FIELD_TYPE] = 'file',
|
|
||||||
[FIELD_NAME] = 'a.txt',
|
|
||||||
},
|
|
||||||
}, files)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('deletes entries', function()
|
|
||||||
local file = test_adapter.test_set('/a.txt', 'file')
|
|
||||||
local actions = {
|
|
||||||
{ type = 'delete', url = 'oil-test:///a.txt', entry_type = 'file' },
|
|
||||||
}
|
|
||||||
test_util.await(mutator.process_actions, 2, actions)
|
|
||||||
local files = cache.list_url('oil-test:///')
|
|
||||||
assert.are.same({}, files)
|
|
||||||
assert.is_nil(cache.get_entry_by_id(file[FIELD_ID]))
|
|
||||||
assert.has_error(function()
|
|
||||||
cache.get_parent_url(file[FIELD_ID])
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('moves entries', function()
|
|
||||||
local file = test_adapter.test_set('/a.txt', 'file')
|
|
||||||
local actions = {
|
|
||||||
{
|
|
||||||
type = 'move',
|
|
||||||
src_url = 'oil-test:///a.txt',
|
|
||||||
dest_url = 'oil-test:///b.txt',
|
|
||||||
entry_type = 'file',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
test_util.await(mutator.process_actions, 2, actions)
|
|
||||||
local files = cache.list_url('oil-test:///')
|
|
||||||
local new_entry = {
|
|
||||||
[FIELD_ID] = file[FIELD_ID],
|
|
||||||
[FIELD_TYPE] = 'file',
|
|
||||||
[FIELD_NAME] = 'b.txt',
|
|
||||||
}
|
|
||||||
assert.are.same({
|
|
||||||
['b.txt'] = new_entry,
|
|
||||||
}, files)
|
|
||||||
assert.are.same(new_entry, cache.get_entry_by_id(file[FIELD_ID]))
|
|
||||||
assert.equals('oil-test:///', cache.get_parent_url(file[FIELD_ID]))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('copies entries', function()
|
|
||||||
local file = test_adapter.test_set('/a.txt', 'file')
|
|
||||||
local actions = {
|
|
||||||
{
|
|
||||||
type = 'copy',
|
|
||||||
src_url = 'oil-test:///a.txt',
|
|
||||||
dest_url = 'oil-test:///b.txt',
|
|
||||||
entry_type = 'file',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
test_util.await(mutator.process_actions, 2, actions)
|
|
||||||
local files = cache.list_url('oil-test:///')
|
|
||||||
local new_entry = {
|
|
||||||
[FIELD_ID] = file[FIELD_ID] + 1,
|
|
||||||
[FIELD_TYPE] = 'file',
|
|
||||||
[FIELD_NAME] = 'b.txt',
|
|
||||||
}
|
|
||||||
assert.are.same({
|
|
||||||
['a.txt'] = file,
|
|
||||||
['b.txt'] = new_entry,
|
|
||||||
}, files)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
@ -1,249 +0,0 @@
|
||||||
local constants = require('oil.constants')
|
|
||||||
local parser = require('oil.mutator.parser')
|
|
||||||
local test_adapter = require('oil.adapters.test')
|
|
||||||
local test_util = require('spec.test_util')
|
|
||||||
local util = require('oil.util')
|
|
||||||
local view = require('oil.view')
|
|
||||||
|
|
||||||
local FIELD_ID = constants.FIELD_ID
|
|
||||||
local FIELD_META = constants.FIELD_META
|
|
||||||
|
|
||||||
local function set_lines(bufnr, lines)
|
|
||||||
vim.bo[bufnr].modifiable = true
|
|
||||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe('parser', function()
|
|
||||||
after_each(function()
|
|
||||||
test_util.reset_editor()
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('detects new files', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {
|
|
||||||
'a.txt',
|
|
||||||
})
|
|
||||||
local diffs = parser.parse(bufnr)
|
|
||||||
assert.are.same({ { entry_type = 'file', name = 'a.txt', type = 'new' } }, diffs)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('detects new directories', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {
|
|
||||||
'foo/',
|
|
||||||
})
|
|
||||||
local diffs = parser.parse(bufnr)
|
|
||||||
assert.are.same({ { entry_type = 'directory', name = 'foo', type = 'new' } }, diffs)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('detects new links', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {
|
|
||||||
'a.txt -> b.txt',
|
|
||||||
})
|
|
||||||
local diffs = parser.parse(bufnr)
|
|
||||||
assert.are.same(
|
|
||||||
{ { entry_type = 'link', name = 'a.txt', type = 'new', link = 'b.txt' } },
|
|
||||||
diffs
|
|
||||||
)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('detects deleted files', function()
|
|
||||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {})
|
|
||||||
local diffs = parser.parse(bufnr)
|
|
||||||
assert.are.same({
|
|
||||||
{ name = 'a.txt', type = 'delete', id = file[FIELD_ID] },
|
|
||||||
}, diffs)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('detects deleted directories', function()
|
|
||||||
local dir = test_adapter.test_set('/foo/bar', 'directory')
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {})
|
|
||||||
local diffs = parser.parse(bufnr)
|
|
||||||
assert.are.same({
|
|
||||||
{ name = 'bar', type = 'delete', id = dir[FIELD_ID] },
|
|
||||||
}, diffs)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('detects deleted links', function()
|
|
||||||
local file = test_adapter.test_set('/foo/a.txt', 'link')
|
|
||||||
file[FIELD_META] = { link = 'b.txt' }
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {})
|
|
||||||
local diffs = parser.parse(bufnr)
|
|
||||||
assert.are.same({
|
|
||||||
{ name = 'a.txt', type = 'delete', id = file[FIELD_ID] },
|
|
||||||
}, diffs)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('ignores empty lines', function()
|
|
||||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
local cols = view.format_entry_cols(file, {}, {}, test_adapter, false)
|
|
||||||
local lines = util.render_table({ cols }, {})
|
|
||||||
table.insert(lines, '')
|
|
||||||
table.insert(lines, ' ')
|
|
||||||
set_lines(bufnr, lines)
|
|
||||||
local diffs = parser.parse(bufnr)
|
|
||||||
assert.are.same({}, diffs)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('errors on missing filename', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {
|
|
||||||
'/008',
|
|
||||||
})
|
|
||||||
local _, errors = parser.parse(bufnr)
|
|
||||||
assert.are_same({
|
|
||||||
{
|
|
||||||
message = 'Malformed ID at start of line',
|
|
||||||
lnum = 0,
|
|
||||||
end_lnum = 1,
|
|
||||||
col = 0,
|
|
||||||
},
|
|
||||||
}, errors)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('errors on empty dirname', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {
|
|
||||||
'/008 /',
|
|
||||||
})
|
|
||||||
local _, errors = parser.parse(bufnr)
|
|
||||||
assert.are.same({
|
|
||||||
{
|
|
||||||
message = 'No filename found',
|
|
||||||
lnum = 0,
|
|
||||||
end_lnum = 1,
|
|
||||||
col = 0,
|
|
||||||
},
|
|
||||||
}, errors)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('errors on duplicate names', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {
|
|
||||||
'foo',
|
|
||||||
'foo/',
|
|
||||||
})
|
|
||||||
local _, errors = parser.parse(bufnr)
|
|
||||||
assert.are.same({
|
|
||||||
{
|
|
||||||
message = 'Duplicate filename',
|
|
||||||
lnum = 1,
|
|
||||||
end_lnum = 2,
|
|
||||||
col = 0,
|
|
||||||
},
|
|
||||||
}, errors)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('errors on duplicate names for existing files', function()
|
|
||||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {
|
|
||||||
'a.txt',
|
|
||||||
string.format('/%d a.txt', file[FIELD_ID]),
|
|
||||||
})
|
|
||||||
local _, errors = parser.parse(bufnr)
|
|
||||||
assert.are.same({
|
|
||||||
{
|
|
||||||
message = 'Duplicate filename',
|
|
||||||
lnum = 1,
|
|
||||||
end_lnum = 2,
|
|
||||||
col = 0,
|
|
||||||
},
|
|
||||||
}, errors)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('ignores new dirs with empty name', function()
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {
|
|
||||||
'/',
|
|
||||||
})
|
|
||||||
local diffs = parser.parse(bufnr)
|
|
||||||
assert.are.same({}, diffs)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('parses a rename as a delete + new', function()
|
|
||||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {
|
|
||||||
string.format('/%d b.txt', file[FIELD_ID]),
|
|
||||||
})
|
|
||||||
local diffs = parser.parse(bufnr)
|
|
||||||
assert.are.same({
|
|
||||||
{ type = 'new', id = file[FIELD_ID], name = 'b.txt', entry_type = 'file' },
|
|
||||||
{ type = 'delete', id = file[FIELD_ID], name = 'a.txt' },
|
|
||||||
}, diffs)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('detects a new trailing slash as a delete + create', function()
|
|
||||||
local file = test_adapter.test_set('/foo', 'file')
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {
|
|
||||||
string.format('/%d foo/', file[FIELD_ID]),
|
|
||||||
})
|
|
||||||
local diffs = parser.parse(bufnr)
|
|
||||||
assert.are.same({
|
|
||||||
{ type = 'new', name = 'foo', entry_type = 'directory' },
|
|
||||||
{ type = 'delete', id = file[FIELD_ID], name = 'foo' },
|
|
||||||
}, diffs)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('detects renamed files that conflict', function()
|
|
||||||
local afile = test_adapter.test_set('/foo/a.txt', 'file')
|
|
||||||
local bfile = test_adapter.test_set('/foo/b.txt', 'file')
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {
|
|
||||||
string.format('/%d a.txt', bfile[FIELD_ID]),
|
|
||||||
string.format('/%d b.txt', afile[FIELD_ID]),
|
|
||||||
})
|
|
||||||
local diffs = parser.parse(bufnr)
|
|
||||||
local first_two = { diffs[1], diffs[2] }
|
|
||||||
local last_two = { diffs[3], diffs[4] }
|
|
||||||
table.sort(first_two, function(a, b)
|
|
||||||
return a.id < b.id
|
|
||||||
end)
|
|
||||||
table.sort(last_two, function(a, b)
|
|
||||||
return a.id < b.id
|
|
||||||
end)
|
|
||||||
assert.are.same({
|
|
||||||
{ name = 'b.txt', type = 'new', id = afile[FIELD_ID], entry_type = 'file' },
|
|
||||||
{ name = 'a.txt', type = 'new', id = bfile[FIELD_ID], entry_type = 'file' },
|
|
||||||
}, first_two)
|
|
||||||
assert.are.same({
|
|
||||||
{ name = 'a.txt', type = 'delete', id = afile[FIELD_ID] },
|
|
||||||
{ name = 'b.txt', type = 'delete', id = bfile[FIELD_ID] },
|
|
||||||
}, last_two)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('views link targets with trailing slashes as the same', function()
|
|
||||||
local file = test_adapter.test_set('/foo/mydir', 'link')
|
|
||||||
file[FIELD_META] = { link = 'dir/' }
|
|
||||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
set_lines(bufnr, {
|
|
||||||
string.format('/%d mydir/ -> dir/', file[FIELD_ID]),
|
|
||||||
})
|
|
||||||
local diffs = parser.parse(bufnr)
|
|
||||||
assert.are.same({}, diffs)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
local TmpDir = require('spec.tmpdir')
|
|
||||||
local actions = require('oil.actions')
|
|
||||||
local oil = require('oil')
|
|
||||||
local test_util = require('spec.test_util')
|
|
||||||
local view = require('oil.view')
|
|
||||||
|
|
||||||
describe('regression tests', function()
|
|
||||||
local tmpdir
|
|
||||||
before_each(function()
|
|
||||||
tmpdir = TmpDir.new()
|
|
||||||
end)
|
|
||||||
after_each(function()
|
|
||||||
if tmpdir then
|
|
||||||
tmpdir:dispose()
|
|
||||||
tmpdir = nil
|
|
||||||
end
|
|
||||||
test_util.reset_editor()
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('can edit dirs that will be renamed to an existing buffer', function()
|
|
||||||
vim.cmd.edit({ args = { 'README.md' } })
|
|
||||||
vim.cmd.vsplit()
|
|
||||||
vim.cmd.edit({ args = { '%:p:h' } })
|
|
||||||
assert.equals('oil', vim.bo.filetype)
|
|
||||||
vim.cmd.wincmd({ args = { 'p' } })
|
|
||||||
assert.equals('markdown', vim.bo.filetype)
|
|
||||||
vim.cmd.edit({ args = { '%:p:h' } })
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
assert.equals('oil', vim.bo.filetype)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('places the cursor on correct entry when opening on file', function()
|
|
||||||
vim.cmd.edit({ args = { '.' } })
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
local entry = oil.get_cursor_entry()
|
|
||||||
assert.not_nil(entry)
|
|
||||||
assert.not_equals('README.md', entry and entry.name)
|
|
||||||
vim.cmd.edit({ args = { 'README.md' } })
|
|
||||||
view.delete_hidden_buffers()
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
entry = oil.get_cursor_entry()
|
|
||||||
assert.equals('README.md', entry and entry.name)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("doesn't close floating windows oil didn't open itself", function()
|
|
||||||
local winid = vim.api.nvim_open_win(vim.fn.bufadd('README.md'), true, {
|
|
||||||
relative = 'editor',
|
|
||||||
row = 1,
|
|
||||||
col = 1,
|
|
||||||
width = 100,
|
|
||||||
height = 100,
|
|
||||||
})
|
|
||||||
oil.open()
|
|
||||||
vim.wait(10)
|
|
||||||
oil.close()
|
|
||||||
vim.wait(10)
|
|
||||||
assert.equals(winid, vim.api.nvim_get_current_win())
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("doesn't close splits on oil.close", function()
|
|
||||||
vim.cmd.edit({ args = { 'README.md' } })
|
|
||||||
vim.cmd.vsplit()
|
|
||||||
local winid = vim.api.nvim_get_current_win()
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
oil.open()
|
|
||||||
vim.wait(10)
|
|
||||||
oil.close()
|
|
||||||
vim.wait(10)
|
|
||||||
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
|
|
||||||
assert.equals(winid, vim.api.nvim_get_current_win())
|
|
||||||
assert.equals(bufnr, vim.api.nvim_get_current_buf())
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Returns to empty buffer on close', function()
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
oil.close()
|
|
||||||
assert.not_equals('oil', vim.bo.filetype)
|
|
||||||
assert.equals('', vim.api.nvim_buf_get_name(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('All buffers set nomodified after save', function()
|
|
||||||
tmpdir:create({ 'a.txt' })
|
|
||||||
vim.cmd.edit({ args = { 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') } })
|
|
||||||
local first_dir = vim.api.nvim_get_current_buf()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
test_util.feedkeys({ 'dd', 'itest/<esc>', '<CR>' }, 10)
|
|
||||||
vim.wait(1000, function()
|
|
||||||
return vim.bo.modifiable
|
|
||||||
end, 10)
|
|
||||||
test_util.feedkeys({ 'p' }, 10)
|
|
||||||
oil.save({ confirm = false })
|
|
||||||
vim.wait(1000, function()
|
|
||||||
return vim.bo.modifiable
|
|
||||||
end, 10)
|
|
||||||
tmpdir:assert_fs({
|
|
||||||
['test/a.txt'] = 'a.txt',
|
|
||||||
})
|
|
||||||
assert.falsy(vim.bo[first_dir].modified)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("refreshing buffer doesn't lose track of it", function()
|
|
||||||
vim.cmd.edit({ args = { '.' } })
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
|
||||||
vim.cmd.edit({ bang = true })
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
assert.are.same({ bufnr }, require('oil.view').get_all_buffers())
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('can copy a file multiple times', function()
|
|
||||||
test_util.actions.open({ tmpdir.path })
|
|
||||||
vim.api.nvim_feedkeys('ifoo.txt', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
vim.api.nvim_feedkeys('yyp$ciWbar.txt', 'x', true)
|
|
||||||
vim.api.nvim_feedkeys('yyp$ciWbaz.txt', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
assert.are.same({ 'bar.txt', 'baz.txt', 'foo.txt' }, test_util.parse_entries(0))
|
|
||||||
tmpdir:assert_fs({
|
|
||||||
['foo.txt'] = '',
|
|
||||||
['bar.txt'] = '',
|
|
||||||
['baz.txt'] = '',
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('can open files from floating window', function()
|
|
||||||
tmpdir:create({ 'a.txt' })
|
|
||||||
oil.open_float(tmpdir.path)
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
actions.select.callback()
|
|
||||||
vim.wait(1000, function()
|
|
||||||
return vim.fn.expand('%:t') == 'a.txt'
|
|
||||||
end, 10)
|
|
||||||
assert.equals('a.txt', vim.fn.expand('%:t'))
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
local TmpDir = require('spec.tmpdir')
|
|
||||||
local test_util = require('spec.test_util')
|
|
||||||
|
|
||||||
describe('freedesktop', function()
|
|
||||||
local tmpdir
|
|
||||||
local tmphome
|
|
||||||
local home = vim.env.XDG_DATA_HOME
|
|
||||||
before_each(function()
|
|
||||||
require('oil.config').delete_to_trash = true
|
|
||||||
tmpdir = TmpDir.new()
|
|
||||||
tmphome = TmpDir.new()
|
|
||||||
package.loaded['oil.adapters.trash'] = require('oil.adapters.trash.freedesktop')
|
|
||||||
vim.env.XDG_DATA_HOME = tmphome.path
|
|
||||||
end)
|
|
||||||
after_each(function()
|
|
||||||
vim.env.XDG_DATA_HOME = home
|
|
||||||
if tmpdir then
|
|
||||||
tmpdir:dispose()
|
|
||||||
end
|
|
||||||
if tmphome then
|
|
||||||
tmphome:dispose()
|
|
||||||
end
|
|
||||||
test_util.reset_editor()
|
|
||||||
package.loaded['oil.adapters.trash'] = nil
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('files can be moved to the trash', function()
|
|
||||||
tmpdir:create({ 'a.txt', 'foo/b.txt' })
|
|
||||||
test_util.actions.open({ tmpdir.path })
|
|
||||||
test_util.actions.focus('a.txt')
|
|
||||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
|
||||||
test_util.actions.open({ '--trash', tmpdir.path })
|
|
||||||
vim.api.nvim_feedkeys('p', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
tmpdir:assert_not_exists('a.txt')
|
|
||||||
tmpdir:assert_exists('foo/b.txt')
|
|
||||||
test_util.actions.reload()
|
|
||||||
assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('deleting a file moves it to trash', function()
|
|
||||||
tmpdir:create({ 'a.txt', 'foo/b.txt' })
|
|
||||||
test_util.actions.open({ tmpdir.path })
|
|
||||||
test_util.actions.focus('a.txt')
|
|
||||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
tmpdir:assert_not_exists('a.txt')
|
|
||||||
tmpdir:assert_exists('foo/b.txt')
|
|
||||||
test_util.actions.open({ '--trash', tmpdir.path })
|
|
||||||
assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('deleting a directory moves it to trash', function()
|
|
||||||
tmpdir:create({ 'a.txt', 'foo/b.txt' })
|
|
||||||
test_util.actions.open({ tmpdir.path })
|
|
||||||
test_util.actions.focus('foo/')
|
|
||||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
tmpdir:assert_not_exists('foo')
|
|
||||||
tmpdir:assert_exists('a.txt')
|
|
||||||
test_util.actions.open({ '--trash', tmpdir.path })
|
|
||||||
assert.are.same({ 'foo/' }, test_util.parse_entries(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('deleting a file from trash deletes it permanently', function()
|
|
||||||
tmpdir:create({ 'a.txt' })
|
|
||||||
test_util.actions.open({ tmpdir.path })
|
|
||||||
test_util.actions.focus('a.txt')
|
|
||||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
test_util.actions.open({ '--trash', tmpdir.path })
|
|
||||||
test_util.actions.focus('a.txt')
|
|
||||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
test_util.actions.reload()
|
|
||||||
tmpdir:assert_not_exists('a.txt')
|
|
||||||
assert.are.same({}, test_util.parse_entries(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('cannot create files in the trash', function()
|
|
||||||
tmpdir:create({ 'a.txt' })
|
|
||||||
test_util.actions.open({ tmpdir.path })
|
|
||||||
test_util.actions.focus('a.txt')
|
|
||||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
test_util.actions.open({ '--trash', tmpdir.path })
|
|
||||||
vim.api.nvim_feedkeys('onew_file.txt', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
test_util.actions.reload()
|
|
||||||
assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('cannot rename files in the trash', function()
|
|
||||||
tmpdir:create({ 'a.txt' })
|
|
||||||
test_util.actions.open({ tmpdir.path })
|
|
||||||
test_util.actions.focus('a.txt')
|
|
||||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
test_util.actions.open({ '--trash', tmpdir.path })
|
|
||||||
vim.api.nvim_feedkeys('0facwnew_name', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
test_util.actions.reload()
|
|
||||||
assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('cannot copy files in the trash', function()
|
|
||||||
tmpdir:create({ 'a.txt' })
|
|
||||||
test_util.actions.open({ tmpdir.path })
|
|
||||||
test_util.actions.focus('a.txt')
|
|
||||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
test_util.actions.open({ '--trash', tmpdir.path })
|
|
||||||
vim.api.nvim_feedkeys('yypp', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
test_util.actions.reload()
|
|
||||||
assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('can restore files from trash', function()
|
|
||||||
tmpdir:create({ 'a.txt' })
|
|
||||||
test_util.actions.open({ tmpdir.path })
|
|
||||||
test_util.actions.focus('a.txt')
|
|
||||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
test_util.actions.open({ '--trash', tmpdir.path })
|
|
||||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
|
||||||
test_util.actions.open({ tmpdir.path })
|
|
||||||
vim.api.nvim_feedkeys('p', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
test_util.actions.reload()
|
|
||||||
assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
|
|
||||||
tmpdir:assert_fs({
|
|
||||||
['a.txt'] = 'a.txt',
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('can have multiple files with the same name in trash', function()
|
|
||||||
tmpdir:create({ 'a.txt' })
|
|
||||||
test_util.actions.open({ tmpdir.path })
|
|
||||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
tmpdir:create({ 'a.txt' })
|
|
||||||
test_util.actions.reload()
|
|
||||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
|
||||||
test_util.actions.save()
|
|
||||||
test_util.actions.open({ '--trash', tmpdir.path })
|
|
||||||
assert.are.same({ 'a.txt', 'a.txt' }, test_util.parse_entries(0))
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
local oil = require('oil')
|
|
||||||
local util = require('oil.util')
|
|
||||||
describe('url', function()
|
|
||||||
it('get_url_for_path', function()
|
|
||||||
local cases = {
|
|
||||||
{ '', 'oil://' .. util.addslash(vim.fn.getcwd()) },
|
|
||||||
{ 'term://~/oil.nvim//52953:/bin/sh', 'oil://' .. vim.loop.os_homedir() .. '/oil.nvim/' },
|
|
||||||
{ '/foo/bar.txt', 'oil:///foo/', 'bar.txt' },
|
|
||||||
{ 'oil:///foo/bar.txt', 'oil:///foo/', 'bar.txt' },
|
|
||||||
{ 'oil:///', 'oil:///' },
|
|
||||||
{ 'oil-ssh://user@hostname:8888//bar.txt', 'oil-ssh://user@hostname:8888//', 'bar.txt' },
|
|
||||||
{ 'oil-ssh://user@hostname:8888//', 'oil-ssh://user@hostname:8888//' },
|
|
||||||
}
|
|
||||||
for _, case in ipairs(cases) do
|
|
||||||
local input, expected, expected_basename = unpack(case)
|
|
||||||
local output, basename = oil.get_buffer_parent_url(input, true)
|
|
||||||
assert.equals(expected, output, string.format('Parent url for path "%s" failed', input))
|
|
||||||
assert.equals(
|
|
||||||
expected_basename,
|
|
||||||
basename,
|
|
||||||
string.format('Basename for path "%s" failed', input)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
local oil = require('oil')
|
|
||||||
local test_util = require('spec.test_util')
|
|
||||||
|
|
||||||
describe('window options', function()
|
|
||||||
after_each(function()
|
|
||||||
test_util.reset_editor()
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Restores window options on close', function()
|
|
||||||
vim.cmd.edit({ args = { 'README.md' } })
|
|
||||||
test_util.oil_open()
|
|
||||||
assert.equals('no', vim.o.signcolumn)
|
|
||||||
oil.close()
|
|
||||||
assert.equals('auto', vim.o.signcolumn)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Restores window options on edit', function()
|
|
||||||
test_util.oil_open()
|
|
||||||
assert.equals('no', vim.o.signcolumn)
|
|
||||||
vim.cmd.edit({ args = { 'README.md' } })
|
|
||||||
assert.equals('auto', vim.o.signcolumn)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Restores window options on split <filename>', function()
|
|
||||||
test_util.oil_open()
|
|
||||||
assert.equals('no', vim.o.signcolumn)
|
|
||||||
vim.cmd.split({ args = { 'README.md' } })
|
|
||||||
assert.equals('auto', vim.o.signcolumn)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Restores window options on split', function()
|
|
||||||
test_util.oil_open()
|
|
||||||
assert.equals('no', vim.o.signcolumn)
|
|
||||||
vim.cmd.split()
|
|
||||||
vim.cmd.edit({ args = { 'README.md' } })
|
|
||||||
assert.equals('auto', vim.o.signcolumn)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Restores window options on tabnew <filename>', function()
|
|
||||||
test_util.oil_open()
|
|
||||||
assert.equals('no', vim.o.signcolumn)
|
|
||||||
vim.cmd.tabnew({ args = { 'README.md' } })
|
|
||||||
assert.equals('auto', vim.o.signcolumn)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Restores window options on tabnew', function()
|
|
||||||
test_util.oil_open()
|
|
||||||
assert.equals('no', vim.o.signcolumn)
|
|
||||||
vim.cmd.tabnew()
|
|
||||||
vim.cmd.edit({ args = { 'README.md' } })
|
|
||||||
assert.equals('auto', vim.o.signcolumn)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('Sets the window options when re-entering oil buffer', function()
|
|
||||||
oil.open()
|
|
||||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
|
||||||
assert.truthy(vim.w.oil_did_enter)
|
|
||||||
vim.cmd.edit({ args = { 'README.md' } })
|
|
||||||
assert.falsy(vim.w.oil_did_enter)
|
|
||||||
oil.open()
|
|
||||||
assert.truthy(vim.w.oil_did_enter)
|
|
||||||
vim.cmd.vsplit()
|
|
||||||
assert.truthy(vim.w.oil_did_enter)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
157
tests/altbuf_spec.lua
Normal file
157
tests/altbuf_spec.lua
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
require("plenary.async").tests.add_to_env()
|
||||||
|
local fs = require("oil.fs")
|
||||||
|
local oil = require("oil")
|
||||||
|
local test_util = require("tests.test_util")
|
||||||
|
|
||||||
|
a.describe("Alternate buffer", function()
|
||||||
|
after_each(function()
|
||||||
|
test_util.reset_editor()
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("sets previous buffer as alternate", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
vim.cmd.edit({ args = { "bar" } })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("sets previous buffer as alternate when editing url file", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
local readme = fs.join(vim.fn.getcwd(), "README.md")
|
||||||
|
vim.cmd.edit({ args = { "oil://" .. fs.os_to_posix_path(readme) } })
|
||||||
|
-- We're gonna jump around to 2 different buffers
|
||||||
|
test_util.wait_for_autocmd("BufEnter")
|
||||||
|
test_util.wait_for_autocmd("BufEnter")
|
||||||
|
assert.equals(readme, vim.api.nvim_buf_get_name(0))
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("sets previous buffer as alternate when editing oil://", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
vim.cmd.edit({ args = { "oil://" .. fs.os_to_posix_path(vim.fn.getcwd()) } })
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
vim.cmd.edit({ args = { "bar" } })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("preserves alternate buffer if editing the same file", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
vim.cmd.edit({ args = { "bar" } })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
vim.cmd.edit({ args = { "bar" } })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("preserves alternate buffer if discarding changes", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
vim.cmd.edit({ args = { "bar" } })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
oil.close()
|
||||||
|
assert.equals("bar", vim.fn.expand("%"))
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("sets previous buffer as alternate after multi-dir hops", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
vim.cmd.edit({ args = { "bar" } })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("sets previous buffer as alternate when inside oil buffer", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
vim.cmd.edit({ args = { "bar" } })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
oil.open()
|
||||||
|
assert.equals("bar", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("preserves alternate when traversing oil dirs", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
vim.wait(1000, function()
|
||||||
|
return oil.get_cursor_entry()
|
||||||
|
end, 10)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 1, 1 })
|
||||||
|
oil.select()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("preserves alternate when opening preview", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
vim.wait(1000, function()
|
||||||
|
return oil.get_cursor_entry()
|
||||||
|
end, 10)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 1, 1 })
|
||||||
|
oil.open_preview()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.describe("floating window", function()
|
||||||
|
a.it("sets previous buffer as alternate", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
oil.open_float()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
-- This is lazy, but testing the actual select logic is more difficult. We can simply
|
||||||
|
-- replicate it by closing the current window and then doing the edit
|
||||||
|
vim.api.nvim_win_close(0, true)
|
||||||
|
vim.cmd.edit({ args = { "bar" } })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("preserves alternate buffer if editing the same file", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
vim.cmd.edit({ args = { "bar" } })
|
||||||
|
oil.open_float()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
-- This is lazy, but testing the actual select logic is more difficult. We can simply
|
||||||
|
-- replicate it by closing the current window and then doing the edit
|
||||||
|
vim.api.nvim_win_close(0, true)
|
||||||
|
vim.cmd.edit({ args = { "bar" } })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("preserves alternate buffer if discarding changes", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
vim.cmd.edit({ args = { "bar" } })
|
||||||
|
oil.open_float()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
oil.close()
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("preserves alternate when traversing to a new file", function()
|
||||||
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
|
oil.open_float()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
test_util.feedkeys({ "/LICENSE<CR>" }, 10)
|
||||||
|
oil.select()
|
||||||
|
test_util.wait_for_autocmd("BufEnter")
|
||||||
|
assert.equals("LICENSE", vim.fn.expand("%:."))
|
||||||
|
assert.equals("foo", vim.fn.expand("#"))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
45
tests/close_spec.lua
Normal file
45
tests/close_spec.lua
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
require("plenary.async").tests.add_to_env()
|
||||||
|
local oil = require("oil")
|
||||||
|
local test_util = require("tests.test_util")
|
||||||
|
|
||||||
|
a.describe("close", function()
|
||||||
|
a.before_each(function()
|
||||||
|
test_util.reset_editor()
|
||||||
|
end)
|
||||||
|
a.after_each(function()
|
||||||
|
test_util.reset_editor()
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("does not close buffer from visual mode", function()
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
assert.equals("oil", vim.bo.filetype)
|
||||||
|
test_util.feedkeys({ "V" }, 10)
|
||||||
|
oil.close()
|
||||||
|
assert.equals("oil", vim.bo.filetype)
|
||||||
|
test_util.feedkeys({ "<Esc>" }, 10)
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("does not close buffer from operator-pending mode", function()
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
assert.equals("oil", vim.bo.filetype)
|
||||||
|
vim.api.nvim_feedkeys("d", "n", false)
|
||||||
|
a.util.sleep(20)
|
||||||
|
local mode = vim.api.nvim_get_mode().mode
|
||||||
|
if mode:match("^no") then
|
||||||
|
oil.close()
|
||||||
|
assert.equals("oil", vim.bo.filetype)
|
||||||
|
end
|
||||||
|
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<Esc>", true, true, true), "n", false)
|
||||||
|
a.util.sleep(20)
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("closes buffer from normal mode", function()
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
assert.equals("oil", vim.bo.filetype)
|
||||||
|
oil.close()
|
||||||
|
assert.not_equals("oil", vim.bo.filetype)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
@ -1,25 +1,25 @@
|
||||||
local config = require('oil.config')
|
local config = require("oil.config")
|
||||||
|
|
||||||
describe('config', function()
|
describe("config", function()
|
||||||
after_each(function()
|
after_each(function()
|
||||||
vim.g.oil = nil
|
vim.g.oil = nil
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('falls back to vim.g.oil when setup() is called with no args', function()
|
it("falls back to vim.g.oil when setup() is called with no args", function()
|
||||||
vim.g.oil = { delete_to_trash = true, cleanup_delay_ms = 5000 }
|
vim.g.oil = { delete_to_trash = true, cleanup_delay_ms = 5000 }
|
||||||
config.setup()
|
config.setup()
|
||||||
assert.is_true(config.delete_to_trash)
|
assert.is_true(config.delete_to_trash)
|
||||||
assert.equals(5000, config.cleanup_delay_ms)
|
assert.equals(5000, config.cleanup_delay_ms)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('uses defaults when neither opts nor vim.g.oil is set', function()
|
it("uses defaults when neither opts nor vim.g.oil is set", function()
|
||||||
vim.g.oil = nil
|
vim.g.oil = nil
|
||||||
config.setup()
|
config.setup()
|
||||||
assert.is_false(config.delete_to_trash)
|
assert.is_false(config.delete_to_trash)
|
||||||
assert.equals(2000, config.cleanup_delay_ms)
|
assert.equals(2000, config.cleanup_delay_ms)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('prefers explicit opts over vim.g.oil', function()
|
it("prefers explicit opts over vim.g.oil", function()
|
||||||
vim.g.oil = { delete_to_trash = true }
|
vim.g.oil = { delete_to_trash = true }
|
||||||
config.setup({ delete_to_trash = false })
|
config.setup({ delete_to_trash = false })
|
||||||
assert.is_false(config.delete_to_trash)
|
assert.is_false(config.delete_to_trash)
|
||||||
173
tests/files_spec.lua
Normal file
173
tests/files_spec.lua
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
require("plenary.async").tests.add_to_env()
|
||||||
|
local TmpDir = require("tests.tmpdir")
|
||||||
|
local files = require("oil.adapters.files")
|
||||||
|
local test_util = require("tests.test_util")
|
||||||
|
|
||||||
|
a.describe("files adapter", function()
|
||||||
|
local tmpdir
|
||||||
|
a.before_each(function()
|
||||||
|
tmpdir = TmpDir.new()
|
||||||
|
end)
|
||||||
|
a.after_each(function()
|
||||||
|
if tmpdir then
|
||||||
|
tmpdir:dispose()
|
||||||
|
end
|
||||||
|
test_util.reset_editor()
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("tmpdir creates files and asserts they exist", function()
|
||||||
|
tmpdir:create({ "a.txt", "foo/b.txt", "foo/c.txt", "bar/" })
|
||||||
|
tmpdir:assert_fs({
|
||||||
|
["a.txt"] = "a.txt",
|
||||||
|
["foo/b.txt"] = "foo/b.txt",
|
||||||
|
["foo/c.txt"] = "foo/c.txt",
|
||||||
|
["bar/"] = true,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("Creates files", function()
|
||||||
|
local err = a.wrap(files.perform_action, 2)({
|
||||||
|
url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "a.txt",
|
||||||
|
entry_type = "file",
|
||||||
|
type = "create",
|
||||||
|
})
|
||||||
|
assert.is_nil(err)
|
||||||
|
tmpdir:assert_fs({
|
||||||
|
["a.txt"] = "",
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("Creates directories", function()
|
||||||
|
local err = a.wrap(files.perform_action, 2)({
|
||||||
|
url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "a",
|
||||||
|
entry_type = "directory",
|
||||||
|
type = "create",
|
||||||
|
})
|
||||||
|
assert.is_nil(err)
|
||||||
|
tmpdir:assert_fs({
|
||||||
|
["a/"] = true,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("Deletes files", function()
|
||||||
|
tmpdir:create({ "a.txt" })
|
||||||
|
a.util.scheduler()
|
||||||
|
local url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "a.txt"
|
||||||
|
local err = a.wrap(files.perform_action, 2)({
|
||||||
|
url = url,
|
||||||
|
entry_type = "file",
|
||||||
|
type = "delete",
|
||||||
|
})
|
||||||
|
assert.is_nil(err)
|
||||||
|
tmpdir:assert_fs({})
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("Deletes directories", function()
|
||||||
|
tmpdir:create({ "a/" })
|
||||||
|
local url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "a"
|
||||||
|
local err = a.wrap(files.perform_action, 2)({
|
||||||
|
url = url,
|
||||||
|
entry_type = "directory",
|
||||||
|
type = "delete",
|
||||||
|
})
|
||||||
|
assert.is_nil(err)
|
||||||
|
tmpdir:assert_fs({})
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("Moves files", function()
|
||||||
|
tmpdir:create({ "a.txt" })
|
||||||
|
a.util.scheduler()
|
||||||
|
local src_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "a.txt"
|
||||||
|
local dest_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "b.txt"
|
||||||
|
local err = a.wrap(files.perform_action, 2)({
|
||||||
|
src_url = src_url,
|
||||||
|
dest_url = dest_url,
|
||||||
|
entry_type = "file",
|
||||||
|
type = "move",
|
||||||
|
})
|
||||||
|
assert.is_nil(err)
|
||||||
|
tmpdir:assert_fs({
|
||||||
|
["b.txt"] = "a.txt",
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("Moves directories", function()
|
||||||
|
tmpdir:create({ "a/a.txt" })
|
||||||
|
a.util.scheduler()
|
||||||
|
local src_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "a"
|
||||||
|
local dest_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "b"
|
||||||
|
local err = a.wrap(files.perform_action, 2)({
|
||||||
|
src_url = src_url,
|
||||||
|
dest_url = dest_url,
|
||||||
|
entry_type = "directory",
|
||||||
|
type = "move",
|
||||||
|
})
|
||||||
|
assert.is_nil(err)
|
||||||
|
tmpdir:assert_fs({
|
||||||
|
["b/a.txt"] = "a/a.txt",
|
||||||
|
["b/"] = true,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("Copies files", function()
|
||||||
|
tmpdir:create({ "a.txt" })
|
||||||
|
a.util.scheduler()
|
||||||
|
local src_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "a.txt"
|
||||||
|
local dest_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "b.txt"
|
||||||
|
local err = a.wrap(files.perform_action, 2)({
|
||||||
|
src_url = src_url,
|
||||||
|
dest_url = dest_url,
|
||||||
|
entry_type = "file",
|
||||||
|
type = "copy",
|
||||||
|
})
|
||||||
|
assert.is_nil(err)
|
||||||
|
tmpdir:assert_fs({
|
||||||
|
["a.txt"] = "a.txt",
|
||||||
|
["b.txt"] = "a.txt",
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("Recursively copies directories", function()
|
||||||
|
tmpdir:create({ "a/a.txt" })
|
||||||
|
a.util.scheduler()
|
||||||
|
local src_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "a"
|
||||||
|
local dest_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "b"
|
||||||
|
local err = a.wrap(files.perform_action, 2)({
|
||||||
|
src_url = src_url,
|
||||||
|
dest_url = dest_url,
|
||||||
|
entry_type = "directory",
|
||||||
|
type = "copy",
|
||||||
|
})
|
||||||
|
assert.is_nil(err)
|
||||||
|
tmpdir:assert_fs({
|
||||||
|
["b/a.txt"] = "a/a.txt",
|
||||||
|
["b/"] = true,
|
||||||
|
["a/a.txt"] = "a/a.txt",
|
||||||
|
["a/"] = true,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("Editing a new oil://path/ creates an oil buffer", function()
|
||||||
|
local tmpdir_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "/"
|
||||||
|
vim.cmd.edit({ args = { tmpdir_url } })
|
||||||
|
test_util.wait_oil_ready()
|
||||||
|
local new_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "newdir"
|
||||||
|
vim.cmd.edit({ args = { new_url } })
|
||||||
|
test_util.wait_oil_ready()
|
||||||
|
assert.equals("oil", vim.bo.filetype)
|
||||||
|
-- The normalization will add a '/'
|
||||||
|
assert.equals(new_url .. "/", vim.api.nvim_buf_get_name(0))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("Editing a new oil://file.rb creates a normal buffer", function()
|
||||||
|
local tmpdir_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "/"
|
||||||
|
vim.cmd.edit({ args = { tmpdir_url } })
|
||||||
|
test_util.wait_for_autocmd("BufReadPost")
|
||||||
|
local new_url = "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") .. "file.rb"
|
||||||
|
vim.cmd.edit({ args = { new_url } })
|
||||||
|
test_util.wait_for_autocmd("BufReadPost")
|
||||||
|
assert.equals("ruby", vim.bo.filetype)
|
||||||
|
assert.equals(vim.fn.fnamemodify(tmpdir.path, ":p") .. "file.rb", vim.api.nvim_buf_get_name(0))
|
||||||
|
assert.equals(tmpdir.path .. "/file.rb", vim.fn.bufname())
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
-- Manual test for minimizing/restoring progress window
|
-- Manual test for minimizing/restoring progress window
|
||||||
local Progress = require('oil.mutator.progress')
|
local Progress = require("oil.mutator.progress")
|
||||||
|
|
||||||
local progress = Progress.new()
|
local progress = Progress.new()
|
||||||
|
|
||||||
|
|
@ -12,9 +12,9 @@ progress:show({
|
||||||
for i = 1, 10, 1 do
|
for i = 1, 10, 1 do
|
||||||
vim.defer_fn(function()
|
vim.defer_fn(function()
|
||||||
progress:set_action({
|
progress:set_action({
|
||||||
type = 'create',
|
type = "create",
|
||||||
url = string.format('oil:///tmp/test_%d.txt', i),
|
url = string.format("oil:///tmp/test_%d.txt", i),
|
||||||
entry_type = 'file',
|
entry_type = "file",
|
||||||
}, i, 10)
|
}, i, 10)
|
||||||
end, (i - 1) * 1000)
|
end, (i - 1) * 1000)
|
||||||
end
|
end
|
||||||
|
|
@ -23,6 +23,6 @@ vim.defer_fn(function()
|
||||||
progress:close()
|
progress:close()
|
||||||
end, 10000)
|
end, 10000)
|
||||||
|
|
||||||
vim.keymap.set('n', 'R', function()
|
vim.keymap.set("n", "R", function()
|
||||||
progress:restore()
|
progress:restore()
|
||||||
end, {})
|
end, {})
|
||||||
5
tests/minimal_init.lua
Normal file
5
tests/minimal_init.lua
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
vim.opt.runtimepath:append(".")
|
||||||
|
|
||||||
|
vim.o.swapfile = false
|
||||||
|
vim.bo.swapfile = false
|
||||||
|
require("tests.test_util").reset_editor()
|
||||||
59
tests/move_rename_spec.lua
Normal file
59
tests/move_rename_spec.lua
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
local fs = require("oil.fs")
|
||||||
|
local test_util = require("tests.test_util")
|
||||||
|
local util = require("oil.util")
|
||||||
|
|
||||||
|
describe("update_moved_buffers", function()
|
||||||
|
after_each(function()
|
||||||
|
test_util.reset_editor()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Renames moved buffers", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/bar.txt" } })
|
||||||
|
util.update_moved_buffers("file", "oil-test:///foo/bar.txt", "oil-test:///foo/baz.txt")
|
||||||
|
assert.equals("oil-test:///foo/baz.txt", vim.api.nvim_buf_get_name(0))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Renames moved buffers when they are normal files", function()
|
||||||
|
local tmpdir = fs.join(vim.loop.fs_realpath(vim.fn.stdpath("cache")), "oil", "test")
|
||||||
|
local testfile = fs.join(tmpdir, "foo.txt")
|
||||||
|
vim.cmd.edit({ args = { testfile } })
|
||||||
|
util.update_moved_buffers(
|
||||||
|
"file",
|
||||||
|
"oil://" .. fs.os_to_posix_path(testfile),
|
||||||
|
"oil://" .. fs.os_to_posix_path(fs.join(tmpdir, "bar.txt"))
|
||||||
|
)
|
||||||
|
assert.equals(fs.join(tmpdir, "bar.txt"), vim.api.nvim_buf_get_name(0))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Renames directories", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
util.update_moved_buffers("directory", "oil-test:///foo/", "oil-test:///bar/")
|
||||||
|
assert.equals("oil-test:///bar/", vim.api.nvim_buf_get_name(0))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Renames subdirectories", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/bar/" } })
|
||||||
|
util.update_moved_buffers("directory", "oil-test:///foo/", "oil-test:///baz/")
|
||||||
|
assert.equals("oil-test:///baz/bar/", vim.api.nvim_buf_get_name(0))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Renames subfiles", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/bar.txt" } })
|
||||||
|
util.update_moved_buffers("directory", "oil-test:///foo/", "oil-test:///baz/")
|
||||||
|
assert.equals("oil-test:///baz/bar.txt", vim.api.nvim_buf_get_name(0))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Renames subfiles when they are normal files", function()
|
||||||
|
local tmpdir = fs.join(vim.loop.fs_realpath(vim.fn.stdpath("cache")), "oil", "test")
|
||||||
|
local foo = fs.join(tmpdir, "foo")
|
||||||
|
local bar = fs.join(tmpdir, "bar")
|
||||||
|
local testfile = fs.join(foo, "foo.txt")
|
||||||
|
vim.cmd.edit({ args = { testfile } })
|
||||||
|
util.update_moved_buffers(
|
||||||
|
"directory",
|
||||||
|
"oil://" .. fs.os_to_posix_path(foo),
|
||||||
|
"oil://" .. fs.os_to_posix_path(bar)
|
||||||
|
)
|
||||||
|
assert.equals(fs.join(bar, "foo.txt"), vim.api.nvim_buf_get_name(0))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
426
tests/mutator_spec.lua
Normal file
426
tests/mutator_spec.lua
Normal file
|
|
@ -0,0 +1,426 @@
|
||||||
|
require("plenary.async").tests.add_to_env()
|
||||||
|
local cache = require("oil.cache")
|
||||||
|
local constants = require("oil.constants")
|
||||||
|
local mutator = require("oil.mutator")
|
||||||
|
local test_adapter = require("oil.adapters.test")
|
||||||
|
local test_util = require("tests.test_util")
|
||||||
|
|
||||||
|
local FIELD_ID = constants.FIELD_ID
|
||||||
|
local FIELD_NAME = constants.FIELD_NAME
|
||||||
|
local FIELD_TYPE = constants.FIELD_TYPE
|
||||||
|
|
||||||
|
a.describe("mutator", function()
|
||||||
|
after_each(function()
|
||||||
|
test_util.reset_editor()
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("build actions", function()
|
||||||
|
it("empty diffs produce no actions", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
local actions = mutator.create_actions_from_diffs({
|
||||||
|
[bufnr] = {},
|
||||||
|
})
|
||||||
|
assert.are.same({}, actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("constructs CREATE actions", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
local diffs = {
|
||||||
|
{ type = "new", name = "a.txt", entry_type = "file" },
|
||||||
|
}
|
||||||
|
local actions = mutator.create_actions_from_diffs({
|
||||||
|
[bufnr] = diffs,
|
||||||
|
})
|
||||||
|
assert.are.same({
|
||||||
|
{
|
||||||
|
type = "create",
|
||||||
|
entry_type = "file",
|
||||||
|
url = "oil-test:///foo/a.txt",
|
||||||
|
},
|
||||||
|
}, actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("constructs DELETE actions", function()
|
||||||
|
local file = test_adapter.test_set("/foo/a.txt", "file")
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
local diffs = {
|
||||||
|
{ type = "delete", name = "a.txt", id = file[FIELD_ID] },
|
||||||
|
}
|
||||||
|
local actions = mutator.create_actions_from_diffs({
|
||||||
|
[bufnr] = diffs,
|
||||||
|
})
|
||||||
|
assert.are.same({
|
||||||
|
{
|
||||||
|
type = "delete",
|
||||||
|
entry_type = "file",
|
||||||
|
url = "oil-test:///foo/a.txt",
|
||||||
|
},
|
||||||
|
}, actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("constructs COPY actions", function()
|
||||||
|
local file = test_adapter.test_set("/foo/a.txt", "file")
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
local diffs = {
|
||||||
|
{ type = "new", name = "b.txt", entry_type = "file", id = file[FIELD_ID] },
|
||||||
|
}
|
||||||
|
local actions = mutator.create_actions_from_diffs({
|
||||||
|
[bufnr] = diffs,
|
||||||
|
})
|
||||||
|
assert.are.same({
|
||||||
|
{
|
||||||
|
type = "copy",
|
||||||
|
entry_type = "file",
|
||||||
|
src_url = "oil-test:///foo/a.txt",
|
||||||
|
dest_url = "oil-test:///foo/b.txt",
|
||||||
|
},
|
||||||
|
}, actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("constructs MOVE actions", function()
|
||||||
|
local file = test_adapter.test_set("/foo/a.txt", "file")
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
local diffs = {
|
||||||
|
{ type = "delete", name = "a.txt", id = file[FIELD_ID] },
|
||||||
|
{ type = "new", name = "b.txt", entry_type = "file", id = file[FIELD_ID] },
|
||||||
|
}
|
||||||
|
local actions = mutator.create_actions_from_diffs({
|
||||||
|
[bufnr] = diffs,
|
||||||
|
})
|
||||||
|
assert.are.same({
|
||||||
|
{
|
||||||
|
type = "move",
|
||||||
|
entry_type = "file",
|
||||||
|
src_url = "oil-test:///foo/a.txt",
|
||||||
|
dest_url = "oil-test:///foo/b.txt",
|
||||||
|
},
|
||||||
|
}, actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("correctly orders MOVE + CREATE", function()
|
||||||
|
local file = test_adapter.test_set("/a.txt", "file")
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
local diffs = {
|
||||||
|
{ type = "delete", name = "a.txt", id = file[FIELD_ID] },
|
||||||
|
{ type = "new", name = "b.txt", entry_type = "file", id = file[FIELD_ID] },
|
||||||
|
{ type = "new", name = "a.txt", entry_type = "file" },
|
||||||
|
}
|
||||||
|
local actions = mutator.create_actions_from_diffs({
|
||||||
|
[bufnr] = diffs,
|
||||||
|
})
|
||||||
|
assert.are.same({
|
||||||
|
{
|
||||||
|
type = "move",
|
||||||
|
entry_type = "file",
|
||||||
|
src_url = "oil-test:///a.txt",
|
||||||
|
dest_url = "oil-test:///b.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type = "create",
|
||||||
|
entry_type = "file",
|
||||||
|
url = "oil-test:///a.txt",
|
||||||
|
},
|
||||||
|
}, actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("resolves MOVE loops", function()
|
||||||
|
local afile = test_adapter.test_set("/a.txt", "file")
|
||||||
|
local bfile = test_adapter.test_set("/b.txt", "file")
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
local diffs = {
|
||||||
|
{ type = "delete", name = "a.txt", id = afile[FIELD_ID] },
|
||||||
|
{ type = "new", name = "b.txt", entry_type = "file", id = afile[FIELD_ID] },
|
||||||
|
{ type = "delete", name = "b.txt", id = bfile[FIELD_ID] },
|
||||||
|
{ type = "new", name = "a.txt", entry_type = "file", id = bfile[FIELD_ID] },
|
||||||
|
}
|
||||||
|
math.randomseed(2983982)
|
||||||
|
local actions = mutator.create_actions_from_diffs({
|
||||||
|
[bufnr] = diffs,
|
||||||
|
})
|
||||||
|
local tmp_url = "oil-test:///a.txt__oil_tmp_510852"
|
||||||
|
assert.are.same({
|
||||||
|
{
|
||||||
|
type = "move",
|
||||||
|
entry_type = "file",
|
||||||
|
src_url = "oil-test:///a.txt",
|
||||||
|
dest_url = tmp_url,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type = "move",
|
||||||
|
entry_type = "file",
|
||||||
|
src_url = "oil-test:///b.txt",
|
||||||
|
dest_url = "oil-test:///a.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type = "move",
|
||||||
|
entry_type = "file",
|
||||||
|
src_url = tmp_url,
|
||||||
|
dest_url = "oil-test:///b.txt",
|
||||||
|
},
|
||||||
|
}, actions)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("order actions", function()
|
||||||
|
it("Creates files inside dir before move", function()
|
||||||
|
local move = {
|
||||||
|
type = "move",
|
||||||
|
src_url = "oil-test:///a",
|
||||||
|
dest_url = "oil-test:///b",
|
||||||
|
entry_type = "directory",
|
||||||
|
}
|
||||||
|
local create = { type = "create", url = "oil-test:///a/hi.txt", entry_type = "file" }
|
||||||
|
local actions = { move, create }
|
||||||
|
local ordered_actions = mutator.enforce_action_order(actions)
|
||||||
|
assert.are.same({ create, move }, ordered_actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Moves file out of parent before deleting parent", function()
|
||||||
|
local move = {
|
||||||
|
type = "move",
|
||||||
|
src_url = "oil-test:///a/b.txt",
|
||||||
|
dest_url = "oil-test:///b.txt",
|
||||||
|
entry_type = "file",
|
||||||
|
}
|
||||||
|
local delete = { type = "delete", url = "oil-test:///a", entry_type = "directory" }
|
||||||
|
local actions = { delete, move }
|
||||||
|
local ordered_actions = mutator.enforce_action_order(actions)
|
||||||
|
assert.are.same({ move, delete }, ordered_actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Handles parent child move ordering", function()
|
||||||
|
-- move parent into a child and child OUT of parent
|
||||||
|
-- MOVE /a/b -> /b
|
||||||
|
-- MOVE /a -> /b/a
|
||||||
|
local move1 = {
|
||||||
|
type = "move",
|
||||||
|
src_url = "oil-test:///a/b",
|
||||||
|
dest_url = "oil-test:///b",
|
||||||
|
entry_type = "directory",
|
||||||
|
}
|
||||||
|
local move2 = {
|
||||||
|
type = "move",
|
||||||
|
src_url = "oil-test:///a",
|
||||||
|
dest_url = "oil-test:///b/a",
|
||||||
|
entry_type = "directory",
|
||||||
|
}
|
||||||
|
local actions = { move2, move1 }
|
||||||
|
local ordered_actions = mutator.enforce_action_order(actions)
|
||||||
|
assert.are.same({ move1, move2 }, ordered_actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Handles a delete inside a moved folder", function()
|
||||||
|
-- delete in directory and move directory
|
||||||
|
-- DELETE /a/b.txt
|
||||||
|
-- MOVE /a/ -> /b/
|
||||||
|
local del = {
|
||||||
|
type = "delete",
|
||||||
|
url = "oil-test:///a/b.txt",
|
||||||
|
entry_type = "file",
|
||||||
|
}
|
||||||
|
local move = {
|
||||||
|
type = "move",
|
||||||
|
src_url = "oil-test:///a",
|
||||||
|
dest_url = "oil-test:///b",
|
||||||
|
entry_type = "directory",
|
||||||
|
}
|
||||||
|
local actions = { move, del }
|
||||||
|
local ordered_actions = mutator.enforce_action_order(actions)
|
||||||
|
assert.are.same({ del, move }, ordered_actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Detects move directory loops", function()
|
||||||
|
local move = {
|
||||||
|
type = "move",
|
||||||
|
src_url = "oil-test:///a",
|
||||||
|
dest_url = "oil-test:///a/b",
|
||||||
|
entry_type = "directory",
|
||||||
|
}
|
||||||
|
assert.has_error(function()
|
||||||
|
mutator.enforce_action_order({ move })
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Detects copy directory loops", function()
|
||||||
|
local move = {
|
||||||
|
type = "copy",
|
||||||
|
src_url = "oil-test:///a",
|
||||||
|
dest_url = "oil-test:///a/b",
|
||||||
|
entry_type = "directory",
|
||||||
|
}
|
||||||
|
assert.has_error(function()
|
||||||
|
mutator.enforce_action_order({ move })
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Detects nested copy directory loops", function()
|
||||||
|
local move = {
|
||||||
|
type = "copy",
|
||||||
|
src_url = "oil-test:///a",
|
||||||
|
dest_url = "oil-test:///a/b/a",
|
||||||
|
entry_type = "directory",
|
||||||
|
}
|
||||||
|
assert.has_error(function()
|
||||||
|
mutator.enforce_action_order({ move })
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("change", function()
|
||||||
|
it("applies CHANGE after CREATE", function()
|
||||||
|
local create = { type = "create", url = "oil-test:///a/hi.txt", entry_type = "file" }
|
||||||
|
local change = {
|
||||||
|
type = "change",
|
||||||
|
url = "oil-test:///a/hi.txt",
|
||||||
|
entry_type = "file",
|
||||||
|
column = "TEST",
|
||||||
|
value = "TEST",
|
||||||
|
}
|
||||||
|
local actions = { change, create }
|
||||||
|
local ordered_actions = mutator.enforce_action_order(actions)
|
||||||
|
assert.are.same({ create, change }, ordered_actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("applies CHANGE after COPY src", function()
|
||||||
|
local copy = {
|
||||||
|
type = "copy",
|
||||||
|
src_url = "oil-test:///a/hi.txt",
|
||||||
|
dest_url = "oil-test:///b.txt",
|
||||||
|
entry_type = "file",
|
||||||
|
}
|
||||||
|
local change = {
|
||||||
|
type = "change",
|
||||||
|
url = "oil-test:///a/hi.txt",
|
||||||
|
entry_type = "file",
|
||||||
|
column = "TEST",
|
||||||
|
value = "TEST",
|
||||||
|
}
|
||||||
|
local actions = { change, copy }
|
||||||
|
local ordered_actions = mutator.enforce_action_order(actions)
|
||||||
|
assert.are.same({ copy, change }, ordered_actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("applies CHANGE after COPY dest", function()
|
||||||
|
local copy = {
|
||||||
|
type = "copy",
|
||||||
|
src_url = "oil-test:///b.txt",
|
||||||
|
dest_url = "oil-test:///a/hi.txt",
|
||||||
|
entry_type = "file",
|
||||||
|
}
|
||||||
|
local change = {
|
||||||
|
type = "change",
|
||||||
|
url = "oil-test:///a/hi.txt",
|
||||||
|
entry_type = "file",
|
||||||
|
column = "TEST",
|
||||||
|
value = "TEST",
|
||||||
|
}
|
||||||
|
local actions = { change, copy }
|
||||||
|
local ordered_actions = mutator.enforce_action_order(actions)
|
||||||
|
assert.are.same({ copy, change }, ordered_actions)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("applies CHANGE after MOVE dest", function()
|
||||||
|
local move = {
|
||||||
|
type = "move",
|
||||||
|
src_url = "oil-test:///b.txt",
|
||||||
|
dest_url = "oil-test:///a/hi.txt",
|
||||||
|
entry_type = "file",
|
||||||
|
}
|
||||||
|
local change = {
|
||||||
|
type = "change",
|
||||||
|
url = "oil-test:///a/hi.txt",
|
||||||
|
entry_type = "file",
|
||||||
|
column = "TEST",
|
||||||
|
value = "TEST",
|
||||||
|
}
|
||||||
|
local actions = { change, move }
|
||||||
|
local ordered_actions = mutator.enforce_action_order(actions)
|
||||||
|
assert.are.same({ move, change }, ordered_actions)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.describe("perform actions", function()
|
||||||
|
a.it("creates new entries", function()
|
||||||
|
local actions = {
|
||||||
|
{ type = "create", url = "oil-test:///a.txt", entry_type = "file" },
|
||||||
|
}
|
||||||
|
a.wrap(mutator.process_actions, 2)(actions)
|
||||||
|
local files = cache.list_url("oil-test:///")
|
||||||
|
assert.are.same({
|
||||||
|
["a.txt"] = {
|
||||||
|
[FIELD_ID] = 1,
|
||||||
|
[FIELD_TYPE] = "file",
|
||||||
|
[FIELD_NAME] = "a.txt",
|
||||||
|
},
|
||||||
|
}, files)
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("deletes entries", function()
|
||||||
|
local file = test_adapter.test_set("/a.txt", "file")
|
||||||
|
local actions = {
|
||||||
|
{ type = "delete", url = "oil-test:///a.txt", entry_type = "file" },
|
||||||
|
}
|
||||||
|
a.wrap(mutator.process_actions, 2)(actions)
|
||||||
|
local files = cache.list_url("oil-test:///")
|
||||||
|
assert.are.same({}, files)
|
||||||
|
assert.is_nil(cache.get_entry_by_id(file[FIELD_ID]))
|
||||||
|
assert.has_error(function()
|
||||||
|
cache.get_parent_url(file[FIELD_ID])
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("moves entries", function()
|
||||||
|
local file = test_adapter.test_set("/a.txt", "file")
|
||||||
|
local actions = {
|
||||||
|
{
|
||||||
|
type = "move",
|
||||||
|
src_url = "oil-test:///a.txt",
|
||||||
|
dest_url = "oil-test:///b.txt",
|
||||||
|
entry_type = "file",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
a.wrap(mutator.process_actions, 2)(actions)
|
||||||
|
local files = cache.list_url("oil-test:///")
|
||||||
|
local new_entry = {
|
||||||
|
[FIELD_ID] = file[FIELD_ID],
|
||||||
|
[FIELD_TYPE] = "file",
|
||||||
|
[FIELD_NAME] = "b.txt",
|
||||||
|
}
|
||||||
|
assert.are.same({
|
||||||
|
["b.txt"] = new_entry,
|
||||||
|
}, files)
|
||||||
|
assert.are.same(new_entry, cache.get_entry_by_id(file[FIELD_ID]))
|
||||||
|
assert.equals("oil-test:///", cache.get_parent_url(file[FIELD_ID]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("copies entries", function()
|
||||||
|
local file = test_adapter.test_set("/a.txt", "file")
|
||||||
|
local actions = {
|
||||||
|
{
|
||||||
|
type = "copy",
|
||||||
|
src_url = "oil-test:///a.txt",
|
||||||
|
dest_url = "oil-test:///b.txt",
|
||||||
|
entry_type = "file",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
a.wrap(mutator.process_actions, 2)(actions)
|
||||||
|
local files = cache.list_url("oil-test:///")
|
||||||
|
local new_entry = {
|
||||||
|
[FIELD_ID] = file[FIELD_ID] + 1,
|
||||||
|
[FIELD_TYPE] = "file",
|
||||||
|
[FIELD_NAME] = "b.txt",
|
||||||
|
}
|
||||||
|
assert.are.same({
|
||||||
|
["a.txt"] = file,
|
||||||
|
["b.txt"] = new_entry,
|
||||||
|
}, files)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
250
tests/parser_spec.lua
Normal file
250
tests/parser_spec.lua
Normal file
|
|
@ -0,0 +1,250 @@
|
||||||
|
require("plenary.async").tests.add_to_env()
|
||||||
|
local constants = require("oil.constants")
|
||||||
|
local parser = require("oil.mutator.parser")
|
||||||
|
local test_adapter = require("oil.adapters.test")
|
||||||
|
local test_util = require("tests.test_util")
|
||||||
|
local util = require("oil.util")
|
||||||
|
local view = require("oil.view")
|
||||||
|
|
||||||
|
local FIELD_ID = constants.FIELD_ID
|
||||||
|
local FIELD_META = constants.FIELD_META
|
||||||
|
|
||||||
|
local function set_lines(bufnr, lines)
|
||||||
|
vim.bo[bufnr].modifiable = true
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe("parser", function()
|
||||||
|
after_each(function()
|
||||||
|
test_util.reset_editor()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("detects new files", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {
|
||||||
|
"a.txt",
|
||||||
|
})
|
||||||
|
local diffs = parser.parse(bufnr)
|
||||||
|
assert.are.same({ { entry_type = "file", name = "a.txt", type = "new" } }, diffs)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("detects new directories", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {
|
||||||
|
"foo/",
|
||||||
|
})
|
||||||
|
local diffs = parser.parse(bufnr)
|
||||||
|
assert.are.same({ { entry_type = "directory", name = "foo", type = "new" } }, diffs)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("detects new links", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {
|
||||||
|
"a.txt -> b.txt",
|
||||||
|
})
|
||||||
|
local diffs = parser.parse(bufnr)
|
||||||
|
assert.are.same(
|
||||||
|
{ { entry_type = "link", name = "a.txt", type = "new", link = "b.txt" } },
|
||||||
|
diffs
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("detects deleted files", function()
|
||||||
|
local file = test_adapter.test_set("/foo/a.txt", "file")
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {})
|
||||||
|
local diffs = parser.parse(bufnr)
|
||||||
|
assert.are.same({
|
||||||
|
{ name = "a.txt", type = "delete", id = file[FIELD_ID] },
|
||||||
|
}, diffs)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("detects deleted directories", function()
|
||||||
|
local dir = test_adapter.test_set("/foo/bar", "directory")
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {})
|
||||||
|
local diffs = parser.parse(bufnr)
|
||||||
|
assert.are.same({
|
||||||
|
{ name = "bar", type = "delete", id = dir[FIELD_ID] },
|
||||||
|
}, diffs)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("detects deleted links", function()
|
||||||
|
local file = test_adapter.test_set("/foo/a.txt", "link")
|
||||||
|
file[FIELD_META] = { link = "b.txt" }
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {})
|
||||||
|
local diffs = parser.parse(bufnr)
|
||||||
|
assert.are.same({
|
||||||
|
{ name = "a.txt", type = "delete", id = file[FIELD_ID] },
|
||||||
|
}, diffs)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("ignores empty lines", function()
|
||||||
|
local file = test_adapter.test_set("/foo/a.txt", "file")
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
local cols = view.format_entry_cols(file, {}, {}, test_adapter, false)
|
||||||
|
local lines = util.render_table({ cols }, {})
|
||||||
|
table.insert(lines, "")
|
||||||
|
table.insert(lines, " ")
|
||||||
|
set_lines(bufnr, lines)
|
||||||
|
local diffs = parser.parse(bufnr)
|
||||||
|
assert.are.same({}, diffs)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("errors on missing filename", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {
|
||||||
|
"/008",
|
||||||
|
})
|
||||||
|
local _, errors = parser.parse(bufnr)
|
||||||
|
assert.are_same({
|
||||||
|
{
|
||||||
|
message = "Malformed ID at start of line",
|
||||||
|
lnum = 0,
|
||||||
|
end_lnum = 1,
|
||||||
|
col = 0,
|
||||||
|
},
|
||||||
|
}, errors)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("errors on empty dirname", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {
|
||||||
|
"/008 /",
|
||||||
|
})
|
||||||
|
local _, errors = parser.parse(bufnr)
|
||||||
|
assert.are.same({
|
||||||
|
{
|
||||||
|
message = "No filename found",
|
||||||
|
lnum = 0,
|
||||||
|
end_lnum = 1,
|
||||||
|
col = 0,
|
||||||
|
},
|
||||||
|
}, errors)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("errors on duplicate names", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {
|
||||||
|
"foo",
|
||||||
|
"foo/",
|
||||||
|
})
|
||||||
|
local _, errors = parser.parse(bufnr)
|
||||||
|
assert.are.same({
|
||||||
|
{
|
||||||
|
message = "Duplicate filename",
|
||||||
|
lnum = 1,
|
||||||
|
end_lnum = 2,
|
||||||
|
col = 0,
|
||||||
|
},
|
||||||
|
}, errors)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("errors on duplicate names for existing files", function()
|
||||||
|
local file = test_adapter.test_set("/foo/a.txt", "file")
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {
|
||||||
|
"a.txt",
|
||||||
|
string.format("/%d a.txt", file[FIELD_ID]),
|
||||||
|
})
|
||||||
|
local _, errors = parser.parse(bufnr)
|
||||||
|
assert.are.same({
|
||||||
|
{
|
||||||
|
message = "Duplicate filename",
|
||||||
|
lnum = 1,
|
||||||
|
end_lnum = 2,
|
||||||
|
col = 0,
|
||||||
|
},
|
||||||
|
}, errors)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("ignores new dirs with empty name", function()
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {
|
||||||
|
"/",
|
||||||
|
})
|
||||||
|
local diffs = parser.parse(bufnr)
|
||||||
|
assert.are.same({}, diffs)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("parses a rename as a delete + new", function()
|
||||||
|
local file = test_adapter.test_set("/foo/a.txt", "file")
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {
|
||||||
|
string.format("/%d b.txt", file[FIELD_ID]),
|
||||||
|
})
|
||||||
|
local diffs = parser.parse(bufnr)
|
||||||
|
assert.are.same({
|
||||||
|
{ type = "new", id = file[FIELD_ID], name = "b.txt", entry_type = "file" },
|
||||||
|
{ type = "delete", id = file[FIELD_ID], name = "a.txt" },
|
||||||
|
}, diffs)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("detects a new trailing slash as a delete + create", function()
|
||||||
|
local file = test_adapter.test_set("/foo", "file")
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {
|
||||||
|
string.format("/%d foo/", file[FIELD_ID]),
|
||||||
|
})
|
||||||
|
local diffs = parser.parse(bufnr)
|
||||||
|
assert.are.same({
|
||||||
|
{ type = "new", name = "foo", entry_type = "directory" },
|
||||||
|
{ type = "delete", id = file[FIELD_ID], name = "foo" },
|
||||||
|
}, diffs)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("detects renamed files that conflict", function()
|
||||||
|
local afile = test_adapter.test_set("/foo/a.txt", "file")
|
||||||
|
local bfile = test_adapter.test_set("/foo/b.txt", "file")
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {
|
||||||
|
string.format("/%d a.txt", bfile[FIELD_ID]),
|
||||||
|
string.format("/%d b.txt", afile[FIELD_ID]),
|
||||||
|
})
|
||||||
|
local diffs = parser.parse(bufnr)
|
||||||
|
local first_two = { diffs[1], diffs[2] }
|
||||||
|
local last_two = { diffs[3], diffs[4] }
|
||||||
|
table.sort(first_two, function(a, b)
|
||||||
|
return a.id < b.id
|
||||||
|
end)
|
||||||
|
table.sort(last_two, function(a, b)
|
||||||
|
return a.id < b.id
|
||||||
|
end)
|
||||||
|
assert.are.same({
|
||||||
|
{ name = "b.txt", type = "new", id = afile[FIELD_ID], entry_type = "file" },
|
||||||
|
{ name = "a.txt", type = "new", id = bfile[FIELD_ID], entry_type = "file" },
|
||||||
|
}, first_two)
|
||||||
|
assert.are.same({
|
||||||
|
{ name = "a.txt", type = "delete", id = afile[FIELD_ID] },
|
||||||
|
{ name = "b.txt", type = "delete", id = bfile[FIELD_ID] },
|
||||||
|
}, last_two)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("views link targets with trailing slashes as the same", function()
|
||||||
|
local file = test_adapter.test_set("/foo/mydir", "link")
|
||||||
|
file[FIELD_META] = { link = "dir/" }
|
||||||
|
vim.cmd.edit({ args = { "oil-test:///foo/" } })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
set_lines(bufnr, {
|
||||||
|
string.format("/%d mydir/ -> dir/", file[FIELD_ID]),
|
||||||
|
})
|
||||||
|
local diffs = parser.parse(bufnr)
|
||||||
|
assert.are.same({}, diffs)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
local pathutil = require('oil.pathutil')
|
local pathutil = require("oil.pathutil")
|
||||||
describe('pathutil', function()
|
describe("pathutil", function()
|
||||||
it('calculates parent path', function()
|
it("calculates parent path", function()
|
||||||
local cases = {
|
local cases = {
|
||||||
{ '/foo/bar', '/foo/' },
|
{ "/foo/bar", "/foo/" },
|
||||||
{ '/foo/bar/', '/foo/' },
|
{ "/foo/bar/", "/foo/" },
|
||||||
{ '/', '/' },
|
{ "/", "/" },
|
||||||
{ '', '' },
|
{ "", "" },
|
||||||
{ 'foo/bar/', 'foo/' },
|
{ "foo/bar/", "foo/" },
|
||||||
{ 'foo', '' },
|
{ "foo", "" },
|
||||||
}
|
}
|
||||||
for _, case in ipairs(cases) do
|
for _, case in ipairs(cases) do
|
||||||
local input, expected = unpack(case)
|
local input, expected = unpack(case)
|
||||||
|
|
@ -16,12 +16,12 @@ describe('pathutil', function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('calculates basename', function()
|
it("calculates basename", function()
|
||||||
local cases = {
|
local cases = {
|
||||||
{ '/foo/bar', 'bar' },
|
{ "/foo/bar", "bar" },
|
||||||
{ '/foo/bar/', 'bar' },
|
{ "/foo/bar/", "bar" },
|
||||||
{ '/', nil },
|
{ "/", nil },
|
||||||
{ '', nil },
|
{ "", nil },
|
||||||
}
|
}
|
||||||
for _, case in ipairs(cases) do
|
for _, case in ipairs(cases) do
|
||||||
local input, expected = unpack(case)
|
local input, expected = unpack(case)
|
||||||
|
|
@ -1,40 +1,41 @@
|
||||||
local TmpDir = require('spec.tmpdir')
|
require("plenary.async").tests.add_to_env()
|
||||||
local oil = require('oil')
|
local TmpDir = require("tests.tmpdir")
|
||||||
local test_util = require('spec.test_util')
|
local oil = require("oil")
|
||||||
local util = require('oil.util')
|
local test_util = require("tests.test_util")
|
||||||
|
local util = require("oil.util")
|
||||||
|
|
||||||
describe('oil preview', function()
|
a.describe("oil preview", function()
|
||||||
local tmpdir
|
local tmpdir
|
||||||
before_each(function()
|
a.before_each(function()
|
||||||
tmpdir = TmpDir.new()
|
tmpdir = TmpDir.new()
|
||||||
end)
|
end)
|
||||||
after_each(function()
|
a.after_each(function()
|
||||||
if tmpdir then
|
if tmpdir then
|
||||||
tmpdir:dispose()
|
tmpdir:dispose()
|
||||||
end
|
end
|
||||||
test_util.reset_editor()
|
test_util.reset_editor()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('opens preview window', function()
|
a.it("opens preview window", function()
|
||||||
tmpdir:create({ 'a.txt' })
|
tmpdir:create({ "a.txt" })
|
||||||
test_util.oil_open(tmpdir.path)
|
test_util.oil_open(tmpdir.path)
|
||||||
test_util.await(oil.open_preview, 2)
|
a.wrap(oil.open_preview, 2)()
|
||||||
local preview_win = util.get_preview_win()
|
local preview_win = util.get_preview_win()
|
||||||
assert.not_nil(preview_win)
|
assert.not_nil(preview_win)
|
||||||
assert(preview_win)
|
assert(preview_win)
|
||||||
local bufnr = vim.api.nvim_win_get_buf(preview_win)
|
local bufnr = vim.api.nvim_win_get_buf(preview_win)
|
||||||
local preview_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
local preview_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
assert.are.same({ 'a.txt' }, preview_lines)
|
assert.are.same({ "a.txt" }, preview_lines)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('opens preview window when open(preview={})', function()
|
a.it("opens preview window when open(preview={})", function()
|
||||||
tmpdir:create({ 'a.txt' })
|
tmpdir:create({ "a.txt" })
|
||||||
test_util.oil_open(tmpdir.path, { preview = {} })
|
test_util.oil_open(tmpdir.path, { preview = {} })
|
||||||
local preview_win = util.get_preview_win()
|
local preview_win = util.get_preview_win()
|
||||||
assert.not_nil(preview_win)
|
assert.not_nil(preview_win)
|
||||||
assert(preview_win)
|
assert(preview_win)
|
||||||
local bufnr = vim.api.nvim_win_get_buf(preview_win)
|
local bufnr = vim.api.nvim_win_get_buf(preview_win)
|
||||||
local preview_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
local preview_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
assert.are.same({ 'a.txt' }, preview_lines)
|
assert.are.same({ "a.txt" }, preview_lines)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
148
tests/regression_spec.lua
Normal file
148
tests/regression_spec.lua
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
require("plenary.async").tests.add_to_env()
|
||||||
|
local TmpDir = require("tests.tmpdir")
|
||||||
|
local actions = require("oil.actions")
|
||||||
|
local oil = require("oil")
|
||||||
|
local test_util = require("tests.test_util")
|
||||||
|
local view = require("oil.view")
|
||||||
|
|
||||||
|
a.describe("regression tests", function()
|
||||||
|
local tmpdir
|
||||||
|
a.before_each(function()
|
||||||
|
tmpdir = TmpDir.new()
|
||||||
|
end)
|
||||||
|
a.after_each(function()
|
||||||
|
if tmpdir then
|
||||||
|
tmpdir:dispose()
|
||||||
|
tmpdir = nil
|
||||||
|
end
|
||||||
|
test_util.reset_editor()
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- see https://github.com/stevearc/oil.nvim/issues/25
|
||||||
|
a.it("can edit dirs that will be renamed to an existing buffer", function()
|
||||||
|
vim.cmd.edit({ args = { "README.md" } })
|
||||||
|
vim.cmd.vsplit()
|
||||||
|
vim.cmd.edit({ args = { "%:p:h" } })
|
||||||
|
assert.equals("oil", vim.bo.filetype)
|
||||||
|
vim.cmd.wincmd({ args = { "p" } })
|
||||||
|
assert.equals("markdown", vim.bo.filetype)
|
||||||
|
vim.cmd.edit({ args = { "%:p:h" } })
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
assert.equals("oil", vim.bo.filetype)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- https://github.com/stevearc/oil.nvim/issues/37
|
||||||
|
a.it("places the cursor on correct entry when opening on file", function()
|
||||||
|
vim.cmd.edit({ args = { "." } })
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
local entry = oil.get_cursor_entry()
|
||||||
|
assert.not_nil(entry)
|
||||||
|
assert.not_equals("README.md", entry and entry.name)
|
||||||
|
vim.cmd.edit({ args = { "README.md" } })
|
||||||
|
view.delete_hidden_buffers()
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
entry = oil.get_cursor_entry()
|
||||||
|
assert.equals("README.md", entry and entry.name)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- https://github.com/stevearc/oil.nvim/issues/64
|
||||||
|
a.it("doesn't close floating windows oil didn't open itself", function()
|
||||||
|
local winid = vim.api.nvim_open_win(vim.fn.bufadd("README.md"), true, {
|
||||||
|
relative = "editor",
|
||||||
|
row = 1,
|
||||||
|
col = 1,
|
||||||
|
width = 100,
|
||||||
|
height = 100,
|
||||||
|
})
|
||||||
|
oil.open()
|
||||||
|
a.util.sleep(10)
|
||||||
|
oil.close()
|
||||||
|
a.util.sleep(10)
|
||||||
|
assert.equals(winid, vim.api.nvim_get_current_win())
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- https://github.com/stevearc/oil.nvim/issues/64
|
||||||
|
a.it("doesn't close splits on oil.close", function()
|
||||||
|
vim.cmd.edit({ args = { "README.md" } })
|
||||||
|
vim.cmd.vsplit()
|
||||||
|
local winid = vim.api.nvim_get_current_win()
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
oil.open()
|
||||||
|
a.util.sleep(10)
|
||||||
|
oil.close()
|
||||||
|
a.util.sleep(10)
|
||||||
|
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
|
||||||
|
assert.equals(winid, vim.api.nvim_get_current_win())
|
||||||
|
assert.equals(bufnr, vim.api.nvim_get_current_buf())
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- https://github.com/stevearc/oil.nvim/issues/79
|
||||||
|
a.it("Returns to empty buffer on close", function()
|
||||||
|
oil.open()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
oil.close()
|
||||||
|
assert.not_equals("oil", vim.bo.filetype)
|
||||||
|
assert.equals("", vim.api.nvim_buf_get_name(0))
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("All buffers set nomodified after save", function()
|
||||||
|
tmpdir:create({ "a.txt" })
|
||||||
|
a.util.scheduler()
|
||||||
|
vim.cmd.edit({ args = { "oil://" .. vim.fn.fnamemodify(tmpdir.path, ":p") } })
|
||||||
|
local first_dir = vim.api.nvim_get_current_buf()
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
test_util.feedkeys({ "dd", "itest/<esc>", "<CR>" }, 10)
|
||||||
|
vim.wait(1000, function()
|
||||||
|
return vim.bo.modifiable
|
||||||
|
end, 10)
|
||||||
|
test_util.feedkeys({ "p" }, 10)
|
||||||
|
a.util.scheduler()
|
||||||
|
oil.save({ confirm = false })
|
||||||
|
vim.wait(1000, function()
|
||||||
|
return vim.bo.modifiable
|
||||||
|
end, 10)
|
||||||
|
tmpdir:assert_fs({
|
||||||
|
["test/a.txt"] = "a.txt",
|
||||||
|
})
|
||||||
|
-- The first oil buffer should not be modified anymore
|
||||||
|
assert.falsy(vim.bo[first_dir].modified)
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("refreshing buffer doesn't lose track of it", function()
|
||||||
|
vim.cmd.edit({ args = { "." } })
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
vim.cmd.edit({ bang = true })
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
assert.are.same({ bufnr }, require("oil.view").get_all_buffers())
|
||||||
|
end)
|
||||||
|
|
||||||
|
a.it("can copy a file multiple times", function()
|
||||||
|
test_util.actions.open({ tmpdir.path })
|
||||||
|
vim.api.nvim_feedkeys("ifoo.txt", "x", true)
|
||||||
|
test_util.actions.save()
|
||||||
|
vim.api.nvim_feedkeys("yyp$ciWbar.txt", "x", true)
|
||||||
|
vim.api.nvim_feedkeys("yyp$ciWbaz.txt", "x", true)
|
||||||
|
test_util.actions.save()
|
||||||
|
assert.are.same({ "bar.txt", "baz.txt", "foo.txt" }, test_util.parse_entries(0))
|
||||||
|
tmpdir:assert_fs({
|
||||||
|
["foo.txt"] = "",
|
||||||
|
["bar.txt"] = "",
|
||||||
|
["baz.txt"] = "",
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- https://github.com/stevearc/oil.nvim/issues/355
|
||||||
|
a.it("can open files from floating window", function()
|
||||||
|
tmpdir:create({ "a.txt" })
|
||||||
|
a.util.scheduler()
|
||||||
|
oil.open_float(tmpdir.path)
|
||||||
|
test_util.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
|
actions.select.callback()
|
||||||
|
vim.wait(1000, function()
|
||||||
|
return vim.fn.expand("%:t") == "a.txt"
|
||||||
|
end, 10)
|
||||||
|
assert.equals("a.txt", vim.fn.expand("%:t"))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
@ -1,85 +1,90 @@
|
||||||
local oil = require('oil')
|
require("plenary.async").tests.add_to_env()
|
||||||
local test_util = require('spec.test_util')
|
local oil = require("oil")
|
||||||
|
local test_util = require("tests.test_util")
|
||||||
|
|
||||||
describe('oil select', function()
|
a.describe("oil select", function()
|
||||||
after_each(function()
|
after_each(function()
|
||||||
test_util.reset_editor()
|
test_util.reset_editor()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('opens file under cursor', function()
|
a.it("opens file under cursor", function()
|
||||||
test_util.oil_open()
|
test_util.oil_open()
|
||||||
vim.cmd.normal({ args = { 'G' } })
|
-- Go to the bottom, so the cursor is not on a directory
|
||||||
test_util.await(oil.select, 2)
|
vim.cmd.normal({ args = { "G" } })
|
||||||
|
a.wrap(oil.select, 2)()
|
||||||
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
||||||
assert.not_equals('oil', vim.bo.filetype)
|
assert.not_equals("oil", vim.bo.filetype)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('opens file in new tab', function()
|
a.it("opens file in new tab", function()
|
||||||
test_util.oil_open()
|
test_util.oil_open()
|
||||||
local tabpage = vim.api.nvim_get_current_tabpage()
|
local tabpage = vim.api.nvim_get_current_tabpage()
|
||||||
test_util.await(oil.select, 2, { tab = true })
|
a.wrap(oil.select, 2)({ tab = true })
|
||||||
assert.equals(2, #vim.api.nvim_list_tabpages())
|
assert.equals(2, #vim.api.nvim_list_tabpages())
|
||||||
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
||||||
assert.not_equals(tabpage, vim.api.nvim_get_current_tabpage())
|
assert.not_equals(tabpage, vim.api.nvim_get_current_tabpage())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('opens file in new split', function()
|
a.it("opens file in new split", function()
|
||||||
test_util.oil_open()
|
test_util.oil_open()
|
||||||
local winid = vim.api.nvim_get_current_win()
|
local winid = vim.api.nvim_get_current_win()
|
||||||
test_util.await(oil.select, 2, { vertical = true })
|
a.wrap(oil.select, 2)({ vertical = true })
|
||||||
assert.equals(1, #vim.api.nvim_list_tabpages())
|
assert.equals(1, #vim.api.nvim_list_tabpages())
|
||||||
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
|
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
|
||||||
assert.not_equals(winid, vim.api.nvim_get_current_win())
|
assert.not_equals(winid, vim.api.nvim_get_current_win())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('opens multiple files in new tabs', function()
|
a.it("opens multiple files in new tabs", function()
|
||||||
test_util.oil_open()
|
test_util.oil_open()
|
||||||
vim.api.nvim_feedkeys('Vj', 'x', true)
|
vim.api.nvim_feedkeys("Vj", "x", true)
|
||||||
local tabpage = vim.api.nvim_get_current_tabpage()
|
local tabpage = vim.api.nvim_get_current_tabpage()
|
||||||
test_util.await(oil.select, 2, { tab = true })
|
a.wrap(oil.select, 2)({ tab = true })
|
||||||
assert.equals(3, #vim.api.nvim_list_tabpages())
|
assert.equals(3, #vim.api.nvim_list_tabpages())
|
||||||
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
||||||
assert.not_equals(tabpage, vim.api.nvim_get_current_tabpage())
|
assert.not_equals(tabpage, vim.api.nvim_get_current_tabpage())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('opens multiple files in new splits', function()
|
a.it("opens multiple files in new splits", function()
|
||||||
test_util.oil_open()
|
test_util.oil_open()
|
||||||
vim.api.nvim_feedkeys('Vj', 'x', true)
|
vim.api.nvim_feedkeys("Vj", "x", true)
|
||||||
local winid = vim.api.nvim_get_current_win()
|
local winid = vim.api.nvim_get_current_win()
|
||||||
test_util.await(oil.select, 2, { vertical = true })
|
a.wrap(oil.select, 2)({ vertical = true })
|
||||||
assert.equals(1, #vim.api.nvim_list_tabpages())
|
assert.equals(1, #vim.api.nvim_list_tabpages())
|
||||||
assert.equals(3, #vim.api.nvim_tabpage_list_wins(0))
|
assert.equals(3, #vim.api.nvim_tabpage_list_wins(0))
|
||||||
assert.not_equals(winid, vim.api.nvim_get_current_win())
|
assert.not_equals(winid, vim.api.nvim_get_current_win())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('close after open', function()
|
a.describe("close after open", function()
|
||||||
it('same window', function()
|
a.it("same window", function()
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
test_util.oil_open()
|
test_util.oil_open()
|
||||||
vim.cmd.normal({ args = { 'G' } })
|
-- Go to the bottom, so the cursor is not on a directory
|
||||||
test_util.await(oil.select, 2, { close = true })
|
vim.cmd.normal({ args = { "G" } })
|
||||||
|
a.wrap(oil.select, 2)({ close = true })
|
||||||
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
||||||
|
-- This one we actually don't expect the buffer to be the same as the initial buffer, because
|
||||||
|
-- we opened a file
|
||||||
assert.not_equals(bufnr, vim.api.nvim_get_current_buf())
|
assert.not_equals(bufnr, vim.api.nvim_get_current_buf())
|
||||||
assert.not_equals('oil', vim.bo.filetype)
|
assert.not_equals("oil", vim.bo.filetype)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('split', function()
|
a.it("split", function()
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
local winid = vim.api.nvim_get_current_win()
|
local winid = vim.api.nvim_get_current_win()
|
||||||
test_util.oil_open()
|
test_util.oil_open()
|
||||||
test_util.await(oil.select, 2, { vertical = true, close = true })
|
a.wrap(oil.select, 2)({ vertical = true, close = true })
|
||||||
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
|
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
|
||||||
assert.equals(bufnr, vim.api.nvim_win_get_buf(winid))
|
assert.equals(bufnr, vim.api.nvim_win_get_buf(winid))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('tab', function()
|
a.it("tab", function()
|
||||||
vim.cmd.edit({ args = { 'foo' } })
|
vim.cmd.edit({ args = { "foo" } })
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
local tabpage = vim.api.nvim_get_current_tabpage()
|
local tabpage = vim.api.nvim_get_current_tabpage()
|
||||||
test_util.oil_open()
|
test_util.oil_open()
|
||||||
test_util.await(oil.select, 2, { tab = true, close = true })
|
a.wrap(oil.select, 2)({ tab = true, close = true })
|
||||||
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
||||||
assert.equals(2, #vim.api.nvim_list_tabpages())
|
assert.equals(2, #vim.api.nvim_list_tabpages())
|
||||||
vim.api.nvim_set_current_tabpage(tabpage)
|
vim.api.nvim_set_current_tabpage(tabpage)
|
||||||
|
|
@ -1,19 +1,24 @@
|
||||||
local cache = require('oil.cache')
|
require("plenary.async").tests.add_to_env()
|
||||||
local test_adapter = require('oil.adapters.test')
|
local cache = require("oil.cache")
|
||||||
local util = require('oil.util')
|
local test_adapter = require("oil.adapters.test")
|
||||||
|
local util = require("oil.util")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
M.reset_editor = function()
|
M.reset_editor = function()
|
||||||
require('oil').setup({
|
require("oil").setup({
|
||||||
columms = {},
|
columms = {},
|
||||||
adapters = {
|
adapters = {
|
||||||
['oil-test://'] = 'test',
|
["oil-test://"] = "test",
|
||||||
},
|
},
|
||||||
prompt_save_on_select_new_entry = false,
|
prompt_save_on_select_new_entry = false,
|
||||||
})
|
})
|
||||||
vim.cmd.tabonly({ mods = { silent = true } })
|
vim.cmd.tabonly({ mods = { silent = true } })
|
||||||
vim.cmd.new()
|
for i, winid in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
|
||||||
vim.cmd.only()
|
if i > 1 then
|
||||||
|
vim.api.nvim_win_close(winid, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
vim.api.nvim_win_set_buf(0, vim.api.nvim_create_buf(false, true))
|
||||||
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
|
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
|
||||||
vim.api.nvim_buf_delete(bufnr, { force = true })
|
vim.api.nvim_buf_delete(bufnr, { force = true })
|
||||||
end
|
end
|
||||||
|
|
@ -29,87 +34,49 @@ local function throwiferr(err, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
M.await = function(fn, nargs, ...)
|
|
||||||
local done = false
|
|
||||||
local results
|
|
||||||
local n_results = 0
|
|
||||||
local args = { ... }
|
|
||||||
args[nargs] = function(...)
|
|
||||||
results = { ... }
|
|
||||||
n_results = select('#', ...)
|
|
||||||
done = true
|
|
||||||
end
|
|
||||||
fn(unpack(args, 1, nargs))
|
|
||||||
vim.wait(10000, function()
|
|
||||||
return done
|
|
||||||
end, 10)
|
|
||||||
if not done then
|
|
||||||
error('M.await timed out')
|
|
||||||
end
|
|
||||||
return unpack(results, 1, n_results)
|
|
||||||
end
|
|
||||||
|
|
||||||
M.await_throwiferr = function(fn, nargs, ...)
|
|
||||||
return throwiferr(M.await(fn, nargs, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
M.oil_open = function(...)
|
M.oil_open = function(...)
|
||||||
M.await(require('oil').open, 3, ...)
|
a.wrap(require("oil").open, 3)(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
M.wait_for_autocmd = function(autocmd)
|
M.await = function(fn, nargs, ...)
|
||||||
local triggered = false
|
return throwiferr(a.wrap(fn, nargs)(...))
|
||||||
|
end
|
||||||
|
|
||||||
|
M.wait_for_autocmd = a.wrap(function(autocmd, cb)
|
||||||
local opts = {
|
local opts = {
|
||||||
pattern = '*',
|
pattern = "*",
|
||||||
nested = true,
|
nested = true,
|
||||||
once = true,
|
once = true,
|
||||||
}
|
}
|
||||||
if type(autocmd) == 'table' then
|
if type(autocmd) == "table" then
|
||||||
opts = vim.tbl_extend('force', opts, autocmd)
|
opts = vim.tbl_extend("force", opts, autocmd)
|
||||||
autocmd = autocmd[1]
|
autocmd = autocmd[1]
|
||||||
opts[1] = nil
|
opts[1] = nil
|
||||||
end
|
end
|
||||||
opts.callback = vim.schedule_wrap(function()
|
opts.callback = vim.schedule_wrap(cb)
|
||||||
triggered = true
|
|
||||||
end)
|
|
||||||
vim.api.nvim_create_autocmd(autocmd, opts)
|
|
||||||
vim.wait(10000, function()
|
|
||||||
return triggered
|
|
||||||
end, 10)
|
|
||||||
if not triggered then
|
|
||||||
error('wait_for_autocmd timed out waiting for ' .. tostring(autocmd))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
M.wait_oil_ready = function()
|
vim.api.nvim_create_autocmd(autocmd, opts)
|
||||||
local ready = false
|
end, 2)
|
||||||
util.run_after_load(
|
|
||||||
0,
|
M.wait_oil_ready = a.wrap(function(cb)
|
||||||
vim.schedule_wrap(function()
|
util.run_after_load(0, vim.schedule_wrap(cb))
|
||||||
ready = true
|
end, 1)
|
||||||
end)
|
|
||||||
)
|
|
||||||
vim.wait(10000, function()
|
|
||||||
return ready
|
|
||||||
end, 10)
|
|
||||||
if not ready then
|
|
||||||
error('wait_oil_ready timed out')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param actions string[]
|
---@param actions string[]
|
||||||
---@param timestep integer
|
---@param timestep integer
|
||||||
M.feedkeys = function(actions, timestep)
|
M.feedkeys = function(actions, timestep)
|
||||||
timestep = timestep or 10
|
timestep = timestep or 10
|
||||||
vim.wait(timestep)
|
a.util.sleep(timestep)
|
||||||
for _, action in ipairs(actions) do
|
for _, action in ipairs(actions) do
|
||||||
vim.wait(timestep)
|
a.util.sleep(timestep)
|
||||||
local escaped = vim.api.nvim_replace_termcodes(action, true, false, true)
|
local escaped = vim.api.nvim_replace_termcodes(action, true, false, true)
|
||||||
vim.api.nvim_feedkeys(escaped, 'm', true)
|
vim.api.nvim_feedkeys(escaped, "m", true)
|
||||||
end
|
end
|
||||||
vim.wait(timestep)
|
a.util.sleep(timestep)
|
||||||
vim.api.nvim_feedkeys('', 'x', true)
|
-- process pending keys until the queue is empty.
|
||||||
vim.wait(timestep)
|
-- Note that this will exit insert mode.
|
||||||
|
vim.api.nvim_feedkeys("", "x", true)
|
||||||
|
a.util.sleep(timestep)
|
||||||
end
|
end
|
||||||
|
|
||||||
M.actions = {
|
M.actions = {
|
||||||
|
|
@ -118,40 +85,41 @@ M.actions = {
|
||||||
open = function(args)
|
open = function(args)
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
vim.cmd.Oil({ args = args })
|
vim.cmd.Oil({ args = args })
|
||||||
|
-- If this buffer was already open, manually dispatch the autocmd to finish the wait
|
||||||
if vim.b.oil_ready then
|
if vim.b.oil_ready then
|
||||||
vim.api.nvim_exec_autocmds('User', {
|
vim.api.nvim_exec_autocmds("User", {
|
||||||
pattern = 'OilEnter',
|
pattern = "OilEnter",
|
||||||
modeline = false,
|
modeline = false,
|
||||||
data = { buf = vim.api.nvim_get_current_buf() },
|
data = { buf = vim.api.nvim_get_current_buf() },
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
M.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
M.wait_for_autocmd({ "User", pattern = "OilEnter" })
|
||||||
end,
|
end,
|
||||||
|
|
||||||
---Save all changes and wait for operation to complete
|
---Save all changes and wait for operation to complete
|
||||||
save = function()
|
save = function()
|
||||||
vim.schedule_wrap(require('oil').save)({ confirm = false })
|
vim.schedule_wrap(require("oil").save)({ confirm = false })
|
||||||
M.wait_for_autocmd({ 'User', pattern = 'OilMutationComplete' })
|
M.wait_for_autocmd({ "User", pattern = "OilMutationComplete" })
|
||||||
end,
|
end,
|
||||||
|
|
||||||
---@param bufnr? integer
|
---@param bufnr? integer
|
||||||
reload = function(bufnr)
|
reload = function(bufnr)
|
||||||
M.await(require('oil.view').render_buffer_async, 3, bufnr or 0)
|
M.await(require("oil.view").render_buffer_async, 3, bufnr or 0)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
---Move cursor to a file or directory in an oil buffer
|
---Move cursor to a file or directory in an oil buffer
|
||||||
---@param filename string
|
---@param filename string
|
||||||
focus = function(filename)
|
focus = function(filename)
|
||||||
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, true)
|
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, true)
|
||||||
local search = ' ' .. filename .. '$'
|
local search = " " .. filename .. "$"
|
||||||
for i, line in ipairs(lines) do
|
for i, line in ipairs(lines) do
|
||||||
if line:match(search) then
|
if line:match(search) then
|
||||||
vim.api.nvim_win_set_cursor(0, { i, 0 })
|
vim.api.nvim_win_set_cursor(0, { i, 0 })
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
error('Could not find file ' .. filename)
|
error("Could not find file " .. filename)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,7 +133,7 @@ M.parse_entries = function(bufnr)
|
||||||
end
|
end
|
||||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
|
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
|
||||||
return vim.tbl_map(function(line)
|
return vim.tbl_map(function(line)
|
||||||
return line:match('^/%d+ +(.+)$')
|
return line:match("^/%d+ +(.+)$")
|
||||||
end, lines)
|
end, lines)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
local fs = require('oil.fs')
|
local fs = require("oil.fs")
|
||||||
local test_util = require('spec.test_util')
|
local test_util = require("tests.test_util")
|
||||||
|
|
||||||
|
local await = test_util.await
|
||||||
|
|
||||||
---@param path string
|
---@param path string
|
||||||
local function touch(path)
|
---@param cb fun(err: nil|string)
|
||||||
local fd, open_err = vim.loop.fs_open(path, 'w', 420) -- 0644
|
local function touch(path, cb)
|
||||||
if not fd then
|
vim.loop.fs_open(path, "w", 420, function(err, fd) -- 0644
|
||||||
error(open_err)
|
if err then
|
||||||
end
|
cb(err)
|
||||||
local shortpath = path:gsub('^[^' .. fs.sep .. ']*' .. fs.sep, '')
|
else
|
||||||
local _, write_err = vim.loop.fs_write(fd, shortpath)
|
local shortpath = path:gsub("^[^" .. fs.sep .. "]*" .. fs.sep, "")
|
||||||
if write_err then
|
vim.loop.fs_write(fd, shortpath, nil, function(err2)
|
||||||
error(write_err)
|
if err2 then
|
||||||
end
|
cb(err2)
|
||||||
vim.loop.fs_close(fd)
|
else
|
||||||
|
vim.loop.fs_close(fd, cb)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param filepath string
|
---@param filepath string
|
||||||
|
|
@ -21,14 +28,11 @@ local function exists(filepath)
|
||||||
local stat = vim.loop.fs_stat(filepath)
|
local stat = vim.loop.fs_stat(filepath)
|
||||||
return stat ~= nil and stat.type ~= nil
|
return stat ~= nil and stat.type ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local TmpDir = {}
|
local TmpDir = {}
|
||||||
|
|
||||||
TmpDir.new = function()
|
TmpDir.new = function()
|
||||||
local path, err = vim.loop.fs_mkdtemp('oil_test_XXXXXXXXX')
|
local path = await(vim.loop.fs_mkdtemp, 2, "oil_test_XXXXXXXXX")
|
||||||
if not path then
|
a.util.scheduler()
|
||||||
error(err)
|
|
||||||
end
|
|
||||||
return setmetatable({ path = path }, {
|
return setmetatable({ path = path }, {
|
||||||
__index = TmpDir,
|
__index = TmpDir,
|
||||||
})
|
})
|
||||||
|
|
@ -42,28 +46,31 @@ function TmpDir:create(paths)
|
||||||
for i, piece in ipairs(pieces) do
|
for i, piece in ipairs(pieces) do
|
||||||
partial_path = fs.join(partial_path, piece)
|
partial_path = fs.join(partial_path, piece)
|
||||||
if i == #pieces and not vim.endswith(partial_path, fs.sep) then
|
if i == #pieces and not vim.endswith(partial_path, fs.sep) then
|
||||||
touch(partial_path)
|
await(touch, 2, partial_path)
|
||||||
elseif not exists(partial_path) then
|
elseif not exists(partial_path) then
|
||||||
vim.loop.fs_mkdir(partial_path, 493)
|
vim.loop.fs_mkdir(partial_path, 493)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
a.util.scheduler()
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param filepath string
|
---@param filepath string
|
||||||
---@return string?
|
---@return string?
|
||||||
local read_file = function(filepath)
|
local read_file = function(filepath)
|
||||||
local fd = vim.loop.fs_open(filepath, 'r', 420)
|
local fd = vim.loop.fs_open(filepath, "r", 420)
|
||||||
if not fd then
|
if not fd then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
local stat = vim.loop.fs_fstat(fd)
|
local stat = vim.loop.fs_fstat(fd)
|
||||||
local content = vim.loop.fs_read(fd, stat.size)
|
local content = vim.loop.fs_read(fd, stat.size)
|
||||||
vim.loop.fs_close(fd)
|
vim.loop.fs_close(fd)
|
||||||
|
a.util.scheduler()
|
||||||
return content
|
return content
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param dir string
|
---@param dir string
|
||||||
|
---@param cb fun(err: nil|string, entry: {type: oil.EntryType, name: string, root: string}
|
||||||
local function walk(dir)
|
local function walk(dir)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
for name, type in vim.fs.dir(dir) do
|
for name, type in vim.fs.dir(dir) do
|
||||||
|
|
@ -72,7 +79,7 @@ local function walk(dir)
|
||||||
type = type,
|
type = type,
|
||||||
root = dir,
|
root = dir,
|
||||||
})
|
})
|
||||||
if type == 'directory' then
|
if type == "directory" then
|
||||||
vim.list_extend(ret, walk(fs.join(dir, name)))
|
vim.list_extend(ret, walk(fs.join(dir, name)))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -83,10 +90,10 @@ end
|
||||||
local assert_fs = function(root, paths)
|
local assert_fs = function(root, paths)
|
||||||
local unlisted_dirs = {}
|
local unlisted_dirs = {}
|
||||||
for k in pairs(paths) do
|
for k in pairs(paths) do
|
||||||
local pieces = vim.split(k, '/')
|
local pieces = vim.split(k, "/")
|
||||||
local partial_path = ''
|
local partial_path = ""
|
||||||
for i, piece in ipairs(pieces) do
|
for i, piece in ipairs(pieces) do
|
||||||
partial_path = partial_path .. piece .. '/'
|
partial_path = partial_path .. piece .. "/"
|
||||||
if i ~= #pieces then
|
if i ~= #pieces then
|
||||||
unlisted_dirs[partial_path] = true
|
unlisted_dirs[partial_path] = true
|
||||||
end
|
end
|
||||||
|
|
@ -100,16 +107,17 @@ local assert_fs = function(root, paths)
|
||||||
for _, entry in ipairs(entries) do
|
for _, entry in ipairs(entries) do
|
||||||
local fullpath = fs.join(entry.root, entry.name)
|
local fullpath = fs.join(entry.root, entry.name)
|
||||||
local shortpath = fullpath:sub(root:len() + 2)
|
local shortpath = fullpath:sub(root:len() + 2)
|
||||||
if entry.type == 'directory' then
|
if entry.type == "directory" then
|
||||||
shortpath = shortpath .. '/'
|
shortpath = shortpath .. "/"
|
||||||
end
|
end
|
||||||
local expected_content = paths[shortpath]
|
local expected_content = paths[shortpath]
|
||||||
paths[shortpath] = nil
|
paths[shortpath] = nil
|
||||||
assert(expected_content, string.format("Unexpected entry '%s'", shortpath))
|
assert.truthy(expected_content, string.format("Unexpected entry '%s'", shortpath))
|
||||||
if entry.type == 'file' then
|
if entry.type == "file" then
|
||||||
local data = read_file(fullpath)
|
local data = read_file(fullpath)
|
||||||
assert(
|
assert.equals(
|
||||||
expected_content == data,
|
expected_content,
|
||||||
|
data,
|
||||||
string.format(
|
string.format(
|
||||||
"File '%s' expected content '%s' received '%s'",
|
"File '%s' expected content '%s' received '%s'",
|
||||||
shortpath,
|
shortpath,
|
||||||
|
|
@ -121,11 +129,11 @@ local assert_fs = function(root, paths)
|
||||||
end
|
end
|
||||||
|
|
||||||
for k, v in pairs(paths) do
|
for k, v in pairs(paths) do
|
||||||
assert(
|
assert.falsy(
|
||||||
not k,
|
k,
|
||||||
string.format(
|
string.format(
|
||||||
"Expected %s '%s', but it was not found",
|
"Expected %s '%s', but it was not found",
|
||||||
v == true and 'directory' or 'file',
|
v == true and "directory" or "file",
|
||||||
k
|
k
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -134,23 +142,27 @@ end
|
||||||
|
|
||||||
---@param paths table<string, string>
|
---@param paths table<string, string>
|
||||||
function TmpDir:assert_fs(paths)
|
function TmpDir:assert_fs(paths)
|
||||||
|
a.util.scheduler()
|
||||||
assert_fs(self.path, paths)
|
assert_fs(self.path, paths)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TmpDir:assert_exists(path)
|
function TmpDir:assert_exists(path)
|
||||||
|
a.util.scheduler()
|
||||||
path = fs.join(self.path, path)
|
path = fs.join(self.path, path)
|
||||||
local stat = vim.loop.fs_stat(path)
|
local stat = vim.loop.fs_stat(path)
|
||||||
assert(stat, string.format("Expected path '%s' to exist", path))
|
assert.truthy(stat, string.format("Expected path '%s' to exist", path))
|
||||||
end
|
end
|
||||||
|
|
||||||
function TmpDir:assert_not_exists(path)
|
function TmpDir:assert_not_exists(path)
|
||||||
|
a.util.scheduler()
|
||||||
path = fs.join(self.path, path)
|
path = fs.join(self.path, path)
|
||||||
local stat = vim.loop.fs_stat(path)
|
local stat = vim.loop.fs_stat(path)
|
||||||
assert(not stat, string.format("Expected path '%s' to not exist", path))
|
assert.falsy(stat, string.format("Expected path '%s' to not exist", path))
|
||||||
end
|
end
|
||||||
|
|
||||||
function TmpDir:dispose()
|
function TmpDir:dispose()
|
||||||
test_util.await_throwiferr(fs.recursive_delete, 3, 'directory', self.path)
|
await(fs.recursive_delete, 3, "directory", self.path)
|
||||||
|
a.util.scheduler()
|
||||||
end
|
end
|
||||||
|
|
||||||
return TmpDir
|
return TmpDir
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue