diff --git a/.busted b/.busted new file mode 100644 index 0000000..f729b3e --- /dev/null +++ b/.busted @@ -0,0 +1,9 @@ +return { + _all = { + lua = "nlua", + }, + default = { + ROOT = { "./spec/" }, + helper = "spec/minimal_init.lua", + }, +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b9de190 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +insert_final_newline = true +charset = utf-8 + +[*.lua] +indent_style = space +indent_size = 2 diff --git a/.envrc b/.envrc deleted file mode 100644 index d522e34..0000000 --- a/.envrc +++ /dev/null @@ -1,3 +0,0 @@ -export VIRTUAL_ENV=venv -layout python -python -c 'import pyparsing' 2>/dev/null || pip install -r scripts/requirements.txt diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 0000000..6447534 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,80 @@ +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/oil.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/oil.nvim', + init = function() + vim.g.oil = {} + end, + }, + }, + }) + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index 327aaf2..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,131 +0,0 @@ -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 diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml new file mode 100644 index 0000000..ce81bef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Questions + url: https://github.com/barrettruth/oil.nvim/discussions + about: Ask questions and discuss ideas diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 0000000..6515953 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -0,0 +1,30 @@ +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/oil.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 diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index 9916836..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,43 +0,0 @@ -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 diff --git a/.github/workflows/automation_remove_question_label_on_comment.yml b/.github/workflows/automation_remove_question_label_on_comment.yml index f99bba8..36f9336 100644 --- a/.github/workflows/automation_remove_question_label_on_comment.yml +++ b/.github/workflows/automation_remove_question_label_on_comment.yml @@ -8,7 +8,7 @@ jobs: # issues in my "needs triage" filter. remove_question: runs-on: ubuntu-latest - if: github.event.sender.login != 'stevearc' + if: github.event.sender.login != 'barrettruth' steps: - uses: actions/checkout@v4 - uses: actions-ecosystem/action-remove-labels@v1 diff --git a/.github/workflows/install_nvim.sh b/.github/workflows/install_nvim.sh deleted file mode 100644 index 9ba2d26..0000000 --- a/.github/workflows/install_nvim.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/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 diff --git a/.github/workflows/luarocks.yaml b/.github/workflows/luarocks.yaml index 4dc366c..9b6664e 100644 --- a/.github/workflows/luarocks.yaml +++ b/.github/workflows/luarocks.yaml @@ -7,7 +7,7 @@ on: jobs: quality: - uses: ./.github/workflows/tests.yml + uses: ./.github/workflows/quality.yaml publish: needs: quality diff --git a/.github/workflows/mirror_upstream_prs.yml b/.github/workflows/mirror_upstream_prs.yml deleted file mode 100644 index af9cdb0..0000000 --- a/.github/workflows/mirror_upstream_prs.yml +++ /dev/null @@ -1,85 +0,0 @@ -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}`); - } diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml new file mode 100644 index 0000000..9691be9 --- /dev/null +++ b/.github/workflows/quality.yaml @@ -0,0 +1,90 @@ +name: quality + +on: + workflow_call: + pull_request: + branches: [main] + push: + branches: [main] + +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' + 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: JohnnyMorganz/stylua-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: 2.1.0 + args: --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 + - name: Lint with Selene + uses: NTBBloodbath/selene-action@v1.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --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 + - name: Run Lua LS Type Check + uses: mrcjkb/lua-typecheck-action@v0 + with: + checklevel: Warning + directories: lua + configpath: .luarc.json + + markdown-format: + name: Markdown Format Check + runs-on: ubuntu-latest + needs: changes + if: ${{ needs.changes.outputs.markdown == 'true' }} + steps: + - uses: actions/checkout@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Install prettier + run: pnpm add -g prettier@3.1.0 + - name: Check markdown formatting with prettier + run: prettier --check . diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..6910389 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,22 @@ +name: test + +on: + pull_request: + branches: [main] + push: + branches: [main] + +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 }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 462bb8f..0000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Tests - -on: - workflow_call: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - luacheck: - name: Luacheck - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - - name: Prepare - run: | - sudo apt-get update - sudo add-apt-repository universe - sudo apt install luarocks -y - sudo luarocks install luacheck - - - name: Run Luacheck - run: luacheck lua tests - - 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 diff --git a/.gitignore b/.gitignore index c3b7755..4a27603 100644 --- a/.gitignore +++ b/.gitignore @@ -1,48 +1,12 @@ -# Compiled Lua sources -luac.out - -# luarocks build files -*.src.rock -*.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/ -.testenv/ doc/tags -scripts/nvim-typecheck-action -scripts/benchmark.nvim +*.log +.*cache* +CLAUDE.md +.claude/ +node_modules/ +.direnv/ +.envrc +venv/ perf/tmp/ +scripts/benchmark.nvim profile.json diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29..0000000 diff --git a/.luacheckrc b/.luacheckrc deleted file mode 100644 index 7efefde..0000000 --- a/.luacheckrc +++ /dev/null @@ -1,19 +0,0 @@ -max_comment_line_length = false -codes = true - -exclude_files = { - "tests/treesitter", -} - -ignore = { - "212", -- Unused argument - "631", -- Line is too long - "122", -- Setting a readonly global - "542", -- Empty if branch -} - -read_globals = { - "vim", - "a", - "assert", -} diff --git a/.luarc.json b/.luarc.json index 68da2f2..f22884c 100644 --- a/.luarc.json +++ b/.luarc.json @@ -1,9 +1,8 @@ { - "runtime": { - "version": "LuaJIT", - "pathStrict": true - }, - "type": { - "checkTableShape": true - } + "runtime.version": "Lua 5.1", + "runtime.path": ["lua/?.lua", "lua/?/init.lua"], + "diagnostics.globals": ["vim", "jit", "bit"], + "workspace.library": ["$VIMRUNTIME/lua", "${3rd}/luv/library"], + "workspace.checkThirdParty": false, + "completion.callSnippet": "Replace" } diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5d1f13f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,17 @@ +minimum_pre_commit_version: '3.5.0' + +repos: + - repo: https://github.com/JohnnyMorganz/StyLua + rev: v2.3.1 + hooks: + - id: stylua-github + name: stylua (Lua formatter) + files: \.lua$ + pass_filenames: true + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v4.0.0-alpha.8 + hooks: + - id: prettier + name: prettier + files: \.(md|toml|yaml|yml|sh)$ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0663621 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "proseWrap": "always", + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "trailingComma": "none", + "semi": false, + "singleQuote": true +} diff --git a/.stylua.toml b/.stylua.toml index 020ce91..01ded03 100644 --- a/.stylua.toml +++ b/.stylua.toml @@ -1,5 +1,8 @@ column_width = 100 +line_endings = "Unix" indent_type = "Spaces" indent_width = 2 +quote_style = "AutoPreferSingle" +call_parentheses = "Always" [sort_requires] enabled = true diff --git a/LICENSE b/LICENSE index 4928e96..ccff038 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2022 Steven Arcangeli +Copyright (c) 2025 Barrett Ruth Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile deleted file mode 100644 index 897b281..0000000 --- a/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -## 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 - luacheck lua tests --formatter plain - 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 venv .testenv perf/tmp profile.json diff --git a/README.md b/README.md index 3792191..d14705c 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,37 @@ # oil.nvim -A [vim-vinegar](https://github.com/tpope/vim-vinegar) like file explorer that lets you edit your filesystem like a normal Neovim buffer. +**A file explorer that lets you edit your filesystem like a buffer** + +Browse directories as normal Neovim buffers, then create, rename, move, copy, +and delete files by editing the listing and saving. Cross-directory operations +work seamlessly across local, SSH, S3, and trash adapters. https://user-images.githubusercontent.com/506791/209727111-6b4a11f4-634a-4efa-9461-80e9717cea94.mp4 -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. +## Features -
-Changes from upstream - -### 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 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 (`` = ``) | [`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 | - -
+- Edit directory listings as normal buffers — mutations are derived by diffing +- Cross-directory move, copy, and rename across any adapter +- Adapters for local filesystem, SSH, S3, and OS trash +- File preview in split or floating window +- Configurable columns (icon, size, permissions, timestamps) +- Executable file highlighting and filetype-aware icons +- Floating window and split layouts ## Requirements -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. +- Neovim 0.10+ +- Optional: + [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 ## Installation -Install with your favorite package manager or with luarocks: +Install with your package manager of choice or via +[luarocks](https://luarocks.org/modules/barrettruth/oil.nvim): -```console +``` luarocks install oil.nvim ``` @@ -80,66 +43,53 @@ luarocks install oil.nvim ## FAQ -**Q: How do I migrate from `stevearc/oil.nvim` to `barrettruth/oil.nvim`?** - -Replace your `setup()` call with a `vim.g.oil` assignment. For example, with -[lazy.nvim](https://github.com/folke/lazy.nvim): +**Q: How do I set up oil.nvim with lazy.nvim?** ```lua --- before -{ - 'stevearc/oil.nvim', - config = function(_, opts) - require('oil').setup(opts) - end, - opts = { - ... - } -} - --- after { 'barrettruth/oil.nvim', init = function() vim.g.oil = { - columns = { "icon", "size" }, + columns = { 'icon', 'size' }, delete_to_trash = true, } end, } ``` +Do not use `config`, `opts`, or `lazy` — oil.nvim loads itself when you open a +directory. + +**Q: How do I migrate from stevearc/oil.nvim?** + +Replace `stevearc/oil.nvim` with `barrettruth/oil.nvim` in your plugin manager +and switch your `setup()` call to a `vim.g.oil` assignment in `init`. The +configuration table is the same. + **Q: Why "oil"?** -**A:** From the [vim-vinegar](https://github.com/tpope/vim-vinegar) README, a quote by Drew Neil: +From the [vim-vinegar](https://github.com/tpope/vim-vinegar) README, a quote by +Drew Neil: > 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?** -**A:** +- [stevearc/oil.nvim](https://github.com/stevearc/oil.nvim): the original + oil.nvim +- [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 -- [the original](https://github.com/stevearc/oil.nvim): the lesser-maintained but - official `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. -- [vim-vinegar](https://github.com/tpope/vim-vinegar): The granddaddy of single-directory file browsing. -- [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. +## Acknowledgements + +oil.nvim was created by +[Steven Arcangeli](https://github.com/stevearc/oil.nvim). This fork is +maintained by [Barrett Ruth](https://github.com/barrettruth). diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..7501bfa --- /dev/null +++ b/flake.lock @@ -0,0 +1,43 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1771207753, + "narHash": "sha256-b9uG8yN50DRQ6A7JdZBfzq718ryYrlmGgqkRm9OOwCE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d1c15b7d5806069da59e819999d70e1cec0760bf", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "systems": "systems" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..aebd6d8 --- /dev/null +++ b/flake.nix @@ -0,0 +1,33 @@ +{ + description = "oil.nvim — Neovim file explorer: edit your filesystem like a buffer"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + systems.url = "github:nix-systems/default"; + }; + + outputs = + { + nixpkgs, + systems, + ... + }: + let + forEachSystem = f: nixpkgs.lib.genAttrs (import systems) (system: f nixpkgs.legacyPackages.${system}); + in + { + devShells = forEachSystem (pkgs: { + default = pkgs.mkShell { + packages = [ + pkgs.prettier + pkgs.stylua + pkgs.selene + (pkgs.luajit.withPackages (ps: [ + ps.busted + ps.nlua + ])) + ]; + }; + }); + }; +} diff --git a/lua/oil/actions.lua b/lua/oil/actions.lua index edd44ba..143a454 100644 --- a/lua/oil/actions.lua +++ b/lua/oil/actions.lua @@ -1,18 +1,18 @@ -local oil = require("oil") -local util = require("oil.util") +local oil = require('oil') +local util = require('oil.util') local M = {} M.show_help = { callback = function() - local config = require("oil.config") - require("oil.keymap_util").show_help(config.keymaps) + local config = require('oil.config') + require('oil.keymap_util').show_help(config.keymaps) end, - desc = "Show default keymaps", + desc = 'Show default keymaps', } M.select = { - desc = "Open the entry under the cursor", + desc = 'Open the entry under the cursor', callback = function(opts) opts = opts or {} local callback = opts.callback @@ -21,30 +21,30 @@ M.select = { end, parameters = { vertical = { - type = "boolean", - desc = "Open the buffer in a vertical split", + type = 'boolean', + desc = 'Open the buffer in a vertical split', }, horizontal = { - type = "boolean", - desc = "Open the buffer in a horizontal split", + type = 'boolean', + desc = 'Open the buffer in a horizontal split', }, split = { type = '"aboveleft"|"belowright"|"topleft"|"botright"', - desc = "Split modifier", + desc = 'Split modifier', }, tab = { - type = "boolean", - desc = "Open the buffer in a new tab", + type = 'boolean', + desc = 'Open the buffer in a new tab', }, close = { - type = "boolean", - desc = "Close the original oil buffer once selection is made", + type = 'boolean', + desc = 'Close the original oil buffer once selection is made', }, }, } 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, callback = function() oil.select({ vertical = true }) @@ -52,7 +52,7 @@ M.select_vsplit = { } 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, callback = function() oil.select({ horizontal = true }) @@ -60,7 +60,7 @@ M.select_split = { } 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, callback = function() oil.select({ tab = true }) @@ -68,25 +68,25 @@ M.select_tab = { } 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 = { vertical = { - type = "boolean", - desc = "Open the buffer in a vertical split", + type = 'boolean', + desc = 'Open the buffer in a vertical split', }, horizontal = { - type = "boolean", - desc = "Open the buffer in a horizontal split", + type = 'boolean', + desc = 'Open the buffer in a horizontal split', }, split = { type = '"aboveleft"|"belowright"|"topleft"|"botright"', - desc = "Split modifier", + desc = 'Split modifier', }, }, callback = function(opts) local entry = oil.get_cursor_entry() 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 end local winid = util.get_preview_win() @@ -95,7 +95,7 @@ M.preview = { if entry.id == cur_id then vim.api.nvim_win_close(winid, true) if util.is_floating_win() then - local layout = require("oil.layout") + local layout = require('oil.layout') local win_opts = layout.get_fullscreen_win_opts() vim.api.nvim_win_set_config(0, win_opts) end @@ -107,13 +107,13 @@ M.preview = { } M.preview_scroll_down = { - desc = "Scroll down in the preview window", + desc = 'Scroll down in the preview window', callback = function() local winid = util.get_preview_win() if winid then vim.api.nvim_win_call(winid, function() vim.cmd.normal({ - args = { vim.api.nvim_replace_termcodes("", true, true, true) }, + args = { vim.api.nvim_replace_termcodes('', true, true, true) }, bang = true, }) end) @@ -122,13 +122,13 @@ M.preview_scroll_down = { } M.preview_scroll_up = { - desc = "Scroll up in the preview window", + desc = 'Scroll up in the preview window', callback = function() local winid = util.get_preview_win() if winid then vim.api.nvim_win_call(winid, function() vim.cmd.normal({ - args = { vim.api.nvim_replace_termcodes("", true, true, true) }, + args = { vim.api.nvim_replace_termcodes('', true, true, true) }, bang = true, }) end) @@ -137,50 +137,50 @@ M.preview_scroll_up = { } M.preview_scroll_left = { - desc = "Scroll left in the preview window", + desc = 'Scroll left in the preview window', callback = function() local winid = util.get_preview_win() if winid then vim.api.nvim_win_call(winid, function() - vim.cmd.normal({ "zH", bang = true }) + vim.cmd.normal({ 'zH', bang = true }) end) end end, } M.preview_scroll_right = { - desc = "Scroll right in the preview window", + desc = 'Scroll right in the preview window', callback = function() local winid = util.get_preview_win() if winid then vim.api.nvim_win_call(winid, function() - vim.cmd.normal({ "zL", bang = true }) + vim.cmd.normal({ 'zL', bang = true }) end) end end, } M.parent = { - desc = "Navigate to the parent path", + desc = 'Navigate to the parent path', callback = oil.open, } M.close = { - desc = "Close oil and restore original buffer", + desc = 'Close oil and restore original buffer', callback = function(opts) opts = opts or {} oil.close(opts) end, parameters = { exit_if_last_buf = { - type = "boolean", - desc = "Exit vim if oil is closed as the last buffer", + type = 'boolean', + desc = 'Exit vim if oil is closed as the last buffer', }, }, } 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) if vim.w.is_oil_win then opts = opts or {} @@ -189,8 +189,8 @@ M.close_float = { end, parameters = { exit_if_last_buf = { - type = "boolean", - desc = "Exit vim if oil is closed as the last buffer", + type = 'boolean', + desc = 'Exit vim if oil is closed as the last buffer', }, }, } @@ -202,42 +202,42 @@ local function cd(cmd, silent) if dir then vim.cmd({ cmd = cmd, args = { dir } }) 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 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 M.cd = { - desc = ":cd to the current oil directory", + desc = ':cd to the current oil directory', callback = function(opts) opts = opts or {} - local cmd = "cd" - if opts.scope == "tab" then - cmd = "tcd" - elseif opts.scope == "win" then - cmd = "lcd" + local cmd = 'cd' + if opts.scope == 'tab' then + cmd = 'tcd' + elseif opts.scope == 'win' then + cmd = 'lcd' end cd(cmd, opts.silent) end, parameters = { scope = { 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 = { - type = "boolean", - desc = "Do not show a message when changing directories", + type = 'boolean', + desc = 'Do not show a message when changing directories', }, }, } M.tcd = { - desc = ":tcd to the current oil directory", + desc = ':tcd to the current oil directory', deprecated = true, callback = function() - cd("tcd") + cd('tcd') end, } @@ -249,46 +249,46 @@ M.open_cwd = { } M.toggle_hidden = { - desc = "Toggle hidden files and directories", + desc = 'Toggle hidden files and directories', callback = function() - require("oil.view").toggle_hidden() + require('oil.view').toggle_hidden() end, } M.open_terminal = { - desc = "Open a terminal in the current directory", + desc = 'Open a terminal in the current directory', callback = function() - local config = require("oil.config") + local config = require('oil.config') local bufname = vim.api.nvim_buf_get_name(0) local adapter = config.get_adapter_by_scheme(bufname) if not adapter then return end - if adapter.name == "files" then + if adapter.name == 'files' then 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) 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 }) else ---@diagnostic disable-next-line: deprecated vim.fn.termopen(vim.o.shell, { cwd = dir }) end - elseif adapter.name == "ssh" then + elseif adapter.name == 'ssh' then local bufnr = vim.api.nvim_create_buf(false, true) vim.api.nvim_set_current_buf(bufnr) - local url = require("oil.adapters.ssh").parse_url(bufname) - local cmd = require("oil.adapters.ssh.connection").create_ssh_command(url) + local url = require('oil.adapters.ssh').parse_url(bufname) + local cmd = require('oil.adapters.ssh.connection').create_ssh_command(url) 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 }) else ---@diagnostic disable-next-line: deprecated term_id = vim.fn.termopen(cmd) end 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 else vim.notify( @@ -304,25 +304,25 @@ M.open_terminal = { ---@return nil|string[] cmd ---@return nil|string error local function get_open_cmd(path) - if vim.fn.has("mac") == 1 then - return { "open", path } - elseif vim.fn.has("win32") == 1 then - if vim.fn.executable("rundll32") == 1 then - return { "rundll32", "url.dll,FileProtocolHandler", path } + if vim.fn.has('mac') == 1 then + return { 'open', path } + elseif vim.fn.has('win32') == 1 then + if vim.fn.executable('rundll32') == 1 then + return { 'rundll32', 'url.dll,FileProtocolHandler', path } else - return nil, "rundll32 not found" + return nil, 'rundll32 not found' end - elseif vim.fn.executable("explorer.exe") == 1 then - return { "explorer.exe", path } - elseif vim.fn.executable("xdg-open") == 1 then - return { "xdg-open", path } + elseif vim.fn.executable('explorer.exe') == 1 then + return { 'explorer.exe', path } + elseif vim.fn.executable('xdg-open') == 1 then + return { 'xdg-open', path } else - return nil, "no handler found" + return nil, 'no handler found' end end 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() local entry = oil.get_cursor_entry() local dir = oil.get_current_dir() @@ -338,20 +338,20 @@ M.open_external = { local cmd, err = get_open_cmd(path) 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 end local jid = vim.fn.jobstart(cmd, { detach = true }) - assert(jid > 0, "Failed to start job") + assert(jid > 0, 'Failed to start job') end, } M.refresh = { - desc = "Refresh current directory list", + desc = 'Refresh current directory list', callback = function(opts) opts = opts or {} 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 return end @@ -363,26 +363,26 @@ M.refresh = { end, parameters = { force = { - desc = "When true, do not prompt user if they will be discarding changes", - type = "boolean", + desc = 'When true, do not prompt user if they will be discarding changes', + type = 'boolean', }, }, } local function open_cmdline_with_path(path) local escaped = - vim.api.nvim_replace_termcodes(": " .. vim.fn.fnameescape(path) .. "", true, false, true) - vim.api.nvim_feedkeys(escaped, "n", false) + vim.api.nvim_replace_termcodes(': ' .. vim.fn.fnameescape(path) .. '', true, false, true) + vim.api.nvim_feedkeys(escaped, 'n', false) end 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) - opts = vim.tbl_deep_extend("keep", opts or {}, { + opts = vim.tbl_deep_extend('keep', opts or {}, { shorten_path = true, }) - local config = require("oil.config") - local fs = require("oil.fs") + local config = require('oil.config') + local fs = require('oil.fs') local entry = oil.get_cursor_entry() if not entry then return @@ -393,7 +393,7 @@ M.open_cmdline = { return end 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 end local fullpath = fs.posix_to_os_path(path) .. entry.name @@ -407,18 +407,18 @@ M.open_cmdline = { end, parameters = { modify = { - desc = "Modify the path with |fnamemodify()| using this as the mods argument", - type = "string", + desc = 'Modify the path with |fnamemodify()| using this as the mods argument', + type = 'string', }, shorten_path = { - desc = "Use relative paths when possible", - type = "boolean", + desc = 'Use relative paths when possible', + type = 'boolean', }, }, } 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) opts = opts or {} local entry = oil.get_cursor_entry() @@ -427,8 +427,8 @@ M.yank_entry = { return end local name = entry.name - if entry.type == "directory" then - name = name .. "/" + if entry.type == 'directory' then + name = name .. '/' end local path = dir .. name if opts.modify then @@ -438,14 +438,14 @@ M.yank_entry = { end, parameters = { modify = { - desc = "Modify the path with |fnamemodify()| using this as the mods argument", - type = "string", + desc = 'Modify the path with |fnamemodify()| using this as the mods argument', + type = 'string', }, }, } 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, callback = function() local entry = oil.get_cursor_entry() @@ -458,7 +458,7 @@ M.copy_entry_path = { } 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, callback = function() local entry = oil.get_cursor_entry() @@ -470,30 +470,30 @@ M.copy_entry_filename = { } 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() - require("oil.clipboard").copy_to_system_clipboard() + require('oil.clipboard').copy_to_system_clipboard() end, } 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) - 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, parameters = { delete_original = { - type = "boolean", - desc = "Delete the original file after copying", + type = 'boolean', + desc = 'Delete the original file after copying', }, }, } 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, callback = function() - local fs = require("oil.fs") + local fs = require('oil.fs') local dir = oil.get_current_dir() if dir then open_cmdline_with_path(fs.shorten_path(dir)) @@ -502,7 +502,7 @@ M.open_cmdline_dir = { } M.change_sort = { - desc = "Change the sort order", + desc = 'Change the sort order', callback = function(opts) opts = opts or {} @@ -511,21 +511,21 @@ M.change_sort = { return end - local sort_cols = { "name", "size", "atime", "mtime", "ctime", "birthtime" } - vim.ui.select(sort_cols, { prompt = "Sort by", kind = "oil_sort_col" }, function(col) + local sort_cols = { 'name', 'size', 'atime', 'mtime', 'ctime', 'birthtime' } + vim.ui.select(sort_cols, { prompt = 'Sort by', kind = 'oil_sort_col' }, function(col) if not col then return end vim.ui.select( - { "ascending", "descending" }, - { prompt = "Sort order", kind = "oil_sort_order" }, + { 'ascending', 'descending' }, + { prompt = 'Sort order', kind = 'oil_sort_order' }, function(order) if not order then return end - order = order == "ascending" and "asc" or "desc" + order = order == 'ascending' and 'asc' or 'desc' oil.set_sort({ - { "type", "asc" }, + { 'type', 'asc' }, { col, order }, }) end @@ -534,24 +534,24 @@ M.change_sort = { end, parameters = { sort = { - type = "oil.SortSpec[]", - desc = "List of columns plus direction (see |oil.set_sort|) instead of interactive selection", + type = 'oil.SortSpec[]', + desc = 'List of columns plus direction (see |oil.set_sort|) instead of interactive selection', }, }, } 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() - local fs = require("oil.fs") + local fs = require('oil.fs') local bufname = vim.api.nvim_buf_get_name(0) local scheme, path = util.parse_url(bufname) local bufnr = vim.api.nvim_get_current_buf() local url - if scheme == "oil://" then - url = "oil-trash://" .. path - elseif scheme == "oil-trash://" then - url = "oil://" .. path + if scheme == 'oil://' then + url = 'oil-trash://' .. path + elseif scheme == 'oil-trash://' then + url = 'oil://' .. path -- The non-linux trash implementations don't support per-directory trash, -- so jump back to the stored source buffer. if not fs.is_linux then @@ -561,7 +561,7 @@ M.toggle_trash = { end end else - vim.notify("No trash found for buffer", vim.log.levels.WARN) + vim.notify('No trash found for buffer', vim.log.levels.WARN) return end vim.cmd.edit({ args = { url } }) @@ -570,11 +570,11 @@ M.toggle_trash = { } 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) - opts = vim.tbl_deep_extend("keep", opts or {}, { - target = "qflist", - action = "r", + opts = vim.tbl_deep_extend('keep', opts or {}, { + target = 'qflist', + action = 'r', only_matching_search = false, }) util.send_to_quickfix({ @@ -586,48 +586,48 @@ M.send_to_qflist = { parameters = { target = { type = '"qflist"|"loclist"', - desc = "The target list to send files to", + desc = 'The target list to send files to', }, action = { 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 = { - type = "boolean", - desc = "Whether to only add the files that matches the last search. This option only applies when search highlighting is active", + type = 'boolean', + 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 = { - 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, callback = function() util.send_to_quickfix({ - target = "qflist", - mode = "a", + target = 'qflist', + mode = 'a', }) end, } 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, callback = function() util.send_to_quickfix({ - target = "loclist", - mode = "r", + target = 'loclist', + mode = 'r', }) end, } 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, callback = function() util.send_to_quickfix({ - target = "loclist", - mode = "a", + target = 'loclist', + mode = 'a', }) end, } @@ -637,7 +637,7 @@ M.add_to_loclist = { M._get_actions = function() local ret = {} 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, { name = name, desc = action.desc, diff --git a/lua/oil/adapters/files.lua b/lua/oil/adapters/files.lua index 228e263..e6331ba 100644 --- a/lua/oil/adapters/files.lua +++ b/lua/oil/adapters/files.lua @@ -1,12 +1,12 @@ -local cache = require("oil.cache") -local columns = require("oil.columns") -local config = require("oil.config") -local constants = require("oil.constants") -local fs = require("oil.fs") -local git = require("oil.git") -local log = require("oil.log") -local permissions = require("oil.adapters.files.permissions") -local util = require("oil.util") +local cache = require('oil.cache') +local columns = require('oil.columns') +local config = require('oil.config') +local constants = require('oil.constants') +local fs = require('oil.fs') +local git = require('oil.git') +local log = require('oil.log') +local permissions = require('oil.adapters.files.permissions') +local util = require('oil.util') local uv = vim.uv or vim.loop local M = {} @@ -25,7 +25,7 @@ local function read_link_data(path, cb) assert(link) local stat_path = link 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 uv.fs_stat(stat_path, function(stat_err, stat) cb(nil, link, stat) @@ -43,7 +43,7 @@ end ---@return string M.to_short_os_path = function(path, entry_type) 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) end return shortpath @@ -61,13 +61,13 @@ file_columns.size = { return columns.EMPTY end if stat.size >= 1e9 then - return string.format("%.1fG", stat.size / 1e9) + return string.format('%.1fG', stat.size / 1e9) elseif stat.size >= 1e6 then - return string.format("%.1fM", stat.size / 1e6) + return string.format('%.1fM', stat.size / 1e6) elseif stat.size >= 1e3 then - return string.format("%.1fk", stat.size / 1e3) + return string.format('%.1fk', stat.size / 1e3) else - return string.format("%d", stat.size) + return string.format('%d', stat.size) end end, @@ -82,7 +82,7 @@ file_columns.size = { end, parse = function(line, conf) - return line:match("^(%d+%S*)%s+(.*)$") + return line:match('^(%d+%S*)%s+(.*)$') end, } @@ -120,7 +120,7 @@ if not fs.is_windows then local _, path = util.parse_url(action.url) assert(path) return string.format( - "CHMOD %s %s", + 'CHMOD %s %s', permissions.mode_to_octal_str(action.value), M.to_short_os_path(path, action.entry_type) ) @@ -147,10 +147,10 @@ end local current_year -- Make sure we run this import-time effect in the main loop (mostly for tests) vim.schedule(function() - current_year = vim.fn.strftime("%Y") + current_year = vim.fn.strftime('%Y') 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] = { require_stat = true, @@ -165,11 +165,11 @@ for _, time_key in ipairs({ "ctime", "mtime", "atime", "birthtime" }) do if fmt then ret = vim.fn.strftime(fmt, stat[time_key].sec) 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 - ret = vim.fn.strftime("%b %d %Y", stat[time_key].sec) + ret = vim.fn.strftime('%b %d %Y', stat[time_key].sec) 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 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 -- e.g. "%b %d %Y" -> "%S+%s+%S+%s+%S+" pattern = fmt - :gsub("%%.", "%%S+") - :gsub("%s+", "%%s+") + :gsub('%%.', '%%S+') + :gsub('%s+', '%%s+') -- escape `()[]` because those are special characters in Lua patterns :gsub( - "%(", - "%%(" + '%(', + '%%(' ) - :gsub("%)", "%%)") - :gsub("%[", "%%[") - :gsub("%]", "%%]") + :gsub('%)', '%%)') + :gsub('%[', '%%[') + :gsub('%]', '%%]') else - pattern = "%S+%s+%d+%s+%d%d:?%d%d" + pattern = '%S+%s+%d+%s+%d%d:?%d%d' end - return line:match("^(" .. pattern .. ")%s+(.+)$") + return line:match('^(' .. pattern .. ')%s+(.+)$') end, get_sort_value = function(entry) @@ -238,17 +238,17 @@ M.normalize_url = function(url, callback) assert(path) if fs.is_windows then - if path == "/" then + if path == '/' then return callback(url) else - local is_root_drive = path:match("^/%u$") + local is_root_drive = path:match('^/%u$') if is_root_drive then - return callback(url .. "/") + return callback(url .. '/') 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) local realpath if fs.is_windows then @@ -264,8 +264,8 @@ M.normalize_url = function(url, callback) vim.schedule_wrap(function(stat_err, stat) local is_directory if stat then - is_directory = stat.type == "directory" - elseif vim.endswith(realpath, "/") or (fs.is_windows and vim.endswith(realpath, "\\")) then + is_directory = stat.type == 'directory' + elseif vim.endswith(realpath, '/') or (fs.is_windows and vim.endswith(realpath, '\\')) then is_directory = true else 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)) callback(scheme .. norm_path) else - callback(vim.fn.fnamemodify(realpath, ":.")) + callback(vim.fn.fnamemodify(realpath, ':.')) end end) ) @@ -292,11 +292,11 @@ M.get_entry_path = function(url, entry, cb) local scheme, path = util.parse_url(parent_url) M.normalize_url(scheme .. path .. entry.name, cb) else - if entry.type == "directory" then + if entry.type == 'directory' then cb(url) else 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) end end @@ -321,10 +321,10 @@ local function fetch_entry_metadata(parent_dir, entry, require_stat, cb) end -- 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) 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() end 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 uv.fs_lstat(entry_path, function(stat_err, stat) 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() end meta.stat = stat @@ -350,7 +350,7 @@ local function fetch_entry_metadata(parent_dir, entry, require_stat, cb) elseif require_stat then uv.fs_stat(entry_path, function(stat_err, stat) 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() end assert(stat) @@ -368,11 +368,11 @@ end if fs.is_windows then local old_fetch_metadata = fetch_entry_metadata 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]) uv.fs_lstat(entry_path, function(stat_err, stat) 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) end assert(stat) @@ -398,17 +398,17 @@ local function list_windows_drives(url, column_defs, cb) local _, path = util.parse_url(url) assert(path) local require_stat = columns_require_stat(column_defs) - local stdout = "" - local jid = vim.fn.jobstart({ "wmic", "logicaldisk", "get", "name" }, { + local stdout = '' + local jid = vim.fn.jobstart({ 'wmic', 'logicaldisk', 'get', 'name' }, { stdout_buffered = true, on_stdout = function(_, data) - stdout = table.concat(data, "\n") + stdout = table.concat(data, '\n') end, on_exit = function(_, code) if code ~= 0 then - return cb("Error listing windows devices") + return cb('Error listing windows devices') 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 table.remove(lines, 1) local internal_entries = {} @@ -421,12 +421,12 @@ local function list_windows_drives(url, column_defs, cb) end) for _, disk in ipairs(lines) do - if disk:match("^%s*$") then + if disk:match('^%s*$') then -- Skip empty line complete_disk_cb() else - disk = disk:gsub(":%s*$", "") - local cache_entry = cache.create_entry(url, disk, "directory") + disk = disk:gsub(':%s*$', '') + local cache_entry = cache.create_entry(url, disk, 'directory') table.insert(internal_entries, cache_entry) fetch_entry_metadata(path, cache_entry, require_stat, complete_disk_cb) end @@ -434,7 +434,7 @@ local function list_windows_drives(url, column_defs, cb) end, }) if jid <= 0 then - cb("Could not list windows devices") + cb('Could not list windows devices') end end @@ -444,7 +444,7 @@ end M.list = function(url, column_defs, cb) local _, path = util.parse_url(url) assert(path) - if fs.is_windows and path == "/" then + if fs.is_windows and path == '/' then return list_windows_drives(url, column_defs, cb) end 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 uv.fs_opendir(dir, function(open_err, fd) 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 -- and edit a not-yet-existing directory. return cb() @@ -505,7 +505,7 @@ M.is_modifiable = function(bufnr) local bufname = vim.api.nvim_buf_get_name(bufnr) local _, path = util.parse_url(bufname) assert(path) - if fs.is_windows and path == "/" then + if fs.is_windows and path == '/' then return false end local dir = fs.posix_to_os_path(path) @@ -515,30 +515,30 @@ M.is_modifiable = function(bufnr) end -- fs_access can return nil, force boolean return - return uv.fs_access(dir, "W") == true + return uv.fs_access(dir, 'W') == true end ---@param action oil.Action ---@return string M.render_action = function(action) - if action.type == "create" then + if action.type == 'create' then local _, path = util.parse_url(action.url) 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 - ret = ret .. " -> " .. fs.posix_to_os_path(action.link) + ret = ret .. ' -> ' .. fs.posix_to_os_path(action.link) end return ret - elseif action.type == "delete" then + elseif action.type == 'delete' then local _, path = util.parse_url(action.url) assert(path) local short_path = M.to_short_os_path(path, action.entry_type) if config.delete_to_trash then - return string.format(" TRASH %s", short_path) + return string.format(' TRASH %s', short_path) else - return string.format("DELETE %s", short_path) + return string.format('DELETE %s', short_path) 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)) if dest_adapter == M then 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) assert(dest_path) return string.format( - " %s %s -> %s", + ' %s %s -> %s', action.type:upper(), M.to_short_os_path(src_path, action.entry_type), M.to_short_os_path(dest_path, action.entry_type) @@ -563,7 +563,7 @@ end ---@param action oil.Action ---@param cb fun(err: nil|string) M.perform_action = function(action, cb) - if action.type == "create" then + if action.type == 'create' then local _, path = util.parse_url(action.url) assert(path) path = fs.posix_to_os_path(path) @@ -579,16 +579,16 @@ M.perform_action = function(action, cb) end) end - if action.entry_type == "directory" then + if action.entry_type == 'directory' then uv.fs_mkdir(path, config.new_dir_mode, function(err) -- Ignore if the directory already exists - if not err or err:match("^EEXIST:") then + if not err or err:match('^EEXIST:') then cb() else cb(err) end end) - elseif action.entry_type == "link" and action.link then + elseif action.entry_type == 'link' and action.link then local flags = nil local target = fs.posix_to_os_path(action.link) if fs.is_windows then @@ -602,7 +602,7 @@ M.perform_action = function(action, cb) else fs.touch(path, config.new_file_mode, cb) end - elseif action.type == "delete" then + elseif action.type == 'delete' then local _, path = util.parse_url(action.url) assert(path) path = fs.posix_to_os_path(path) @@ -619,11 +619,11 @@ M.perform_action = function(action, cb) end 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 fs.recursive_delete(action.entry_type, path, cb) end - elseif action.type == "move" then + elseif action.type == 'move' then local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url)) if dest_adapter == M then 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 cb("files adapter doesn't support cross-adapter move") end - elseif action.type == "copy" then + elseif action.type == 'copy' then local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url)) if dest_adapter == M then 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") end else - cb(string.format("Bad action type: %s", action.type)) + cb(string.format('Bad action type: %s', action.type)) end end diff --git a/lua/oil/adapters/files/permissions.lua b/lua/oil/adapters/files/permissions.lua index 6c306a6..32c8488 100644 --- a/lua/oil/adapters/files/permissions.lua +++ b/lua/oil/adapters/files/permissions.lua @@ -4,7 +4,7 @@ local M = {} ---@param num integer ---@return string 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 bit.band(num, 1) ~= 0 then return str .. exe_modifier @@ -12,7 +12,7 @@ local function perm_to_str(exe_modifier, num) return str .. exe_modifier:upper() end else - return str .. (bit.band(num, 1) ~= 0 and "x" or "-") + return str .. (bit.band(num, 1) ~= 0 and 'x' or '-') end end @@ -20,9 +20,9 @@ end ---@return string M.mode_to_str = function(mode) local extra = bit.rshift(mode, 9) - 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, 1) ~= 0 and "t", mode) + 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, 1) ~= 0 and 't', mode) end ---@param mode integer @@ -38,25 +38,25 @@ end ---@param str string String of 3 characters ---@return nil|integer 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 - if r == "r" then + if r == 'r' then mode = bit.bor(mode, 4) - elseif r ~= "-" then + elseif r ~= '-' then return nil end - if w == "w" then + if w == 'w' then mode = bit.bor(mode, 2) - elseif w ~= "-" then + elseif w ~= '-' then return nil end -- t means sticky and executable -- T means sticky, not executable -- s means setuid/setgid and 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) - elseif x ~= "-" and x ~= "T" and x ~= "S" then + elseif x ~= '-' and x ~= 'T' and x ~= 'S' then return nil end return mode @@ -67,13 +67,13 @@ end local function parse_extra_bits(perm) perm = perm:lower() local mode = 0 - if perm:sub(3, 3) == "s" then + if perm:sub(3, 3) == 's' then mode = bit.bor(mode, 4) end - if perm:sub(6, 6) == "s" then + if perm:sub(6, 6) == 's' then mode = bit.bor(mode, 2) end - if perm:sub(9, 9) == "t" then + if perm:sub(9, 9) == 't' then mode = bit.bor(mode, 1) end return mode @@ -83,7 +83,7 @@ end ---@return nil|integer ---@return nil|string 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 return end diff --git a/lua/oil/adapters/s3.lua b/lua/oil/adapters/s3.lua index b7b5400..81557c7 100644 --- a/lua/oil/adapters/s3.lua +++ b/lua/oil/adapters/s3.lua @@ -1,11 +1,11 @@ -local config = require("oil.config") -local constants = require("oil.constants") -local files = require("oil.adapters.files") -local fs = require("oil.fs") -local loading = require("oil.loading") -local pathutil = require("oil.pathutil") -local s3fs = require("oil.adapters.s3.s3fs") -local util = require("oil.util") +local config = require('oil.config') +local constants = require('oil.constants') +local files = require('oil.adapters.files') +local fs = require('oil.fs') +local loading = require('oil.loading') +local pathutil = require('oil.pathutil') +local s3fs = require('oil.adapters.s3.s3fs') +local util = require('oil.util') local M = {} local FIELD_META = constants.FIELD_META @@ -21,11 +21,11 @@ M.parse_url = function(oil_url) local scheme, url = util.parse_url(oil_url) assert(scheme and url, string.format("Malformed input url '%s'", oil_url)) local ret = { scheme = scheme } - local bucket, path = url:match("^([^/]+)/?(.*)$") + local bucket, path = url:match('^([^/]+)/?(.*)$') 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 - error(string.format("Parsing error for s3 url: %s", oil_url)) + error(string.format('Parsing error for s3 url: %s', oil_url)) end ---@cast ret oil.s3Url return ret @@ -36,43 +36,43 @@ end local function url_to_str(url) local pieces = { url.scheme } if url.bucket then - assert(url.bucket ~= "") + assert(url.bucket ~= '') table.insert(pieces, url.bucket) - table.insert(pieces, "/") + table.insert(pieces, '/') end if url.path then - assert(url.path ~= "") + assert(url.path ~= '') table.insert(pieces, url.path) end - return table.concat(pieces, "") + return table.concat(pieces, '') end ---@param url oil.s3Url ---@param is_folder boolean ---@return string local function url_to_s3(url, is_folder) - local pieces = { "s3://" } + local pieces = { 's3://' } if url.bucket then - assert(url.bucket ~= "") + assert(url.bucket ~= '') table.insert(pieces, url.bucket) - table.insert(pieces, "/") + table.insert(pieces, '/') end if url.path then - assert(url.path ~= "") + assert(url.path ~= '') table.insert(pieces, url.path) - if is_folder and not vim.endswith(url.path, "/") then - table.insert(pieces, "/") + if is_folder and not vim.endswith(url.path, '/') then + table.insert(pieces, '/') end end - return table.concat(pieces, "") + return table.concat(pieces, '') end ---@param url oil.s3Url ---@return boolean local function is_bucket(url) - assert(url.bucket and url.bucket ~= "") + assert(url.bucket and url.bucket ~= '') if url.path then - assert(url.path ~= "") + assert(url.path ~= '') return false end return true @@ -83,20 +83,20 @@ s3_columns.size = { render = function(entry, conf) local meta = entry[FIELD_META] if not meta or not meta.size then - return "" + return '' elseif meta.size >= 1e9 then - return string.format("%.1fG", meta.size / 1e9) + return string.format('%.1fG', meta.size / 1e9) elseif meta.size >= 1e6 then - return string.format("%.1fM", meta.size / 1e6) + return string.format('%.1fM', meta.size / 1e6) elseif meta.size >= 1e3 then - return string.format("%.1fk", meta.size / 1e3) + return string.format('%.1fk', meta.size / 1e3) else - return string.format("%d", meta.size) + return string.format('%d', meta.size) end end, parse = function(line, conf) - return line:match("^(%d+%S*)%s+(.*)$") + return line:match('^(%d+%S*)%s+(.*)$') end, get_sort_value = function(entry) @@ -113,21 +113,21 @@ s3_columns.birthtime = { render = function(entry, conf) local meta = entry[FIELD_META] if not meta or not meta.date then - return "" + return '' else return meta.date end end, 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, get_sort_value = function(entry) local meta = entry[FIELD_META] if meta and meta.date then 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 = os.time({ year = year, month = month, day = day, hour = hour, min = min, sec = sec }) return time @@ -148,9 +148,9 @@ end M.get_parent = function(bufname) local res = M.parse_url(bufname) if res.path then - assert(res.path ~= "") + assert(res.path ~= '') local path = pathutil.parent(res.path) - res.path = path ~= "" and path or nil + res.path = path ~= '' and path or nil else res.bucket = nil end @@ -168,8 +168,8 @@ end ---@param column_defs string[] ---@param callback fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun()) M.list = function(url, column_defs, callback) - if vim.fn.executable("aws") ~= 1 then - callback("`aws` is not executable. Can you run `aws s3 ls`?") + if vim.fn.executable('aws') ~= 1 then + callback('`aws` is not executable. Can you run `aws s3 ls`?') return end @@ -187,16 +187,16 @@ end ---@param action oil.Action ---@return string M.render_action = function(action) - local is_folder = action.entry_type == "directory" - if action.type == "create" then + local is_folder = action.entry_type == 'directory' + if action.type == 'create' then local res = M.parse_url(action.url) - local extra = is_bucket(res) and "BUCKET " or "" - return string.format("CREATE %s%s", extra, url_to_s3(res, is_folder)) - elseif action.type == "delete" then + local extra = is_bucket(res) and 'BUCKET ' or '' + return string.format('CREATE %s%s', extra, url_to_s3(res, is_folder)) + elseif action.type == 'delete' then local res = M.parse_url(action.url) - local extra = is_bucket(res) and "BUCKET " or "" - return string.format("DELETE %s%s", extra, url_to_s3(res, is_folder)) - elseif action.type == "move" or action.type == "copy" then + local extra = is_bucket(res) and 'BUCKET ' or '' + return string.format('DELETE %s%s', extra, url_to_s3(res, is_folder)) + elseif action.type == 'move' or action.type == 'copy' then local src = action.src_url local dest = action.dest_url 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) src = url_to_s3(M.parse_url(src), is_folder) end - return string.format(" %s %s -> %s", action.type:upper(), src, dest) + return string.format(' %s %s -> %s', action.type:upper(), src, dest) else error(string.format("Bad action type: '%s'", action.type)) end @@ -219,30 +219,30 @@ end ---@param action oil.Action ---@param cb fun(err: nil|string) M.perform_action = function(action, cb) - local is_folder = action.entry_type == "directory" - if action.type == "create" then + local is_folder = action.entry_type == 'directory' + if action.type == 'create' then local res = M.parse_url(action.url) 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) - 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) 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 - elseif action.type == "delete" then + elseif action.type == 'delete' then local res = M.parse_url(action.url) 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) - 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) 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 - elseif action.type == "move" then + elseif action.type == 'move' then local src_adapter = assert(config.get_adapter_by_scheme(action.src_url)) local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url)) if @@ -250,7 +250,7 @@ M.perform_action = function(action, cb) then cb( 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, dest_adapter.name ) @@ -276,7 +276,7 @@ M.perform_action = function(action, cb) assert(dest) 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 dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url)) if @@ -284,7 +284,7 @@ M.perform_action = function(action, cb) then cb( 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, dest_adapter.name ) @@ -311,11 +311,11 @@ M.perform_action = function(action, cb) s3fs.cp(src, dest, is_folder, cb) else - cb(string.format("Bad action type: %s", action.type)) + cb(string.format('Bad action type: %s', action.type)) end end -M.supported_cross_adapter_actions = { files = "move" } +M.supported_cross_adapter_actions = { files = 'move' } ---@param bufnr integer M.read_file = function(bufnr) @@ -323,11 +323,11 @@ M.read_file = function(bufnr) local bufname = vim.api.nvim_buf_get_name(bufnr) local url = M.parse_url(bufname) local basename = pathutil.basename(bufname) - local cache_dir = vim.fn.stdpath("cache") - assert(type(cache_dir) == "string") - local tmpdir = fs.join(cache_dir, "oil") + local cache_dir = vim.fn.stdpath('cache') + assert(type(cache_dir) == 'string') + local tmpdir = fs.join(cache_dir, 'oil') 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 vim.loop.fs_close(fd) end @@ -336,9 +336,9 @@ M.read_file = function(bufnr) s3fs.cp(url_to_s3(url, false), tmpfile, false, function(err) loading.set_loading(bufnr, false) 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 - 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 vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {}) vim.api.nvim_buf_call(bufnr, function() @@ -352,7 +352,7 @@ M.read_file = function(bufnr) if filetype then vim.bo[bufnr].filetype = filetype 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 }) end) end @@ -361,14 +361,14 @@ end M.write_file = function(bufnr) local bufname = vim.api.nvim_buf_get_name(bufnr) local url = M.parse_url(bufname) - local cache_dir = vim.fn.stdpath("cache") - assert(type(cache_dir) == "string") - local tmpdir = fs.join(cache_dir, "oil") - local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, "s3_XXXXXXXX")) + local cache_dir = vim.fn.stdpath('cache') + assert(type(cache_dir) == 'string') + local tmpdir = fs.join(cache_dir, 'oil') + local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, 's3_XXXXXXXX')) if fd then vim.loop.fs_close(fd) 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.cmd.write({ args = { tmpfile }, bang = true, mods = { silent = true, noautocmd = true } }) 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) vim.bo[bufnr].modifiable = true 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 vim.bo[bufnr].modified = false - vim.cmd.doautocmd({ args = { "BufWritePost", bufname }, mods = { silent = true } }) + vim.cmd.doautocmd({ args = { 'BufWritePost', bufname }, mods = { silent = true } }) end vim.loop.fs_unlink(tmpfile) vim.api.nvim_buf_delete(tmp_bufnr, { force = true }) diff --git a/lua/oil/adapters/s3/s3fs.lua b/lua/oil/adapters/s3/s3fs.lua index 2e16307..a81f10c 100644 --- a/lua/oil/adapters/s3/s3fs.lua +++ b/lua/oil/adapters/s3/s3fs.lua @@ -1,8 +1,8 @@ -local cache = require("oil.cache") -local config = require("oil.config") -local constants = require("oil.constants") -local shell = require("oil.shell") -local util = require("oil.util") +local cache = require('oil.cache') +local config = require('oil.config') +local constants = require('oil.constants') +local shell = require('oil.shell') +local util = require('oil.util') local M = {} @@ -13,11 +13,11 @@ local FIELD_META = constants.FIELD_META ---@return oil.EntryType ---@return table Metadata for entry 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 error(string.format("Could not parse '%s'", line)) end - local type = "directory" + local type = 'directory' local meta = { date = date } return name, type, meta end @@ -27,18 +27,18 @@ end ---@return oil.EntryType ---@return table Metadata for entry local function parse_ls_line_file(line) - local name = line:match("^%s+PRE%s+(.*)/$") - local type = "directory" + local name = line:match('^%s+PRE%s+(.*)/$') + local type = 'directory' local meta = {} if name then return name, type, meta end 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 error(string.format("Could not parse '%s'", line)) end - type = "file" + type = 'file' meta = { date = date, size = tonumber(size) } return name, type, meta end @@ -46,7 +46,7 @@ end ---@param cmd string[] cmd and flags ---@return string[] Shell command to run 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) end @@ -54,7 +54,7 @@ end ---@param path string ---@param callback fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun()) 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) if err then return callback(err) @@ -63,13 +63,13 @@ function M.list_dir(url, path, callback) local cache_entries = {} local url_path, _ _, 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 for _, line in ipairs(lines) do - if line ~= "" then + if line ~= '' then local name, type, meta = parse_ls_line(line) -- 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) table.insert(cache_entries, cache_entry) cache_entry[FIELD_META] = meta @@ -85,8 +85,8 @@ end ---@param callback fun(err: nil|string) function M.touch(path, callback) -- here "-" means that we copy from stdin - local cmd = create_s3_command({ "cp", "-", path }) - shell.run(cmd, { stdin = "null" }, callback) + local cmd = create_s3_command({ 'cp', '-', path }) + shell.run(cmd, { stdin = 'null' }, callback) end --- Remove files @@ -94,9 +94,9 @@ end ---@param is_folder boolean ---@param callback fun(err: nil|string) function M.rm(path, is_folder, callback) - local main_cmd = { "rm", path } + local main_cmd = { 'rm', path } if is_folder then - table.insert(main_cmd, "--recursive") + table.insert(main_cmd, '--recursive') end local cmd = create_s3_command(main_cmd) shell.run(cmd, callback) @@ -106,7 +106,7 @@ end ---@param bucket string ---@param callback fun(err: nil|string) function M.rb(bucket, callback) - local cmd = create_s3_command({ "rb", bucket }) + local cmd = create_s3_command({ 'rb', bucket }) shell.run(cmd, callback) end @@ -114,7 +114,7 @@ end ---@param bucket string ---@param callback fun(err: nil|string) function M.mb(bucket, callback) - local cmd = create_s3_command({ "mb", bucket }) + local cmd = create_s3_command({ 'mb', bucket }) shell.run(cmd, callback) end @@ -124,9 +124,9 @@ end ---@param is_folder boolean ---@param callback fun(err: nil|string) function M.mv(src, dest, is_folder, callback) - local main_cmd = { "mv", src, dest } + local main_cmd = { 'mv', src, dest } if is_folder then - table.insert(main_cmd, "--recursive") + table.insert(main_cmd, '--recursive') end local cmd = create_s3_command(main_cmd) shell.run(cmd, callback) @@ -138,9 +138,9 @@ end ---@param is_folder boolean ---@param callback fun(err: nil|string) function M.cp(src, dest, is_folder, callback) - local main_cmd = { "cp", src, dest } + local main_cmd = { 'cp', src, dest } if is_folder then - table.insert(main_cmd, "--recursive") + table.insert(main_cmd, '--recursive') end local cmd = create_s3_command(main_cmd) shell.run(cmd, callback) diff --git a/lua/oil/adapters/ssh.lua b/lua/oil/adapters/ssh.lua index ae4291f..b78d02f 100644 --- a/lua/oil/adapters/ssh.lua +++ b/lua/oil/adapters/ssh.lua @@ -1,13 +1,13 @@ -local config = require("oil.config") -local constants = require("oil.constants") -local files = require("oil.adapters.files") -local fs = require("oil.fs") -local loading = require("oil.loading") -local pathutil = require("oil.pathutil") -local permissions = require("oil.adapters.files.permissions") -local shell = require("oil.shell") -local sshfs = require("oil.adapters.ssh.sshfs") -local util = require("oil.util") +local config = require('oil.config') +local constants = require('oil.constants') +local files = require('oil.adapters.files') +local fs = require('oil.fs') +local loading = require('oil.loading') +local pathutil = require('oil.pathutil') +local permissions = require('oil.adapters.files.permissions') +local shell = require('oil.shell') +local sshfs = require('oil.adapters.ssh.sshfs') +local util = require('oil.util') local M = {} local FIELD_NAME = constants.FIELD_NAME @@ -22,7 +22,7 @@ local FIELD_META = constants.FIELD_META ---@param args string[] 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) shell.run(cmd, ...) end @@ -33,21 +33,21 @@ M.parse_url = function(oil_url) local scheme, url = util.parse_url(oil_url) assert(scheme and url, string.format("Malformed input url '%s'", oil_url)) local ret = { scheme = scheme } - local username, rem = url:match("^([^@%s]+)@(.*)$") + local username, rem = url:match('^([^@%s]+)@(.*)$') ret.user = username url = rem or url - local host, port, path = url:match("^([^:]+):(%d+)/(.*)$") + local host, port, path = url:match('^([^:]+):(%d+)/(.*)$') if host then ret.host = host ret.port = tonumber(port) ret.path = path else - host, path = url:match("^([^/]+)/(.*)$") + host, path = url:match('^([^/]+)/(.*)$') ret.host = host ret.path = path end 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 ---@cast ret oil.sshUrl @@ -60,33 +60,33 @@ local function url_to_str(url) local pieces = { url.scheme } if url.user then table.insert(pieces, url.user) - table.insert(pieces, "@") + table.insert(pieces, '@') end table.insert(pieces, url.host) if url.port then - table.insert(pieces, string.format(":%d", url.port)) + table.insert(pieces, string.format(':%d', url.port)) end - table.insert(pieces, "/") + table.insert(pieces, '/') table.insert(pieces, url.path) - return table.concat(pieces, "") + return table.concat(pieces, '') end ---@param url oil.sshUrl ---@return string local function url_to_scp(url) - local pieces = { "scp://" } + local pieces = { 'scp://' } if url.user then table.insert(pieces, url.user) - table.insert(pieces, "@") + table.insert(pieces, '@') end table.insert(pieces, url.host) if url.port then - table.insert(pieces, string.format(":%d", url.port)) + table.insert(pieces, string.format(':%d', url.port)) end - table.insert(pieces, "/") + table.insert(pieces, '/') local escaped_path = util.url_escape(url.path) table.insert(pieces, escaped_path) - return table.concat(pieces, "") + return table.concat(pieces, '') end ---@param url1 oil.sshUrl @@ -103,7 +103,7 @@ local _connections = {} local function get_connection(url, allow_retry) local res = M.parse_url(url) res.scheme = config.adapter_to_scheme.ssh - res.path = "" + res.path = '' local key = url_to_str(res) local conn = _connections[key] if not conn or (allow_retry and conn:get_connection_error()) then @@ -137,7 +137,7 @@ ssh_columns.permissions = { end, 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, perform_action = function(action, callback) @@ -151,20 +151,20 @@ ssh_columns.size = { render = function(entry, conf) local meta = entry[FIELD_META] if not meta or not meta.size then - return "" + return '' elseif meta.size >= 1e9 then - return string.format("%.1fG", meta.size / 1e9) + return string.format('%.1fG', meta.size / 1e9) elseif meta.size >= 1e6 then - return string.format("%.1fM", meta.size / 1e6) + return string.format('%.1fM', meta.size / 1e6) elseif meta.size >= 1e3 then - return string.format("%.1fk", meta.size / 1e3) + return string.format('%.1fk', meta.size / 1e3) else - return string.format("%d", meta.size) + return string.format('%d', meta.size) end end, parse = function(line, conf) - return line:match("^(%d+%S*)%s+(.*)$") + return line:match('^(%d+%S*)%s+(.*)$') end, get_sort_value = function(entry) @@ -206,13 +206,13 @@ M.normalize_url = function(url, callback) local conn = get_connection(url, true) local path = res.path - if path == "" then - path = "." + if path == '' then + path = '.' end conn:realpath(path, function(err, abspath) 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) else res.path = abspath @@ -259,15 +259,15 @@ end ---@param action oil.Action ---@return string M.render_action = function(action) - if action.type == "create" then - local ret = string.format("CREATE %s", action.url) + if action.type == 'create' then + local ret = string.format('CREATE %s', action.url) if action.link then - ret = ret .. " -> " .. action.link + ret = ret .. ' -> ' .. action.link end return ret - elseif action.type == "delete" then - return string.format("DELETE %s", action.url) - elseif action.type == "move" or action.type == "copy" then + elseif action.type == 'delete' then + return string.format('DELETE %s', action.url) + elseif action.type == 'move' or action.type == 'copy' then local src = action.src_url local dest = action.dest_url if config.get_adapter_by_scheme(src) == M then @@ -279,7 +279,7 @@ M.render_action = function(action) assert(path) src = files.to_short_os_path(path, action.entry_type) end - return string.format(" %s %s -> %s", action.type:upper(), src, dest) + return string.format(' %s %s -> %s', action.type:upper(), src, dest) else error(string.format("Bad action type: '%s'", action.type)) end @@ -288,21 +288,21 @@ end ---@param action oil.Action ---@param cb fun(err: nil|string) M.perform_action = function(action, cb) - if action.type == "create" then + if action.type == 'create' then local res = M.parse_url(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) - 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) else conn:touch(res.path, cb) end - elseif action.type == "delete" then + elseif action.type == 'delete' then local res = M.parse_url(action.url) local conn = get_connection(action.url) 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 dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url)) 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 dest_conn = get_connection(action.dest_url) 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 return cb(err) end @@ -321,16 +321,16 @@ M.perform_action = function(action, cb) src_conn:mv(src_res.path, dest_res.path, cb) end else - cb("We should never attempt to move across adapters") + cb('We should never attempt to move across adapters') end - elseif action.type == "copy" then + elseif action.type == 'copy' then local src_adapter = assert(config.get_adapter_by_scheme(action.src_url)) local dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url)) if src_adapter == M and dest_adapter == M then local src_res = M.parse_url(action.src_url) local dest_res = M.parse_url(action.dest_url) 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 local src_conn = get_connection(action.src_url) 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) dest_arg = url_to_scp(M.parse_url(action.dest_url)) end - scp({ "-r", src_arg, dest_arg }, cb) + scp({ '-r', src_arg, dest_arg }, cb) end else - cb(string.format("Bad action type: %s", action.type)) + cb(string.format('Bad action type: %s', action.type)) end end -M.supported_cross_adapter_actions = { files = "copy" } +M.supported_cross_adapter_actions = { files = 'copy' } ---@param bufnr integer M.read_file = function(bufnr) @@ -365,11 +365,11 @@ M.read_file = function(bufnr) local url = M.parse_url(bufname) local scp_url = url_to_scp(url) local basename = pathutil.basename(bufname) - local cache_dir = vim.fn.stdpath("cache") - assert(type(cache_dir) == "string") - local tmpdir = fs.join(cache_dir, "oil") + local cache_dir = vim.fn.stdpath('cache') + assert(type(cache_dir) == 'string') + local tmpdir = fs.join(cache_dir, 'oil') 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 vim.loop.fs_close(fd) end @@ -378,9 +378,9 @@ M.read_file = function(bufnr) scp({ scp_url, tmpfile }, function(err) loading.set_loading(bufnr, false) 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 - 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 vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {}) vim.api.nvim_buf_call(bufnr, function() @@ -394,9 +394,9 @@ M.read_file = function(bufnr) if filetype then vim.bo[bufnr].filetype = filetype 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.keymap.set("n", "gf", M.goto_file, { buffer = bufnr }) + vim.keymap.set('n', 'gf', M.goto_file, { buffer = bufnr }) end) end @@ -405,14 +405,14 @@ M.write_file = function(bufnr) local bufname = vim.api.nvim_buf_get_name(bufnr) local url = M.parse_url(bufname) local scp_url = url_to_scp(url) - local cache_dir = vim.fn.stdpath("cache") - assert(type(cache_dir) == "string") - local tmpdir = fs.join(cache_dir, "oil") - local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, "ssh_XXXXXXXX")) + local cache_dir = vim.fn.stdpath('cache') + assert(type(cache_dir) == 'string') + local tmpdir = fs.join(cache_dir, 'oil') + local fd, tmpfile = vim.loop.fs_mkstemp(fs.join(tmpdir, 'ssh_XXXXXXXX')) if fd then vim.loop.fs_close(fd) 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.cmd.write({ args = { tmpfile }, bang = true, mods = { silent = true, noautocmd = true } }) local tmp_bufnr = vim.fn.bufadd(tmpfile) @@ -420,10 +420,10 @@ M.write_file = function(bufnr) scp({ tmpfile, scp_url }, function(err) vim.bo[bufnr].modifiable = true 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 vim.bo[bufnr].modified = false - vim.cmd.doautocmd({ args = { "BufWritePost", bufname }, mods = { silent = true } }) + vim.cmd.doautocmd({ args = { 'BufWritePost', bufname }, mods = { silent = true } }) end vim.loop.fs_unlink(tmpfile) vim.api.nvim_buf_delete(tmp_bufnr, { force = true }) @@ -432,7 +432,7 @@ end M.goto_file = function() local url = M.parse_url(vim.api.nvim_buf_get_name(0)) - local fname = vim.fn.expand("") + local fname = vim.fn.expand('') local fullpath = fname if not fs.is_absolute(fname) then local pardir = vim.fs.dirname(url.path) @@ -459,7 +459,7 @@ M.goto_file = function() vim.cmd.edit({ args = { url_to_str(url) } }) return 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 if name_map[suffixname] then url.path = fullpath .. suffix diff --git a/lua/oil/adapters/ssh/connection.lua b/lua/oil/adapters/ssh/connection.lua index 6a47c07..6dbaa49 100644 --- a/lua/oil/adapters/ssh/connection.lua +++ b/lua/oil/adapters/ssh/connection.lua @@ -1,6 +1,6 @@ -local config = require("oil.config") -local layout = require("oil.layout") -local util = require("oil.util") +local config = require('oil.config') +local layout = require('oil.layout') +local util = require('oil.util') ---@class (exact) oil.sshCommand ---@field cmd string|string[] @@ -24,12 +24,12 @@ local function output_extend(agg, output) local start = #agg if vim.tbl_isempty(agg) then for _, line in ipairs(output) do - line = line:gsub("\r", "") + line = line:gsub('\r', '') table.insert(agg, line) end else for i, v in ipairs(output) do - v = v:gsub("\r", "") + v = v:gsub('\r', '') if i == 1 then agg[#agg] = agg[#agg] .. v 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), 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) end end_line = end_line - need_lines @@ -66,14 +66,14 @@ end function SSHConnection.create_ssh_command(url) local host = url.host if url.user then - host = url.user .. "@" .. host + host = url.user .. '@' .. host end local command = { - "ssh", + 'ssh', host, } if url.port then - table.insert(command, "-p") + table.insert(command, '-p') table.insert(command, url.port) end return command @@ -84,8 +84,8 @@ end function SSHConnection.new(url) local command = SSHConnection.create_ssh_command(url) vim.list_extend(command, { - "/bin/sh", - "-c", + '/bin/sh', + '-c', -- 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. "echo '_make_newline_'; echo '===READY==='; exec /bin/sh", @@ -112,7 +112,7 @@ function SSHConnection.new(url) }) end) 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) -- 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, { pty = true, -- This is require for interactivity 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 local new_i_start = output_extend(self._stdout, output) self:_handle_output(new_i_start) @@ -134,12 +134,12 @@ function SSHConnection.new(url) pcall( vim.api.nvim_chan_send, 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 vim.defer_fn(function() if code == 0 then - self:_set_connection_error("SSH connection terminated gracefully") + self:_set_connection_error('SSH connection terminated gracefully') else self:_set_connection_error( 'Unknown SSH error\nTo see more, run :lua require("oil.adapters.ssh").open_terminal()' @@ -156,23 +156,23 @@ function SSHConnection.new(url) else self.jid = jid end - self:run("id -u", function(err, lines) + self:run('id -u', function(err, lines) 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 assert(lines) - self.meta.user = vim.trim(table.concat(lines, "")) + self.meta.user = vim.trim(table.concat(lines, '')) end end) - self:run("id -G", function(err, lines) + self:run('id -G', function(err, lines) if err then 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 ) else 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) @@ -197,7 +197,7 @@ function SSHConnection:_handle_output(start_i) if not self.connected then for i = start_i, #self._stdout - 1 do local line = self._stdout[i] - if line == "===READY===" then + if line == '===READY===' then if self.term_winid then if vim.api.nvim_win_is_valid(self.term_winid) then 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 ---@type string 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:_handle_output(1) 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 -- stderr can be interleaved. If the command had an error, the stderr may interfere with a -- clean print of the done line. - local exit_code = line:match("===DONE%((%d+)%)===") + local exit_code = line:match('===DONE%((%d+)%)===') if exit_code then local output = util.tbl_slice(self._stdout, 1, i - 1) local cb = self.commands[1].cb self._stdout = util.tbl_slice(self._stdout, i + 1) - if exit_code == "0" then + if exit_code == '0' then cb(nil, output) else - cb(exit_code .. ": " .. table.concat(output, "\n"), output) + cb(exit_code .. ': ' .. table.concat(output, '\n'), output) end table.remove(self.commands, 1) self:_handle_output(1) @@ -244,16 +244,17 @@ function SSHConnection:_handle_output(start_i) local function check_last_line() local last_lines = get_last_lines(self.term_bufnr, 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() - elseif last_line:match("Password:%s*$") then + -- selene: allow(if_same_then_else) + elseif last_line:match('Password:%s*$') then self:open_terminal() - elseif last_line:match(": Permission denied %(.+%)%.") then - self:_set_connection_error(last_line:match(": (Permission denied %(.+%).)")) - elseif last_line:match("^ssh: .*Connection refused%s*$") then - self:_set_connection_error("Connection refused") - elseif last_line:match("^Connection to .+ closed by remote host.%s*$") then - self:_set_connection_error("Connection closed by remote host") + elseif last_line:match(': Permission denied %(.+%)%.') then + self:_set_connection_error(last_line:match(': (Permission denied %(.+%).)')) + elseif last_line:match('^ssh: .*Connection refused%s*$') then + self:_set_connection_error('Connection refused') + elseif last_line:match('^Connection to .+ closed by remote host.%s*$') then + self:_set_connection_error('Connection closed by remote host') end end -- We have to defer this so the terminal buffer has time to update @@ -273,12 +274,12 @@ function SSHConnection:open_terminal() local row = math.floor((total_height - height) / 2) local col = math.floor((vim.o.columns - width) / 2) self.term_winid = vim.api.nvim_open_win(self.term_bufnr, true, { - relative = "editor", + relative = 'editor', width = width, height = height, row = row, col = col, - style = "minimal", + style = 'minimal', border = config.ssh.border, }) vim.cmd.startinsert() diff --git a/lua/oil/adapters/ssh/sshfs.lua b/lua/oil/adapters/ssh/sshfs.lua index 0dcc169..7d250f2 100644 --- a/lua/oil/adapters/ssh/sshfs.lua +++ b/lua/oil/adapters/ssh/sshfs.lua @@ -1,8 +1,8 @@ -local SSHConnection = require("oil.adapters.ssh.connection") -local cache = require("oil.cache") -local constants = require("oil.constants") -local permissions = require("oil.adapters.files.permissions") -local util = require("oil.util") +local SSHConnection = require('oil.adapters.ssh.connection') +local cache = require('oil.cache') +local constants = require('oil.constants') +local permissions = require('oil.adapters.files.permissions') +local util = require('oil.util') ---@class (exact) 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 typechar_map = { - l = "link", - d = "directory", - p = "fifo", - s = "socket", - ["-"] = "file", - c = "file", -- character special file - b = "file", -- block special file + l = 'link', + d = 'directory', + p = 'fifo', + s = 'socket', + ['-'] = 'file', + c = 'file', -- character special file + b = 'file', -- block special file } ---@param line string ---@return string Name of entry @@ -27,11 +27,11 @@ local typechar_map = { ---@return table Metadata for entry local function parse_ls_line(line) 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 error(string.format("Could not parse '%s'", line)) end - local type = typechar_map[typechar] or "file" + local type = typechar_map[typechar] or 'file' local meta = { user = user, @@ -40,26 +40,26 @@ local function parse_ls_line(line) refcount = tonumber(refcount), } local name, size, date, major, minor - 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+(.*)") + 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+(.*)') if name == nil then 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 meta.major = tonumber(major) meta.minor = tonumber(minor) 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 - 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 meta.size = tonumber(size) end meta.iso_modified_date = date - if type == "link" then + if type == 'link' then local link - name, link = unpack(vim.split(name, " -> ", { plain = true })) - if vim.endswith(link, "/") then + name, link = unpack(vim.split(name, ' -> ', { plain = true })) + if vim.endswith(link, '/') then link = link:sub(1, #link - 1) end meta.link = link @@ -94,7 +94,7 @@ end ---@param callback fun(err: nil|string) function SSHFS:chmod(value, path, callback) 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 function SSHFS:open_terminal() @@ -114,24 +114,24 @@ function SSHFS:realpath(path, callback) return callback(err) end 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 vim.endswith(abspath, ".") then + if vim.endswith(abspath, '.') then abspath = abspath:sub(1, #abspath - 1) end 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) local type if ls_err then -- If the file doesn't exist, treat it like a not-yet-existing directory - type = "directory" + type = 'directory' else assert(ls_lines) local _ _, type = parse_ls_line(ls_lines[1]) end - if type == "directory" then + if type == 'directory' then abspath = util.addslash(abspath) end callback(nil, abspath) @@ -146,13 +146,13 @@ local dir_meta = {} ---@param path string ---@param callback fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun()) function SSHFS:list_dir(url, path, callback) - local path_postfix = "" - if path ~= "" then - path_postfix = string.format(" %s", shellescape(path)) + local path_postfix = '' + if path ~= '' then + path_postfix = string.format(' %s', shellescape(path)) 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: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 -- and edit a not-yet-existing directory. return callback() @@ -165,12 +165,12 @@ function SSHFS:list_dir(url, path, callback) local entries = {} local cache_entries = {} 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) - if name == "." then + if name == '.' then dir_meta[url] = meta - elseif name ~= ".." then - if type == "link" then + elseif name ~= '..' then + if type == 'link' then any_links = true end 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 -- resolve the type of the link target 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) -- 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) end assert(link_lines) 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) - if ok and name ~= "." and name ~= ".." then + if ok and name ~= '.' and name ~= '..' then 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 = { type = type, size = meta.size, @@ -217,40 +217,40 @@ end ---@param path string ---@param callback fun(err: nil|string) 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 ---@param path string ---@param callback fun(err: nil|string) 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 ---@param path string ---@param link string ---@param callback fun(err: nil|string) 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 ---@param path string ---@param callback fun(err: nil|string) 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 ---@param src string ---@param dest string ---@param callback fun(err: nil|string) 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 ---@param src string ---@param dest string ---@param callback fun(err: nil|string) 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 function SSHFS:get_dir_meta(url) diff --git a/lua/oil/adapters/test.lua b/lua/oil/adapters/test.lua index c4176ef..2e80e9a 100644 --- a/lua/oil/adapters/test.lua +++ b/lua/oil/adapters/test.lua @@ -1,5 +1,5 @@ -local cache = require("oil.cache") -local util = require("oil.util") +local cache = require('oil.cache') +local util = require('oil.util') local M = {} ---@param url string @@ -32,24 +32,24 @@ end ---@param entry_type oil.EntryType ---@return oil.InternalEntry M.test_set = function(path, entry_type) - if path == "/" then + if path == '/' then return {} end - local parent = vim.fn.fnamemodify(path, ":h") + local parent = vim.fn.fnamemodify(path, ':h') if parent ~= path then - M.test_set(parent, "directory") + M.test_set(parent, 'directory') end parent = util.addslash(parent) if not dir_listing[parent] then dir_listing[parent] = {} end - local name = vim.fn.fnamemodify(path, ":t") + local name = vim.fn.fnamemodify(path, ':t') local entry = { name = name, entry_type = entry_type, } 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) end @@ -68,12 +68,12 @@ end ---@param action oil.Action ---@return string M.render_action = function(action) - if action.type == "create" or action.type == "delete" then - return string.format("%s %s", action.type:upper(), action.url) - elseif action.type == "move" or action.type == "copy" then - return string.format(" %s %s -> %s", action.type:upper(), action.src_url, action.dest_url) + if action.type == 'create' or action.type == 'delete' then + return string.format('%s %s', action.type:upper(), action.url) + elseif action.type == 'move' or action.type == 'copy' then + return string.format(' %s %s -> %s', action.type:upper(), action.src_url, action.dest_url) else - error("Bad action type") + error('Bad action type') end end diff --git a/lua/oil/adapters/trash.lua b/lua/oil/adapters/trash.lua index b007074..9ec3e52 100644 --- a/lua/oil/adapters/trash.lua +++ b/lua/oil/adapters/trash.lua @@ -1,9 +1,9 @@ -local fs = require("oil.fs") +local fs = require('oil.fs') if fs.is_mac then - return require("oil.adapters.trash.mac") + return require('oil.adapters.trash.mac') elseif fs.is_windows then - return require("oil.adapters.trash.windows") + return require('oil.adapters.trash.windows') else - return require("oil.adapters.trash.freedesktop") + return require('oil.adapters.trash.freedesktop') end diff --git a/lua/oil/adapters/trash/freedesktop.lua b/lua/oil/adapters/trash/freedesktop.lua index 18b7e4c..1e1d34d 100644 --- a/lua/oil/adapters/trash/freedesktop.lua +++ b/lua/oil/adapters/trash/freedesktop.lua @@ -1,11 +1,11 @@ -- Based on the FreeDesktop.org trash specification -- https://specifications.freedesktop.org/trash/1.0/ -local cache = require("oil.cache") -local config = require("oil.config") -local constants = require("oil.constants") -local files = require("oil.adapters.files") -local fs = require("oil.fs") -local util = require("oil.util") +local cache = require('oil.cache') +local config = require('oil.config') +local constants = require('oil.constants') +local files = require('oil.adapters.files') +local fs = require('oil.fs') +local util = require('oil.util') local uv = vim.uv or vim.loop local FIELD_META = constants.FIELD_META @@ -14,8 +14,8 @@ local M = {} local function ensure_trash_dir(path) local mode = 448 -- 0700 - fs.mkdirp(fs.join(path, "info"), mode) - fs.mkdirp(fs.join(path, "files"), mode) + fs.mkdirp(fs.join(path, 'info'), mode) + fs.mkdirp(fs.join(path, 'files'), mode) end ---Gets the location of the home trash dir, creating it if necessary @@ -23,9 +23,9 @@ end local function get_home_trash_dir() local xdg_home = vim.env.XDG_DATA_HOME 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 - local trash_dir = fs.join(xdg_home, "Trash") + local trash_dir = fs.join(xdg_home, 'Trash') ensure_trash_dir(trash_dir) return trash_dir end @@ -43,13 +43,13 @@ end local function get_top_trash_dirs(path) local dirs = {} 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 local stat = uv.fs_lstat(top_trash_dir) if stat and not dev then dev = stat.dev 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())) ensure_trash_dir(trash_dir) table.insert(dirs, trash_dir) @@ -58,7 +58,7 @@ local function get_top_trash_dirs(path) -- Also search for the .Trash-$uid 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 } ) 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] end - local parent = vim.fn.fnamemodify(path, ":h") - local next_parent = vim.fn.fnamemodify(parent, ":h") + local parent = vim.fn.fnamemodify(path, ':h') + local next_parent = vim.fn.fnamemodify(parent, ':h') while parent ~= next_parent and uv.fs_lstat(next_parent).dev == dev do parent = next_parent - next_parent = vim.fn.fnamemodify(parent, ":h") + next_parent = vim.fn.fnamemodify(parent, ':h') 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) return top_trash end @@ -116,7 +116,7 @@ end M.normalize_url = function(url, callback) local scheme, path = util.parse_url(url) 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( os_path, vim.schedule_wrap(function(err, new_os_path) @@ -140,10 +140,10 @@ M.get_entry_path = function(url, entry, cb) return end 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) end - cb("oil://" .. path) + cb('oil://' .. path) end ---@class oil.TrashInfo @@ -156,10 +156,10 @@ end ---@param info_file string ---@param cb fun(err?: string, info?: oil.TrashInfo) local function read_trash_info(info_file, cb) - if not vim.endswith(info_file, ".trashinfo") then - return cb("File is not .trashinfo") + if not vim.endswith(info_file, '.trashinfo') then + return cb('File is not .trashinfo') end - uv.fs_open(info_file, "r", 448, function(err, fd) + uv.fs_open(info_file, 'r', 448, function(err, fd) if err then return cb(err) end @@ -182,32 +182,32 @@ local function read_trash_info(info_file, cb) local trash_info = { info_file = info_file, } - local lines = vim.split(content, "\r?\n") - if lines[1] ~= "[Trash Info]" then - return cb("File missing [Trash Info] header") + local lines = vim.split(content, '\r?\n') + if lines[1] ~= '[Trash Info]' then + return cb('File missing [Trash Info] header') 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 - local key, value = unpack(vim.split(line, "=", { plain = true, trimempty = true })) - if key == "Path" and not trash_info.original_path then - if not vim.startswith(value, "/") then + local key, value = unpack(vim.split(line, '=', { plain = true, trimempty = true })) + if key == 'Path' and not trash_info.original_path then + if not vim.startswith(value, '/') then value = fs.join(trash_base, value) end trash_info.original_path = value - elseif key == "DeletionDate" and not trash_info.deletion_date then - trash_info.deletion_date = vim.fn.strptime("%Y-%m-%dT%H:%M:%S", value) + elseif key == 'DeletionDate' and not trash_info.deletion_date then + trash_info.deletion_date = vim.fn.strptime('%Y-%m-%dT%H:%M:%S', value) end end 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 - local basename = vim.fn.fnamemodify(info_file, ":t:r") - trash_info.trash_file = fs.join(trash_base, "files", basename) + local basename = vim.fn.fnamemodify(info_file, ':t:r') + trash_info.trash_file = fs.join(trash_base, 'files', basename) uv.fs_lstat(trash_info.trash_file, function(trash_stat_err, trash_stat) if trash_stat_err then - cb(".trashinfo file points to non-existant file") + cb('.trashinfo file points to non-existant file') else trash_info.stat = trash_stat ---@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 -- all entries if we are in the top root path "/" if trash_idx == 1 then - show_all_files = path == "/" + show_all_files = path == '/' 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 uv.fs_opendir(info_dir, function(open_err, fd) 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 -- and edit a not-yet-existing directory. return read_next_trash_dir() @@ -286,12 +286,12 @@ M.list = function(url, column_defs, cb) -- files. poll() 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 - local name = vim.fn.fnamemodify(info.trash_file, ":t") + local name = vim.fn.fnamemodify(info.trash_file, ':t') ---@diagnostic disable-next-line: undefined-field 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] = { stat = info.stat, 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 local name = parent:sub(path:len() + 1) local next_par = vim.fs.dirname(name) - while next_par ~= "." do + while next_par ~= '.' do name = next_par next_par = vim.fs.dirname(name) end ---@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] = { stat = info.stat, @@ -348,7 +348,7 @@ local file_columns = {} local current_year -- Make sure we run this import-time effect in the main loop (mostly for tests) vim.schedule(function() - current_year = vim.fn.strftime("%Y") + current_year = vim.fn.strftime('%Y') end) file_columns.mtime = { @@ -368,11 +368,11 @@ file_columns.mtime = { if fmt then ret = vim.fn.strftime(fmt, time) else - local year = vim.fn.strftime("%Y", time) + local year = vim.fn.strftime('%Y', time) if year ~= current_year then - ret = vim.fn.strftime("%b %d %Y", time) + ret = vim.fn.strftime('%b %d %Y', time) else - ret = vim.fn.strftime("%b %d %H:%M", time) + ret = vim.fn.strftime('%b %d %H:%M', time) end end return ret @@ -393,11 +393,11 @@ file_columns.mtime = { local fmt = conf and conf.format local pattern if fmt then - pattern = fmt:gsub("%%.", "%%S+") + pattern = fmt:gsub('%%.', '%%S+') else - pattern = "%S+%s+%d+%s+%d%d:?%d%d" + pattern = '%S+%s+%d+%s+%d%d:?%d%d' end - return line:match("^(" .. pattern .. ")%s+(.+)$") + return line:match('^(' .. pattern .. ')%s+(.+)$') end, } @@ -407,25 +407,26 @@ M.get_column = function(name) return file_columns[name] end -M.supported_cross_adapter_actions = { files = "move" } +M.supported_cross_adapter_actions = { files = 'move' } ---@param action oil.Action ---@return boolean M.filter_action = function(action) - if action.type == "create" then + if action.type == 'create' then return false - elseif action.type == "delete" then + elseif action.type == 'delete' then local entry = assert(cache.get_entry_by_url(action.url)) local meta = entry[FIELD_META] 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 dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url)) - return src_adapter.name == "files" or dest_adapter.name == "files" - elseif action.type == "copy" then + return src_adapter.name == 'files' or dest_adapter.name == 'files' + -- selene: allow(if_same_then_else) + elseif action.type == 'copy' then local src_adapter = assert(config.get_adapter_by_scheme(action.src_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 error(string.format("Bad action type '%s'", action.type)) end @@ -434,7 +435,7 @@ end ---@param err oil.ParseError ---@return boolean M.filter_error = function(err) - if err.message == "Duplicate filename" then + if err.message == 'Duplicate filename' then return false end return true @@ -443,44 +444,44 @@ end ---@param action oil.Action ---@return string 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 meta = entry[FIELD_META] ---@type oil.TrashInfo local trash_info = assert(meta).trash_info local short_path = fs.shorten_path(trash_info.original_path) - return string.format(" PURGE %s", short_path) - elseif action.type == "move" then + return string.format(' PURGE %s', short_path) + elseif action.type == 'move' then local src_adapter = assert(config.get_adapter_by_scheme(action.src_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) assert(path) local short_path = files.to_short_os_path(path, action.entry_type) - return string.format(" TRASH %s", short_path) - elseif dest_adapter.name == "files" then + return string.format(' TRASH %s', short_path) + elseif dest_adapter.name == 'files' then local _, path = util.parse_url(action.dest_url) assert(path) 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 - error("Must be moving files into or out of trash") + error('Must be moving files into or out of trash') end - elseif action.type == "copy" then + elseif action.type == 'copy' then local src_adapter = assert(config.get_adapter_by_scheme(action.src_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) assert(path) local short_path = files.to_short_os_path(path, action.entry_type) - return string.format(" COPY %s -> TRASH", short_path) - elseif dest_adapter.name == "files" then + return string.format(' COPY %s -> TRASH', short_path) + elseif dest_adapter.name == 'files' then local _, path = util.parse_url(action.dest_url) assert(path) 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 - error("Must be copying files into or out of trash") + error('Must be copying files into or out of trash') end else error(string.format("Bad action type '%s'", action.type)) @@ -490,7 +491,7 @@ end ---@param trash_info oil.TrashInfo ---@param cb fun(err?: string) 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 return cb(err) end @@ -505,15 +506,15 @@ end local function write_info_file(path, info_path, cb) uv.fs_open( info_path, - "w", + 'w', 448, vim.schedule_wrap(function(err, fd) if err then return cb(err) end assert(fd) - 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 deletion_date = vim.fn.strftime('%Y-%m-%dT%H:%M:%S') + local contents = string.format('[Trash Info]\nPath=%s\nDeletionDate=%s', path, deletion_date) uv.fs_write(fd, contents, function(write_err) uv.fs_close(fd, function(close_err) cb(write_err or close_err) @@ -529,9 +530,9 @@ local function create_trash_info(path, cb) local trash_dir = get_write_trash_dir(path) local basename = vim.fs.basename(path) local now = os.time() - local name = string.format("%s-%d.%d", basename, now, math.random(100000, 999999)) - local dest_path = fs.join(trash_dir, "files", name) - local dest_info = fs.join(trash_dir, "info", name .. ".trashinfo") + local name = string.format('%s-%d.%d', basename, now, math.random(100000, 999999)) + local dest_path = fs.join(trash_dir, 'files', name) + local dest_info = fs.join(trash_dir, 'info', name .. '.trashinfo') uv.fs_lstat(path, function(err, stat) if err then return cb(err) @@ -557,19 +558,19 @@ end ---@param action oil.Action ---@param cb fun(err: nil|string) 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 meta = entry[FIELD_META] ---@type oil.TrashInfo local trash_info = assert(meta).trash_info 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 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) M.delete_to_trash(assert(path), cb) - elseif dest_adapter.name == "files" then + elseif dest_adapter.name == 'files' then -- Restore local _, dest_path = util.parse_url(action.dest_url) assert(dest_path) @@ -584,23 +585,23 @@ M.perform_action = function(action, cb) uv.fs_unlink(trash_info.info_file, cb) end) else - error("Must be moving files into or out of trash") + error('Must be moving files into or out of trash') end - elseif action.type == "copy" then + elseif action.type == 'copy' then local src_adapter = assert(config.get_adapter_by_scheme(action.src_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) assert(path) create_trash_info(path, function(err, trash_info) if err then cb(err) 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)) end end) - elseif dest_adapter.name == "files" then + elseif dest_adapter.name == 'files' then -- Restore local _, dest_path = util.parse_url(action.dest_url) assert(dest_path) @@ -610,10 +611,10 @@ M.perform_action = function(action, cb) local trash_info = assert(meta).trash_info fs.recursive_copy(action.entry_type, trash_info.trash_file, dest_path, cb) else - error("Must be moving files into or out of trash") + error('Must be moving files into or out of trash') end else - cb(string.format("Bad action type: %s", action.type)) + cb(string.format('Bad action type: %s', action.type)) end end @@ -624,7 +625,7 @@ M.delete_to_trash = function(path, cb) if err then cb(err) 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)) end end) diff --git a/lua/oil/adapters/trash/mac.lua b/lua/oil/adapters/trash/mac.lua index 54d68a3..e1c052d 100644 --- a/lua/oil/adapters/trash/mac.lua +++ b/lua/oil/adapters/trash/mac.lua @@ -1,8 +1,8 @@ -local cache = require("oil.cache") -local config = require("oil.config") -local files = require("oil.adapters.files") -local fs = require("oil.fs") -local util = require("oil.util") +local cache = require('oil.cache') +local config = require('oil.config') +local files = require('oil.adapters.files') +local fs = require('oil.fs') +local util = require('oil.util') local uv = vim.uv or vim.loop @@ -15,7 +15,7 @@ end ---Gets the location of the home trash dir, creating it if necessary ---@return string 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) return trash_dir end @@ -25,7 +25,7 @@ end M.normalize_url = function(url, callback) local scheme, path = util.parse_url(url) assert(path) - callback(scheme .. "/") + callback(scheme .. '/') end ---@param url string @@ -34,8 +34,8 @@ end M.get_entry_path = function(url, entry, cb) local trash_dir = get_trash_dir() local path = fs.join(trash_dir, entry.name) - if entry.type == "directory" then - path = "oil://" .. path + if entry.type == 'directory' then + path = 'oil://' .. path end cb(path) end @@ -51,7 +51,7 @@ M.list = function(url, column_defs, cb) ---@diagnostic disable-next-line: param-type-mismatch, discard-returns uv.fs_opendir(trash_dir, function(open_err, fd) 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 -- and edit a not-yet-existing directory. return cb() @@ -111,35 +111,35 @@ M.get_column = function(name) return nil end -M.supported_cross_adapter_actions = { files = "move" } +M.supported_cross_adapter_actions = { files = 'move' } ---@param action oil.Action ---@return string M.render_action = function(action) - if action.type == "create" then - return string.format("CREATE %s", action.url) - elseif action.type == "delete" then - return string.format(" PURGE %s", action.url) - elseif action.type == "move" then + if action.type == 'create' then + return string.format('CREATE %s', action.url) + elseif action.type == 'delete' then + return string.format(' PURGE %s', action.url) + elseif action.type == 'move' then local src_adapter = assert(config.get_adapter_by_scheme(action.src_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) assert(path) local short_path = files.to_short_os_path(path, action.entry_type) - return string.format(" TRASH %s", short_path) - elseif dest_adapter.name == "files" then + return string.format(' TRASH %s', short_path) + elseif dest_adapter.name == 'files' then local _, path = util.parse_url(action.dest_url) assert(path) 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 - 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 - elseif action.type == "copy" then - return string.format(" %s %s -> %s", action.type:upper(), action.src_url, action.dest_url) + elseif action.type == 'copy' then + return string.format(' %s %s -> %s', action.type:upper(), action.src_url, action.dest_url) else - error("Bad action type") + error('Bad action type') end end @@ -147,20 +147,20 @@ end ---@param cb fun(err: nil|string) M.perform_action = function(action, cb) local trash_dir = get_trash_dir() - if action.type == "create" then + if action.type == 'create' then local _, path = util.parse_url(action.url) assert(path) path = trash_dir .. path - if action.entry_type == "directory" then + if action.entry_type == 'directory' then uv.fs_mkdir(path, 493, function(err) -- Ignore if the directory already exists - if not err or err:match("^EEXIST:") then + if not err or err:match('^EEXIST:') then cb() else cb(err) end end) -- 0755 - elseif action.entry_type == "link" and action.link then + elseif action.entry_type == 'link' and action.link then local flags = nil local target = fs.posix_to_os_path(action.link) ---@diagnostic disable-next-line: param-type-mismatch @@ -168,33 +168,33 @@ M.perform_action = function(action, cb) else fs.touch(path, config.new_file_mode, cb) end - elseif action.type == "delete" then + elseif action.type == 'delete' then local _, path = util.parse_url(action.url) assert(path) local fullpath = trash_dir .. path 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 dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url)) local _, src_path = util.parse_url(action.src_url) local _, dest_path = util.parse_url(action.dest_url) assert(src_path and dest_path) - if src_adapter.name == "files" then + if src_adapter.name == 'files' then dest_path = trash_dir .. dest_path - elseif dest_adapter.name == "files" then + elseif dest_adapter.name == 'files' then src_path = trash_dir .. src_path else dest_path = trash_dir .. dest_path src_path = trash_dir .. src_path end - if action.type == "move" then + if action.type == 'move' then fs.recursive_move(action.entry_type, src_path, dest_path, cb) else fs.recursive_copy(action.entry_type, src_path, dest_path, cb) end else - cb(string.format("Bad action type: %s", action.type)) + cb(string.format('Bad action type: %s', action.type)) end end @@ -212,8 +212,8 @@ M.delete_to_trash = function(path, cb) end assert(src_stat) if uv.fs_lstat(dest) then - local date_str = vim.fn.strftime(" %Y-%m-%dT%H:%M:%S") - local name_pieces = vim.split(basename, ".", { plain = true }) + local date_str = vim.fn.strftime(' %Y-%m-%dT%H:%M:%S') + local name_pieces = vim.split(basename, '.', { plain = true }) if #name_pieces > 1 then table.insert(name_pieces, #name_pieces - 1, date_str) basename = table.concat(name_pieces) diff --git a/lua/oil/adapters/trash/windows.lua b/lua/oil/adapters/trash/windows.lua index f7634e1..c761f82 100644 --- a/lua/oil/adapters/trash/windows.lua +++ b/lua/oil/adapters/trash/windows.lua @@ -1,11 +1,11 @@ -local util = require("oil.util") +local util = require('oil.util') local uv = vim.uv or vim.loop -local cache = require("oil.cache") -local config = require("oil.config") -local constants = require("oil.constants") -local files = require("oil.adapters.files") -local fs = require("oil.fs") -local powershell_trash = require("oil.adapters.trash.windows.powershell-trash") +local cache = require('oil.cache') +local config = require('oil.config') +local constants = require('oil.constants') +local files = require('oil.adapters.files') +local fs = require('oil.fs') +local powershell_trash = require('oil.adapters.trash.windows.powershell-trash') local FIELD_META = constants.FIELD_META local FIELD_TYPE = constants.FIELD_TYPE @@ -15,22 +15,22 @@ local M = {} ---@return string local function get_trash_dir() 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 return trash_dir end - trash_dir = "C:\\$Recycle.Bin" + trash_dir = 'C:\\$Recycle.Bin' if vim.fn.isdirectory(trash_dir) == 1 then return trash_dir end - error("No trash found") + error('No trash found') end ---@param path string ---@return string local win_addslash = function(path) - if not vim.endswith(path, "\\") then - return path .. "\\" + if not vim.endswith(path, '\\') then + return path .. '\\' else return path end @@ -61,7 +61,7 @@ M.list = function(url, column_defs, cb) local raw_displayed_entries = vim.tbl_filter( ---@param entry {IsFolder: boolean, DeletionDate: integer, Name: string, Path: string, OriginalPath: string} 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_subpath = fs.is_subpath(path, parent) 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} ---@return {[1]:nil, [2]:string, [3]:string, [4]:{stat: uv_fs_t, trash_info: oil.WindowsTrashInfo, display_name: string}} 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 local cache_entry if path == parent or show_all_files then - 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_tail = assert(vim.fn.fnamemodify(entry.Path, ':t')) + local deleted_file_head = assert(vim.fn.fnamemodify(entry.Path, ':h')) local info_file_head = deleted_file_head --- @type string? local info_file 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>. -- the hash is the same for the deleted file and the info 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 - 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 - info_file_tail = "$I" .. info_file_tail - info_file = info_file_head .. "\\" .. info_file_tail + info_file_tail = '$I' .. info_file_tail + info_file = info_file_head .. '\\' .. info_file_tail end cache_entry[FIELD_META] = { 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 local name = parent:sub(path:len() + 1) local next_par = vim.fs.dirname(name) - while next_par ~= "." do + while next_par ~= '.' do name = next_par 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] = {} end @@ -132,7 +132,7 @@ end local current_year -- Make sure we run this import-time effect in the main loop (mostly for tests) vim.schedule(function() - current_year = vim.fn.strftime("%Y") + current_year = vim.fn.strftime('%Y') end) local file_columns = {} @@ -153,11 +153,11 @@ file_columns.mtime = { if fmt then ret = vim.fn.strftime(fmt, time) else - local year = vim.fn.strftime("%Y", time) + local year = vim.fn.strftime('%Y', time) if year ~= current_year then - ret = vim.fn.strftime("%b %d %Y", time) + ret = vim.fn.strftime('%b %d %Y', time) else - ret = vim.fn.strftime("%b %d %H:%M", time) + ret = vim.fn.strftime('%b %d %H:%M', time) end end return ret @@ -178,11 +178,11 @@ file_columns.mtime = { local fmt = conf and conf.format local pattern if fmt then - pattern = fmt:gsub("%%.", "%%S+") + pattern = fmt:gsub('%%.', '%%S+') else - pattern = "%S+%s+%d+%s+%d%d:?%d%d" + pattern = '%S+%s+%d+%s+%d%d:?%d%d' end - return line:match("^(" .. pattern .. ")%s+(.+)$") + return line:match('^(' .. pattern .. ')%s+(.+)$') end, } @@ -195,20 +195,21 @@ end ---@param action oil.Action ---@return boolean M.filter_action = function(action) - if action.type == "create" then + if action.type == 'create' then return false - elseif action.type == "delete" then + elseif action.type == 'delete' then local entry = assert(cache.get_entry_by_url(action.url)) local meta = entry[FIELD_META] 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 dest_adapter = assert(config.get_adapter_by_scheme(action.dest_url)) - return src_adapter.name == "files" or dest_adapter.name == "files" - elseif action.type == "copy" then + return src_adapter.name == 'files' or dest_adapter.name == 'files' + -- selene: allow(if_same_then_else) + elseif action.type == 'copy' then local src_adapter = assert(config.get_adapter_by_scheme(action.src_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 error(string.format("Bad action type '%s'", action.type)) end @@ -219,7 +220,7 @@ end M.normalize_url = function(url, callback) local scheme, path = util.parse_url(url) 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) uv.fs_realpath( os_path, @@ -244,16 +245,16 @@ M.get_entry_path = function(url, entry, cb) end 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) end - cb("oil://" .. path) + cb('oil://' .. path) end ---@param err oil.ParseError ---@return boolean M.filter_error = function(err) - if err.message == "Duplicate filename" then + if err.message == 'Duplicate filename' then return false end return true @@ -262,44 +263,44 @@ end ---@param action oil.Action ---@return string 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 meta = entry[FIELD_META] ---@type oil.WindowsTrashInfo local trash_info = assert(meta).trash_info local short_path = fs.shorten_path(trash_info.original_path) - return string.format(" PURGE %s", short_path) - elseif action.type == "move" then + return string.format(' PURGE %s', short_path) + elseif action.type == 'move' then local src_adapter = assert(config.get_adapter_by_scheme(action.src_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) assert(path) local short_path = files.to_short_os_path(path, action.entry_type) - return string.format(" TRASH %s", short_path) - elseif dest_adapter.name == "files" then + return string.format(' TRASH %s', short_path) + elseif dest_adapter.name == 'files' then local _, path = util.parse_url(action.dest_url) assert(path) 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 - error("Must be moving files into or out of trash") + error('Must be moving files into or out of trash') end - elseif action.type == "copy" then + elseif action.type == 'copy' then local src_adapter = assert(config.get_adapter_by_scheme(action.src_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) assert(path) local short_path = files.to_short_os_path(path, action.entry_type) - return string.format(" COPY %s -> TRASH", short_path) - elseif dest_adapter.name == "files" then + return string.format(' COPY %s -> TRASH', short_path) + elseif dest_adapter.name == 'files' then local _, path = util.parse_url(action.dest_url) assert(path) 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 - error("Must be copying files into or out of trash") + error('Must be copying files into or out of trash') end else error(string.format("Bad action type '%s'", action.type)) @@ -309,11 +310,11 @@ end ---@param trash_info oil.WindowsTrashInfo ---@param cb fun(err?: string, raw_entries: oil.WindowsRawEntry[]?) 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 return cb(err) end - fs.recursive_delete("file", trash_info.trash_file, cb) + fs.recursive_delete('file', trash_info.trash_file, cb) end) end @@ -321,7 +322,7 @@ end ---@param type string ---@param cb fun(err?: string, trash_info?: oil.TrashInfo) 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 fs.recursive_copy( type, @@ -346,19 +347,19 @@ end ---@param action oil.Action ---@param cb fun(err: nil|string) 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 meta = entry[FIELD_META] --[[@as {stat: uv_fs_t, trash_info: oil.WindowsTrashInfo, display_name: string}]] local trash_info = meta and meta.trash_info 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 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) M.delete_to_trash(assert(path), cb) - elseif dest_adapter.name == "files" then + elseif dest_adapter.name == 'files' then -- Restore local _, dest_path = util.parse_url(action.dest_url) assert(dest_path) @@ -373,16 +374,16 @@ M.perform_action = function(action, cb) uv.fs_unlink(trash_info.info_file, cb) 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 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) assert(path) path = fs.posix_to_os_path(path) local entry = assert(cache.get_entry_by_url(action.src_url)) create_trash_info_and_copy(path, entry[FIELD_TYPE], cb) - elseif dest_adapter.name == "files" then + elseif dest_adapter.name == 'files' then -- Restore local _, dest_path = util.parse_url(action.dest_url) assert(dest_path) @@ -392,14 +393,14 @@ M.perform_action = function(action, cb) local trash_info = meta and meta.trash_info fs.recursive_copy(action.entry_type, trash_info.trash_file, dest_path, cb) else - error("Must be moving files into or out of trash") + error('Must be moving files into or out of trash') end else - cb(string.format("Bad action type: %s", action.type)) + cb(string.format('Bad action type: %s', action.type)) end end -M.supported_cross_adapter_actions = { files = "move" } +M.supported_cross_adapter_actions = { files = 'move' } ---@param path string ---@param cb fun(err?: string) diff --git a/lua/oil/adapters/trash/windows/powershell-connection.lua b/lua/oil/adapters/trash/windows/powershell-connection.lua index 332defb..d897fe8 100644 --- a/lua/oil/adapters/trash/windows/powershell-connection.lua +++ b/lua/oil/adapters/trash/windows/powershell-connection.lua @@ -41,23 +41,23 @@ function PowershellConnection:_init(init_command) -- 65001 is the UTF-8 codepage -- powershell needs to be launched with the UTF-8 codepage to use it for both stdin and stdout local jid = vim.fn.jobstart({ - "cmd", - "/c", + 'cmd', + '/c', '"chcp 65001 && powershell -NoProfile -NoLogo -ExecutionPolicy Bypass -NoExit -Command -"', }, { ---@param data string[] on_stdout = function(_, data) for _, fragment in ipairs(data) do - if fragment:find("===DONE%((%a+)%)===") then + if fragment:find('===DONE%((%a+)%)===') then self.is_reading_data = false - local output = table.concat(self.stdout, "") + local output = table.concat(self.stdout, '') local cb = self.commands[1].cb table.remove(self.commands, 1) - local success = fragment:match("===DONE%((%a+)%)===") - if success == "True" then + local success = fragment:match('===DONE%((%a+)%)===') + if success == 'True' then cb(nil, output) - elseif success == "False" then - cb(success .. ": " .. output, output) + elseif success == 'False' then + cb(success .. ': ' .. output, output) end self.stdout = {} self:_consume() diff --git a/lua/oil/adapters/trash/windows/powershell-trash.lua b/lua/oil/adapters/trash/windows/powershell-trash.lua index d050bb0..edc9ee6 100644 --- a/lua/oil/adapters/trash/windows/powershell-trash.lua +++ b/lua/oil/adapters/trash/windows/powershell-trash.lua @@ -1,5 +1,5 @@ -- 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 ---@field IsFolder boolean diff --git a/lua/oil/cache.lua b/lua/oil/cache.lua index ef5597c..800c97a 100644 --- a/lua/oil/cache.lua +++ b/lua/oil/cache.lua @@ -1,5 +1,5 @@ -local constants = require("oil.constants") -local util = require("oil.util") +local constants = require('oil.constants') +local util = require('oil.util') local M = {} local FIELD_ID = constants.FIELD_ID @@ -28,7 +28,7 @@ local _cached_id_fmt M.format_id = function(id) if not _cached_id_fmt then 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 return _cached_id_fmt:format(id) end @@ -125,8 +125,8 @@ end M.get_entry_by_url = function(url) local scheme, path = util.parse_url(url) assert(path) - local parent_url = scheme .. vim.fn.fnamemodify(path, ":h") - local basename = vim.fn.fnamemodify(path, ":t") + local parent_url = scheme .. vim.fn.fnamemodify(path, ':h') + local basename = vim.fn.fnamemodify(path, ':t') return M.list_url(parent_url)[basename] end @@ -135,7 +135,7 @@ end M.get_parent_url = function(id) local url = parent_url_by_id[id] if not url then - error(string.format("Entry %d missing parent url", id)) + error(string.format('Entry %d missing parent url', id)) end return url end @@ -149,32 +149,32 @@ end ---@param action oil.Action M.perform_action = function(action) - if action.type == "create" then + if action.type == 'create' then local scheme, path = util.parse_url(action.url) assert(path) - local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ":h")) - local name = vim.fn.fnamemodify(path, ":t") + local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ':h')) + local name = vim.fn.fnamemodify(path, ':t') 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) assert(path) - local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ":h")) - local name = vim.fn.fnamemodify(path, ":t") + local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ':h')) + local name = vim.fn.fnamemodify(path, ':t') local entry = url_directory[parent_url][name] url_directory[parent_url][name] = nil entries_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) assert(src_path) - 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_parent_url = util.addslash(src_scheme .. vim.fn.fnamemodify(src_path, ':h')) + local src_name = vim.fn.fnamemodify(src_path, ':t') local entry = url_directory[src_parent_url][src_name] local dest_scheme, dest_path = util.parse_url(action.dest_url) assert(dest_path) - 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_parent_url = util.addslash(dest_scheme .. vim.fn.fnamemodify(dest_path, ':h')) + local dest_name = vim.fn.fnamemodify(dest_path, ':t') url_directory[src_parent_url][src_name] = nil local dest_parent = url_directory[dest_parent_url] @@ -188,13 +188,14 @@ M.perform_action = function(action) parent_url_by_id[entry[FIELD_ID]] = dest_parent_url entry[FIELD_NAME] = dest_name 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) assert(path) - local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ":h")) - local name = vim.fn.fnamemodify(path, ":t") + local parent_url = util.addslash(scheme .. vim.fn.fnamemodify(path, ':h')) + local name = vim.fn.fnamemodify(path, ':t') M.create_and_store_entry(parent_url, name, action.entry_type) - elseif action.type == "change" then + -- selene: allow(empty_if) + elseif action.type == 'change' then -- Cache doesn't need to update else ---@diagnostic disable-next-line: undefined-field diff --git a/lua/oil/clipboard.lua b/lua/oil/clipboard.lua index 04498f5..84df407 100644 --- a/lua/oil/clipboard.lua +++ b/lua/oil/clipboard.lua @@ -1,11 +1,11 @@ -local cache = require("oil.cache") -local columns = require("oil.columns") -local config = require("oil.config") -local fs = require("oil.fs") -local oil = require("oil") -local parser = require("oil.mutator.parser") -local util = require("oil.util") -local view = require("oil.view") +local cache = require('oil.cache') +local columns = require('oil.columns') +local config = require('oil.config') +local fs = require('oil.fs') +local oil = require('oil') +local parser = require('oil.mutator.parser') +local util = require('oil.util') +local view = require('oil.view') local M = {} @@ -16,10 +16,10 @@ local function get_linux_session_type() return end xdg_session_type = xdg_session_type:lower() - if xdg_session_type:find("x11") then - return "x11" - elseif xdg_session_type:find("wayland") then - return "wayland" + if xdg_session_type:find('x11') then + return 'x11' + elseif xdg_session_type:find('wayland') then + return 'wayland' else return nil end @@ -29,9 +29,9 @@ end local function is_linux_desktop_gnome() local cur_desktop = vim.env.XDG_CURRENT_DESKTOP local session_desktop = vim.env.XDG_SESSION_DESKTOP - local idx = session_desktop and session_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" + local idx = session_desktop and session_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' end ---@param winid integer @@ -55,7 +55,7 @@ end ---@param entry oil.InternalEntry local function remove_entry_from_parent_buffer(parent_url, entry) 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 column_defs = columns.get_supported_columns(adapter) local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) @@ -77,7 +77,7 @@ end ---@param delete_original? boolean local function paste_paths(paths, delete_original) local bufnr = vim.api.nvim_get_current_buf() - local scheme = "oil://" + local scheme = 'oil://' local adapter = assert(config.get_adapter_by_scheme(scheme)) local column_defs = columns.get_supported_columns(scheme) 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 for _, path in ipairs(paths) do -- Trim the trailing slash off directories - if vim.endswith(path, "/") then + if vim.endswith(path, '/') then path = path:sub(1, -2) end @@ -114,7 +114,7 @@ local function paste_paths(paths, delete_original) local cursor = vim.api.nvim_win_get_cursor(winid) local complete_loading = util.cb_collect(#vim.tbl_keys(parent_urls), function(err) 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 -- 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) @@ -149,8 +149,8 @@ end ---@return integer end local function range_from_selection() -- [bufnum, lnum, col, off]; both row and column 1-indexed - local start = vim.fn.getpos("v") - local end_ = vim.fn.getpos(".") + local start = vim.fn.getpos('v') + local end_ = vim.fn.getpos('.') local start_row = start[2] local end_row = end_[2] @@ -164,16 +164,16 @@ end M.copy_to_system_clipboard = function() local dir = oil.get_current_dir() 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 end local entries = {} 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 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 ) return @@ -184,7 +184,7 @@ M.copy_to_system_clipboard = function() end -- leave visual mode - vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, false, true), "n", true) + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('', true, false, true), 'n', true) else table.insert(entries, oil.get_cursor_entry()) end @@ -193,7 +193,7 @@ M.copy_to_system_clipboard = function() entries = vim.tbl_values(entries) 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 end local paths = {} @@ -204,38 +204,38 @@ M.copy_to_system_clipboard = function() local stdin if fs.is_mac then cmd = { - "osascript", - "-e", - "on run args", - "-e", - "set the clipboard to POSIX file (first item of args)", - "-e", - "end run", + 'osascript', + '-e', + 'on run args', + '-e', + 'set the clipboard to POSIX file (first item of args)', + '-e', + 'end run', paths[1], } elseif fs.is_linux then local xdg_session_type = get_linux_session_type() - if xdg_session_type == "x11" then - vim.list_extend(cmd, { "xclip", "-i", "-selection", "clipboard" }) - elseif xdg_session_type == "wayland" then - table.insert(cmd, "wl-copy") + if xdg_session_type == 'x11' then + vim.list_extend(cmd, { 'xclip', '-i', '-selection', 'clipboard' }) + elseif xdg_session_type == 'wayland' then + table.insert(cmd, 'wl-copy') 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 end local urls = {} for _, path in ipairs(paths) do - table.insert(urls, "file://" .. path) + table.insert(urls, 'file://' .. path) end if is_linux_desktop_gnome() then - stdin = string.format("copy\n%s\0", table.concat(urls, "\n")) - vim.list_extend(cmd, { "-t", "x-special/gnome-copied-files" }) + stdin = string.format('copy\n%s\0', table.concat(urls, '\n')) + vim.list_extend(cmd, { '-t', 'x-special/gnome-copied-files' }) else - stdin = table.concat(urls, "\n") .. "\n" - vim.list_extend(cmd, { "-t", "text/uri-list" }) + stdin = table.concat(urls, '\n') .. '\n' + vim.list_extend(cmd, { '-t', 'text/uri-list' }) end 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 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) return end - local stderr = "" + local stderr = '' local jid = vim.fn.jobstart(cmd, { stderr_buffered = true, on_stderr = function(_, data) - stderr = table.concat(data, "\n") + stderr = table.concat(data, '\n') end, on_exit = function(j, exit_code) if exit_code ~= 0 then @@ -259,15 +259,15 @@ M.copy_to_system_clipboard = function() if #paths == 1 then vim.notify(string.format("Copied '%s' to system clipboard", paths[1])) 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, }) - assert(jid > 0, "Failed to start job") + assert(jid > 0, 'Failed to start job') if stdin then vim.api.nvim_chan_send(jid, stdin) - vim.fn.chanclose(jid, "stdin") + vim.fn.chanclose(jid, 'stdin') end end @@ -276,7 +276,7 @@ end local function handle_paste_output_mac(lines) local ret = {} for _, line in ipairs(lines) do - if not line:match("^%s*$") then + if not line:match('^%s*$') then table.insert(ret, line) end end @@ -288,7 +288,7 @@ end local function handle_paste_output_linux(lines) local ret = {} for _, line in ipairs(lines) do - local path = line:match("^file://(.+)$") + local path = line:match('^file://(.+)$') if path then table.insert(ret, util.url_unescape(path)) end @@ -306,37 +306,37 @@ M.paste_from_system_clipboard = function(delete_original) local handle_paste_output if fs.is_mac then cmd = { - "osascript", - "-e", - "on run", - "-e", - "POSIX path of (the clipboard as «class furl»)", - "-e", - "end run", + 'osascript', + '-e', + 'on run', + '-e', + 'POSIX path of (the clipboard as «class furl»)', + '-e', + 'end run', } handle_paste_output = handle_paste_output_mac elseif fs.is_linux then local xdg_session_type = get_linux_session_type() - if xdg_session_type == "x11" then - vim.list_extend(cmd, { "xclip", "-o", "-selection", "clipboard" }) - elseif xdg_session_type == "wayland" then - table.insert(cmd, "wl-paste") + if xdg_session_type == 'x11' then + vim.list_extend(cmd, { 'xclip', '-o', '-selection', 'clipboard' }) + elseif xdg_session_type == 'wayland' then + table.insert(cmd, 'wl-paste') 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 end 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 - vim.list_extend(cmd, { "-t", "text/uri-list" }) + vim.list_extend(cmd, { '-t', 'text/uri-list' }) end handle_paste_output = handle_paste_output_linux 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 end local paths - local stderr = "" + local stderr = '' if vim.fn.executable(cmd[1]) == 0 then vim.notify(string.format("Could not find executable '%s'", cmd[1]), vim.log.levels.ERROR) return @@ -345,26 +345,26 @@ M.paste_from_system_clipboard = function(delete_original) stdout_buffered = true, stderr_buffered = true, 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) end, on_stderr = function(_, data) - stderr = table.concat(data, "\n") + stderr = table.concat(data, '\n') end, on_exit = function(j, exit_code) if exit_code ~= 0 or not paths then vim.notify( - string.format("Error pasting from system clipboard: %s", stderr), + string.format('Error pasting from system clipboard: %s', stderr), vim.log.levels.ERROR ) 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 paste_paths(paths, delete_original) end end, }) - assert(jid > 0, "Failed to start job") + assert(jid > 0, 'Failed to start job') end return M diff --git a/lua/oil/columns.lua b/lua/oil/columns.lua index 6d76674..ef95fdd 100644 --- a/lua/oil/columns.lua +++ b/lua/oil/columns.lua @@ -1,6 +1,6 @@ -local config = require("oil.config") -local constants = require("oil.constants") -local util = require("oil.util") +local config = require('oil.config') +local constants = require('oil.constants') +local util = require('oil.util') local M = {} local FIELD_NAME = constants.FIELD_NAME @@ -38,7 +38,7 @@ end ---@return oil.ColumnSpec[] M.get_supported_columns = function(adapter_or_scheme) 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) else adapter = adapter_or_scheme @@ -53,7 +53,7 @@ M.get_supported_columns = function(adapter_or_scheme) return ret end -local EMPTY = { "-", "OilEmpty" } +local EMPTY = { '-', 'OilEmpty' } M.EMPTY = EMPTY @@ -71,17 +71,17 @@ M.render_col = function(adapter, col_def, entry, bufnr) end local chunk = column.render(entry, conf, bufnr) - if type(chunk) == "table" then - if chunk[1]:match("^%s*$") then + if type(chunk) == 'table' then + if chunk[1]:match('^%s*$') then return EMPTY end else - if not chunk or chunk:match("^%s*$") then + if not chunk or chunk:match('^%s*$') then return EMPTY end if conf and conf.highlight then local highlight = conf.highlight - if type(highlight) == "function" then + if type(highlight) == 'function' then highlight = conf.highlight(chunk) end return { chunk, highlight } @@ -98,13 +98,13 @@ end M.parse_col = function(adapter, line, col_def) local name, conf = util.split_config(col_def) -- 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 return nil, rem end local column = M.get_column(adapter, name) if column then - return column.parse(line:gsub("^%s+", ""), conf) + return column.parse(line:gsub('^%s+', ''), conf) end end @@ -128,12 +128,12 @@ end M.render_change_action = function(adapter, action) local column = M.get_column(adapter, action.column) 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 if column.render_action then return column.render_action(action) 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 @@ -144,7 +144,7 @@ M.perform_change_action = function(adapter, action, callback) local column = M.get_column(adapter, action.column) if not column then 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 column.perform_action(action, callback) @@ -152,12 +152,12 @@ end local icon_provider = util.get_icon_provider() if icon_provider then - M.register("icon", { + M.register('icon', { render = function(entry, conf, bufnr) local field_type = entry[FIELD_TYPE] local name = entry[FIELD_NAME] local meta = entry[FIELD_META] - if field_type == "link" and meta then + if field_type == 'link' and meta then if meta.link then name = meta.link end @@ -170,11 +170,11 @@ if icon_provider then end 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 _, path = util.parse_url(bufname) 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 ft = vim.filetype.match({ filename = name, contents = lines }) end @@ -183,10 +183,10 @@ if icon_provider then local icon, hl = icon_provider(field_type, name, conf, ft) if not conf or conf.add_padding ~= false then - icon = icon .. " " + icon = icon .. ' ' end if conf and conf.highlight then - if type(conf.highlight) == "function" then + if type(conf.highlight) == 'function' then hl = conf.highlight(icon) else hl = conf.highlight @@ -196,29 +196,29 @@ if icon_provider then end, parse = function(line, conf) - return line:match("^(%S+)%s+(.*)$") + return line:match('^(%S+)%s+(.*)$') end, }) end local default_type_icons = { - directory = "dir", - socket = "sock", + directory = 'dir', + socket = 'sock', } ---@param entry oil.InternalEntry ---@return boolean local function is_entry_directory(entry) local type = entry[FIELD_TYPE] - if type == "directory" then + if type == 'directory' then return true - elseif type == "link" then + elseif type == 'link' then 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 return false end end -M.register("type", { +M.register('type', { render = function(entry, conf) local entry_type = entry[FIELD_TYPE] if conf and conf.icons then @@ -229,7 +229,7 @@ M.register("type", { end, parse = function(line, conf) - return line:match("^(%S+)%s+(.*)$") + return line:match('^(%S+)%s+(.*)$') end, get_sort_value = function(entry) @@ -242,22 +242,22 @@ M.register("type", { }) local function adjust_number(int) - return string.format("%03d%s", #int, int) + return string.format('%03d%s', #int, int) end -M.register("name", { +M.register('name', { 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, 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, create_sort_value_factory = function(num_entries) if 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 if config.view_options.case_insensitive then return function(entry) @@ -272,7 +272,7 @@ M.register("name", { local memo = {} return function(entry) 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 name = name:lower() end diff --git a/lua/oil/config.lua b/lua/oil/config.lua index 3734bc0..7383920 100644 --- a/lua/oil/config.lua +++ b/lua/oil/config.lua @@ -5,7 +5,7 @@ local default_config = { -- Id is automatically added at the beginning, and name at the end -- See :help oil-columns columns = { - "icon", + 'icon', -- "permissions", -- "size", -- "mtime", @@ -13,18 +13,18 @@ local default_config = { -- Buffer-local options to use for oil buffers buf_options = { buflisted = false, - bufhidden = "hide", + bufhidden = 'hide', }, -- Window-local options to use for oil buffers win_options = { wrap = false, - signcolumn = "no", + signcolumn = 'no', cursorcolumn = false, - foldcolumn = "0", + foldcolumn = '0', spell = false, list = false, conceallevel = 3, - concealcursor = "nvic", + concealcursor = 'nvic', }, -- Send deleted files to the trash instead of permanently deleting them (:help oil-trash) delete_to_trash = false, @@ -48,7 +48,7 @@ local default_config = { }, -- 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 - constrain_cursor = "editable", + constrain_cursor = 'editable', -- Set to true to watch the filesystem for changes and reload oil watch_for_changes = false, -- 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 -- See :help oil-actions for a list of all available actions keymaps = { - ["g?"] = { "actions.show_help", mode = "n" }, - [""] = "actions.select", - [""] = { "actions.select", opts = { vertical = true } }, - [""] = { "actions.select", opts = { horizontal = true } }, - [""] = { "actions.select", opts = { tab = true } }, - [""] = "actions.preview", - [""] = { "actions.close", mode = "n" }, - [""] = "actions.refresh", - ["-"] = { "actions.parent", mode = "n" }, - ["_"] = { "actions.open_cwd", mode = "n" }, - ["`"] = { "actions.cd", mode = "n" }, - ["g~"] = { "actions.cd", opts = { scope = "tab" }, mode = "n" }, - ["gs"] = { "actions.change_sort", mode = "n" }, - ["gx"] = "actions.open_external", - ["g."] = { "actions.toggle_hidden", mode = "n" }, - ["g\\"] = { "actions.toggle_trash", mode = "n" }, + ['g?'] = { 'actions.show_help', mode = 'n' }, + [''] = 'actions.select', + [''] = { 'actions.select', opts = { vertical = true } }, + [''] = { 'actions.select', opts = { horizontal = true } }, + [''] = { 'actions.select', opts = { tab = true } }, + [''] = 'actions.preview', + [''] = { 'actions.close', mode = 'n' }, + [''] = 'actions.refresh', + ['-'] = { 'actions.parent', mode = 'n' }, + ['_'] = { 'actions.open_cwd', mode = 'n' }, + ['`'] = { 'actions.cd', mode = 'n' }, + ['g~'] = { 'actions.cd', opts = { scope = 'tab' }, mode = 'n' }, + ['gs'] = { 'actions.change_sort', mode = 'n' }, + ['gx'] = 'actions.open_external', + ['g.'] = { 'actions.toggle_hidden', mode = 'n' }, + ['g\\'] = { 'actions.toggle_trash', mode = 'n' }, }, -- Set to false to disable all of the above keymaps use_default_keymaps = true, @@ -82,7 +82,7 @@ local default_config = { show_hidden = false, -- This function defines what is considered a "hidden" file is_hidden_file = function(name, bufnr) - local m = name:match("^%.") + local m = name:match('^%.') return m ~= nil end, -- This function defines what will never be shown, even when `show_hidden` is set @@ -91,14 +91,14 @@ local default_config = { end, -- Sort file names with numbers in a more intuitive order for humans. -- Can be "fast", true, or false. "fast" will turn it off for large directories. - natural_order = "fast", + natural_order = 'fast', -- Sort file and directory names case insensitive case_insensitive = false, sort = { -- sort order can be "asc" or "desc" -- see :help oil-columns to see which columns are sortable - { "type", "asc" }, - { "name", "asc" }, + { 'type', 'asc' }, + { 'name', 'asc' }, }, -- Customize the highlight group for the file name 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 get_win_title = nil, -- 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. -- Change values here to customize the layout override = function(conf) @@ -150,7 +150,7 @@ local default_config = { -- Whether the preview window is automatically updated when the cursor is moved update_on_cursor_moved = true, -- How to open the preview window "load"|"scratch"|"fast_scratch" - preview_method = "fast_scratch", + preview_method = 'fast_scratch', -- A function that returns true to disable preview on a file e.g. to avoid lag disable_preview = function(filename) return false @@ -190,7 +190,7 @@ local default_config = { min_height = { 5, 0.1 }, height = nil, border = nil, - minimized_border = "none", + minimized_border = 'none', win_options = { 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 -- 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 = { - ["oil://"] = "files", - ["oil-ssh://"] = "ssh", - [oil_s3_string] = "s3", - ["oil-trash://"] = "trash", + ['oil://'] = 'files', + ['oil-ssh://'] = 'ssh', + [oil_s3_string] = 's3', + ['oil-trash://'] = 'trash', } default_config.adapter_aliases = {} -- 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) 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 new_conf.keymaps = opts.keymaps or {} elseif opts.keymaps then @@ -429,19 +429,19 @@ M.setup = function(opts) end -- Backwards compatibility for old versions that don't support winborder - if vim.fn.has("nvim-0.11") == 0 then - new_conf = vim.tbl_deep_extend("keep", new_conf, { - float = { border = "rounded" }, - confirmation = { border = "rounded" }, - progress = { border = "rounded" }, - ssh = { border = "rounded" }, - keymaps_help = { border = "rounded" }, + if vim.fn.has('nvim-0.11') == 0 then + new_conf = vim.tbl_deep_extend('keep', new_conf, { + float = { border = 'rounded' }, + confirmation = { border = 'rounded' }, + progress = { border = 'rounded' }, + ssh = { border = 'rounded' }, + keymaps_help = { border = 'rounded' }, }) end -- Backwards compatibility. We renamed the 'preview' window config to be called 'confirmation'. 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 -- Backwards compatibility. We renamed the 'preview' config to 'preview_win' 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_rename_autosave = nil 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 ) end @@ -479,10 +479,10 @@ M.get_adapter_by_scheme = function(scheme) if not scheme then return nil end - if not vim.endswith(scheme, "://") then - local pieces = vim.split(scheme, "://", { plain = true }) + if not vim.endswith(scheme, '://') then + local pieces = vim.split(scheme, '://', { plain = true }) if #pieces <= 2 then - scheme = pieces[1] .. "://" + scheme = pieces[1] .. '://' else error(string.format("Malformed url: '%s'", scheme)) end @@ -494,7 +494,7 @@ M.get_adapter_by_scheme = function(scheme) return nil end 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 adapter.name = name M._adapter_by_scheme[scheme] = adapter diff --git a/lua/oil/fs.lua b/lua/oil/fs.lua index 80fb6d4..6cb084c 100644 --- a/lua/oil/fs.lua +++ b/lua/oil/fs.lua @@ -1,17 +1,17 @@ -local log = require("oil.log") +local log = require('oil.log') local M = {} local uv = vim.uv or vim.loop ---@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 ---@type string -M.sep = M.is_windows and "\\" or "/" +M.sep = M.is_windows and '\\' or '/' ---@param ... string M.join = function(...) @@ -23,15 +23,15 @@ end ---@return boolean M.is_absolute = function(dir) if M.is_windows then - return dir:match("^%a:\\") + return dir:match('^%a:\\') else - return vim.startswith(dir, "/") + return vim.startswith(dir, '/') end end M.abspath = function(path) if not M.is_absolute(path) then - path = vim.fn.fnamemodify(path, ":p") + path = vim.fn.fnamemodify(path, ':p') end return path end @@ -40,11 +40,11 @@ end ---@param mode? integer File mode in decimal (default 420 = 0644) ---@param cb fun(err: nil|string) M.touch = function(path, mode, cb) - if type(mode) == "function" then + if type(mode) == 'function' then cb = mode mode = 420 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 cb(err) else @@ -59,12 +59,12 @@ end ---@param candidate string ---@return boolean M.is_subpath = function(root, candidate) - if candidate == "" then + if candidate == '' then return false end root = vim.fs.normalize(M.abspath(root)) -- Trim trailing "/" from the root - if root:find("/", -1) then + if root:find('/', -1) then root = root:sub(1, -2) end candidate = vim.fs.normalize(M.abspath(candidate)) @@ -80,8 +80,8 @@ M.is_subpath = function(root, candidate) return false end - 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 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() return candidate_starts_with_sep or root_ends_with_sep end @@ -90,15 +90,15 @@ end ---@return string M.posix_to_os_path = function(path) if M.is_windows then - if vim.startswith(path, "/") then - local drive = path:match("^/(%a+)") + if vim.startswith(path, '/') then + local drive = path:match('^/(%a+)') if not drive then return path end local rem = path:sub(drive:len() + 2) - return string.format("%s:%s", drive, rem:gsub("/", "\\")) + return string.format('%s:%s', drive, rem:gsub('/', '\\')) else - local newpath = path:gsub("/", "\\") + local newpath = path:gsub('/', '\\') return newpath end else @@ -111,10 +111,10 @@ end M.os_to_posix_path = function(path) if M.is_windows then if M.is_absolute(path) then - local drive, rem = path:match("^([^:]+):\\(.*)$") - return string.format("/%s/%s", drive:upper(), rem:gsub("\\", "/")) + local drive, rem = path:match('^([^:]+):\\(.*)$') + return string.format('/%s/%s', drive:upper(), rem:gsub('\\', '/')) else - local newpath = path:gsub("\\", "/") + local newpath = path:gsub('\\', '/') return newpath end else @@ -135,16 +135,16 @@ M.shorten_path = function(path, relative_to) if M.is_subpath(relative_to, path) then local idx = relative_to:len() + 1 -- 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 end relpath = path:sub(idx) - if relpath == "" then - relpath = "." + if relpath == '' then + relpath = '.' end end 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 return homepath end @@ -156,13 +156,13 @@ end ---@param mode? integer M.mkdirp = function(dir, mode) mode = mode or 493 - local mod = "" + local mod = '' local path = dir while vim.fn.isdirectory(path) == 0 do - mod = mod .. ":h" + mod = mod .. ':h' path = vim.fn.fnamemodify(dir, mod) end - while mod ~= "" do + while mod ~= '' do mod = mod:sub(3) path = vim.fn.fnamemodify(dir, mod) uv.fs_mkdir(path, mode) @@ -209,7 +209,7 @@ end ---@param path string ---@param cb fun(err: nil|string) M.recursive_delete = function(entry_type, path, cb) - if entry_type ~= "directory" then + if entry_type ~= 'directory' then return uv.fs_unlink(path, cb) end ---@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 uv.fs_copyfile(src_path, dest_path, function(err) if err then - log.warn("Error copying undofile %s: %s", undofile, err) + log.warn('Error copying undofile %s: %s', undofile, err) end end) else uv.fs_rename(undofile, dest_undofile, function(err) if err then - log.warn("Error moving undofile %s: %s", undofile, err) + log.warn('Error moving undofile %s: %s', undofile, err) end end) end @@ -290,7 +290,7 @@ end) ---@param dest_path string ---@param cb fun(err: nil|string) 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) if link_err then return cb(link_err) @@ -300,7 +300,7 @@ M.recursive_copy = function(entry_type, src_path, dest_path, cb) end) return end - if entry_type ~= "directory" then + if entry_type ~= 'directory' then uv.fs_copyfile(src_path, dest_path, { excl = true }, cb) move_undofile(src_path, dest_path, true) return @@ -374,7 +374,7 @@ M.recursive_move = function(entry_type, src_path, dest_path, cb) end end) else - if entry_type ~= "directory" then + if entry_type ~= 'directory' then move_undofile(src_path, dest_path, false) end cb() diff --git a/lua/oil/git.lua b/lua/oil/git.lua index ec3b84b..9fab6e4 100644 --- a/lua/oil/git.lua +++ b/lua/oil/git.lua @@ -1,12 +1,12 @@ -- integration with git operations -local fs = require("oil.fs") +local fs = require('oil.fs') local M = {} ---@param path string ---@return string|nil 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 return vim.fs.dirname(git_dir) else @@ -22,16 +22,16 @@ M.add = function(path, cb) return cb() end - local stderr = "" - local jid = vim.fn.jobstart({ "git", "add", path }, { + local stderr = '' + local jid = vim.fn.jobstart({ 'git', 'add', path }, { cwd = root, stderr_buffered = true, on_stderr = function(_, data) - stderr = table.concat(data, "\n") + stderr = table.concat(data, '\n') end, on_exit = function(_, code) if code ~= 0 then - cb("Error in git add: " .. stderr) + cb('Error in git add: ' .. stderr) else cb() end @@ -50,12 +50,12 @@ M.rm = function(path, cb) return cb() end - local stderr = "" - local jid = vim.fn.jobstart({ "git", "rm", "-r", path }, { + local stderr = '' + local jid = vim.fn.jobstart({ 'git', 'rm', '-r', path }, { cwd = root, stderr_buffered = true, on_stderr = function(_, data) - stderr = table.concat(data, "\n") + stderr = table.concat(data, '\n') end, on_exit = function(_, code) if code ~= 0 then @@ -63,7 +63,7 @@ M.rm = function(path, cb) if stderr:match("^fatal: pathspec '.*' did not match any files$") then cb() else - cb("Error in git rm: " .. stderr) + cb('Error in git rm: ' .. stderr) end else cb() @@ -86,23 +86,23 @@ M.mv = function(entry_type, src_path, dest_path, cb) return end - local stderr = "" - local jid = vim.fn.jobstart({ "git", "mv", src_path, dest_path }, { + local stderr = '' + local jid = vim.fn.jobstart({ 'git', 'mv', src_path, dest_path }, { cwd = src_git, stderr_buffered = true, on_stderr = function(_, data) - stderr = table.concat(data, "\n") + stderr = table.concat(data, '\n') end, on_exit = function(_, code) if code ~= 0 then stderr = vim.trim(stderr) if - stderr:match("^fatal: not under version control") - or stderr:match("^fatal: source directory is empty") + stderr:match('^fatal: not under version control') + or stderr:match('^fatal: source directory is empty') then fs.recursive_move(entry_type, src_path, dest_path, cb) else - cb("Error in git mv: " .. stderr) + cb('Error in git mv: ' .. stderr) end else cb() diff --git a/lua/oil/init.lua b/lua/oil/init.lua index 0329afd..d3e6223 100644 --- a/lua/oil/init.lua +++ b/lua/oil/init.lua @@ -35,10 +35,10 @@ local M = {} ---@param lnum integer ---@return nil|oil.Entry M.get_entry_on_line = function(bufnr, lnum) - local columns = require("oil.columns") - local parser = require("oil.mutator.parser") - local util = require("oil.util") - if vim.bo[bufnr].filetype ~= "oil" then + local columns = require('oil.columns') + local parser = require('oil.mutator.parser') + local util = require('oil.util') + if vim.bo[bufnr].filetype ~= 'oil' then return nil end local adapter = util.get_adapter(bufnr) @@ -69,13 +69,13 @@ M.get_entry_on_line = function(bufnr, lnum) -- This is a NEW entry that hasn't been saved yet local name = vim.trim(line) local entry_type - if vim.endswith(name, "/") then + if vim.endswith(name, '/') then name = name:sub(1, name:len() - 1) - entry_type = "directory" + entry_type = 'directory' else - entry_type = "file" + entry_type = 'file' end - if name == "" then + if name == '' then return nil else return { @@ -95,14 +95,14 @@ end ---Discard all changes made to oil buffers M.discard_all_changes = function() - local view = require("oil.view") + local view = require('oil.view') for _, bufnr in ipairs(view.get_all_buffers()) do if vim.bo[bufnr].modified then view.render_buffer_async(bufnr, {}, function(err) if err then vim.notify( string.format( - "Error rendering oil buffer %s: %s", + 'Error rendering oil buffer %s: %s', vim.api.nvim_buf_get_name(bufnr), err ), @@ -117,7 +117,7 @@ end ---Change the display columns for oil ---@param cols oil.ColumnSpec[] M.set_columns = function(cols) - require("oil.view").set_columns(cols) + require('oil.view').set_columns(cols) end ---Change the sort order for oil @@ -125,30 +125,30 @@ end ---@example --- require("oil").set_sort({ { "type", "asc" }, { "size", "desc" } }) M.set_sort = function(sort) - require("oil.view").set_sort(sort) + require('oil.view').set_sort(sort) end ---Change how oil determines if the file is hidden ---@param is_hidden_file fun(filename: string, bufnr: integer, entry: oil.Entry): boolean Return true if the file/dir should be hidden M.set_is_hidden_file = function(is_hidden_file) - require("oil.view").set_is_hidden_file(is_hidden_file) + require('oil.view').set_is_hidden_file(is_hidden_file) end ---Toggle hidden files and directories M.toggle_hidden = function() - require("oil.view").toggle_hidden() + require('oil.view').toggle_hidden() end ---Get the current directory ---@param bufnr? integer ---@return nil|string M.get_current_dir = function(bufnr) - local config = require("oil.config") - local fs = require("oil.fs") - local util = require("oil.util") + local config = require('oil.config') + local fs = require('oil.fs') + local util = require('oil.util') local buf_name = vim.api.nvim_buf_get_name(bufnr or 0) local scheme, path = util.parse_url(buf_name) - if config.adapters[scheme] == "files" then + if config.adapters[scheme] == 'files' then assert(path) return fs.posix_to_os_path(path) end @@ -164,10 +164,10 @@ M.get_url_for_path = function(dir, use_oil_parent) if use_oil_parent == nil then use_oil_parent = true end - local config = require("oil.config") - local fs = require("oil.fs") - local util = require("oil.util") - if vim.bo.filetype == "netrw" and not dir then + local config = require('oil.config') + local fs = require('oil.fs') + local util = require('oil.util') + if vim.bo.filetype == 'netrw' and not dir then dir = vim.b.netrw_curdir end if dir then @@ -175,7 +175,7 @@ M.get_url_for_path = function(dir, use_oil_parent) if scheme then return dir end - local abspath = vim.fn.fnamemodify(dir, ":p") + local abspath = vim.fn.fnamemodify(dir, ':p') local path = fs.os_to_posix_path(abspath) return config.adapter_to_scheme.files .. path else @@ -190,27 +190,27 @@ end ---@return string ---@return nil|string M.get_buffer_parent_url = function(bufname, use_oil_parent) - local config = require("oil.config") - local fs = require("oil.fs") - local pathutil = require("oil.pathutil") - local util = require("oil.util") + local config = require('oil.config') + local fs = require('oil.fs') + local pathutil = require('oil.pathutil') + local util = require('oil.util') local scheme, path = util.parse_url(bufname) if not scheme then local parent, basename scheme = config.adapter_to_scheme.files - if bufname == "" then + if bufname == '' then parent = fs.os_to_posix_path(vim.fn.getcwd()) else - parent = fs.os_to_posix_path(vim.fn.fnamemodify(bufname, ":p:h")) - basename = vim.fn.fnamemodify(bufname, ":t") + parent = fs.os_to_posix_path(vim.fn.fnamemodify(bufname, ':p:h')) + basename = vim.fn.fnamemodify(bufname, ':t') end local parent_url = util.addslash(scheme .. parent) return parent_url, basename else assert(path) - if scheme == "term://" then + if scheme == 'term://' then ---@type string - path = vim.fn.expand(path:match("^(.*)//")) ---@diagnostic disable-line: assign-type-mismatch + path = vim.fn.expand(path:match('^(.*)//')) ---@diagnostic disable-line: assign-type-mismatch return config.adapter_to_scheme.files .. util.addslash(path) end @@ -248,10 +248,10 @@ end ---@param cb? fun() Called after the oil buffer is ready M.open_float = function(dir, opts, cb) opts = opts or {} - local config = require("oil.config") - local layout = require("oil.layout") - local util = require("oil.util") - local view = require("oil.view") + local config = require('oil.config') + local layout = require('oil.layout') + local util = require('oil.util') + local view = require('oil.view') local parent_url, basename = M.get_url_for_path(dir) if basename then @@ -259,7 +259,7 @@ M.open_float = function(dir, opts, cb) end local bufnr = vim.api.nvim_create_buf(false, true) - vim.bo[bufnr].bufhidden = "wipe" + vim.bo[bufnr].bufhidden = 'wipe' local win_opts = layout.get_fullscreen_win_opts() local original_winid = vim.api.nvim_get_current_win() @@ -267,16 +267,16 @@ M.open_float = function(dir, opts, cb) vim.w[winid].is_oil_win = true vim.w[winid].oil_original_win = original_winid for k, v in pairs(config.float.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 local autocmds = {} table.insert( autocmds, - vim.api.nvim_create_autocmd("WinLeave", { - desc = "Close floating oil window", - group = "Oil", + vim.api.nvim_create_autocmd('WinLeave', { + desc = 'Close floating oil window', + group = 'Oil', callback = vim.schedule_wrap(function() - if util.is_floating_win() or vim.fn.win_gettype() == "command" then + if util.is_floating_win() or vim.fn.win_gettype() == 'command' then return end if vim.api.nvim_win_is_valid(winid) then @@ -293,23 +293,23 @@ M.open_float = function(dir, opts, cb) table.insert( autocmds, - vim.api.nvim_create_autocmd("BufWinEnter", { - desc = "Reset local oil window options when buffer changes", - pattern = "*", + vim.api.nvim_create_autocmd('BufWinEnter', { + desc = 'Reset local oil window options when buffer changes', + pattern = '*', callback = function(params) local winbuf = params.buf if not vim.api.nvim_win_is_valid(winid) or vim.api.nvim_win_get_buf(winid) ~= winbuf then return end for k, v in pairs(config.float.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 -- Update the floating window title - if vim.fn.has("nvim-0.9") == 1 and config.float.border ~= "none" then + if vim.fn.has('nvim-0.9') == 1 and config.float.border ~= 'none' then local cur_win_opts = vim.api.nvim_win_get_config(winid) vim.api.nvim_win_set_config(winid, { - relative = "editor", + relative = 'editor', row = cur_win_opts.row, col = cur_win_opts.col, width = cur_win_opts.width, @@ -324,7 +324,7 @@ M.open_float = function(dir, opts, cb) vim.cmd.edit({ args = { util.escape_filename(parent_url) }, mods = { keepalt = true } }) -- :edit will set buflisted = true, but we may not want that if config.buf_options.buflisted ~= nil then - vim.api.nvim_set_option_value("buflisted", config.buf_options.buflisted, { buf = 0 }) + vim.api.nvim_set_option_value('buflisted', config.buf_options.buflisted, { buf = 0 }) end util.run_after_load(0, function() @@ -335,7 +335,7 @@ M.open_float = function(dir, opts, cb) end end) - if vim.fn.has("nvim-0.9") == 0 then + if vim.fn.has('nvim-0.9') == 0 then util.add_title_to_win(winid) end end @@ -358,7 +358,7 @@ end ---@param oil_bufnr? integer local function update_preview_window(oil_bufnr) oil_bufnr = oil_bufnr or 0 - local util = require("oil.util") + local util = require('oil.util') util.run_after_load(oil_bufnr, function() local cursor_entry = M.get_cursor_entry() local preview_win_id = util.get_preview_win() @@ -378,9 +378,9 @@ end ---@param cb? fun() Called after the oil buffer is ready M.open = function(dir, opts, cb) opts = opts or {} - local config = require("oil.config") - local util = require("oil.util") - local view = require("oil.view") + local config = require('oil.config') + local util = require('oil.util') + local view = require('oil.view') local parent_url, basename = M.get_url_for_path(dir) if basename then view.set_last_cursor(parent_url, basename) @@ -388,7 +388,7 @@ M.open = function(dir, opts, cb) vim.cmd.edit({ args = { util.escape_filename(parent_url) }, mods = { keepalt = true } }) -- :edit will set buflisted = true, but we may not want that if config.buf_options.buflisted ~= nil then - vim.api.nvim_set_option_value("buflisted", config.buf_options.buflisted, { buf = 0 }) + vim.api.nvim_set_option_value('buflisted', config.buf_options.buflisted, { buf = 0 }) end util.run_after_load(0, function() @@ -411,8 +411,8 @@ end M.close = function(opts) opts = opts or {} local mode = vim.api.nvim_get_mode().mode - if mode:match("^[vVsS\22\19]") or mode:match("^no") then - vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, true, true), "n", false) + if mode:match('^[vVsS\22\19]') or mode:match('^no') then + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('', true, true, true), 'n', false) return end -- If we're in a floating oil window, close it and try to restore focus to the original window @@ -424,7 +424,7 @@ M.close = function(opts) end return end - local ok, bufnr = pcall(vim.api.nvim_win_get_var, 0, "oil_original_buffer") + local ok, bufnr = pcall(vim.api.nvim_win_get_var, 0, 'oil_original_buffer') if ok and vim.api.nvim_buf_is_valid(bufnr) then vim.api.nvim_win_set_buf(0, bufnr) if vim.w.oil_original_view then @@ -459,9 +459,9 @@ end ---@param callback? fun(err: nil|string) Called once the preview window has been opened M.open_preview = function(opts, callback) opts = opts or {} - local config = require("oil.config") - local layout = require("oil.layout") - local util = require("oil.util") + local config = require('oil.config') + local layout = require('oil.layout') + local util = require('oil.util') local function finish(err) if err then @@ -477,9 +477,9 @@ M.open_preview = function(opts, callback) end if not opts.split then if opts.horizontal then - opts.split = vim.o.splitbelow and "belowright" or "aboveleft" + opts.split = vim.o.splitbelow and 'belowright' or 'aboveleft' else - opts.split = vim.o.splitright and "belowright" or "aboveleft" + opts.split = vim.o.splitright and 'belowright' or 'aboveleft' end end @@ -489,11 +489,11 @@ M.open_preview = function(opts, callback) local entry = M.get_cursor_entry() if not entry then - return finish("Could not find entry under cursor") + return finish('Could not find entry under cursor') end local entry_title = entry.name - if entry.type == "directory" then - entry_title = entry_title .. "/" + if entry.type == 'directory' then + entry_title = entry_title .. '/' end if util.is_floating_win() then @@ -502,7 +502,7 @@ M.open_preview = function(opts, callback) layout.split_window(0, config.float.preview_split, config.float.padding) local win_opts_oil = { - relative = "editor", + relative = 'editor', width = root_win_opts.width, height = root_win_opts.height, row = root_win_opts.row, @@ -512,7 +512,7 @@ M.open_preview = function(opts, callback) } vim.api.nvim_win_set_config(0, win_opts_oil) local win_opts = { - relative = "editor", + relative = 'editor', width = preview_win_opts.width, height = preview_win_opts.height, row = preview_win_opts.row, @@ -521,23 +521,23 @@ M.open_preview = function(opts, callback) zindex = 45, focusable = false, noautocmd = true, - style = "minimal", + style = 'minimal', } - if vim.fn.has("nvim-0.9") == 1 then + if vim.fn.has('nvim-0.9') == 1 then win_opts.title = entry_title end preview_win = vim.api.nvim_open_win(bufnr, true, win_opts) - vim.api.nvim_set_option_value("previewwindow", true, { scope = "local", win = preview_win }) - vim.api.nvim_win_set_var(preview_win, "oil_preview", true) + vim.api.nvim_set_option_value('previewwindow', true, { scope = 'local', win = preview_win }) + vim.api.nvim_win_set_var(preview_win, 'oil_preview', true) vim.api.nvim_set_current_win(prev_win) - elseif vim.fn.has("nvim-0.9") == 1 then + elseif vim.fn.has('nvim-0.9') == 1 then vim.api.nvim_win_set_config(preview_win, { title = entry_title }) end end - local cmd = preview_win and "buffer" or "sbuffer" + local cmd = preview_win and 'buffer' or 'sbuffer' local mods = { vertical = opts.vertical, horizontal = opts.horizontal, @@ -549,11 +549,11 @@ M.open_preview = function(opts, callback) -- not get set properly. So we have to switch windows this way instead. local hack_set_win = function(winid) local winnr = vim.api.nvim_win_get_number(winid) - vim.cmd.wincmd({ args = { "w" }, count = winnr }) + vim.cmd.wincmd({ args = { 'w' }, count = winnr }) end util.get_edit_path(bufnr, entry, function(normalized_url) - local mc = package.loaded["multicursor-nvim"] + local mc = package.loaded['multicursor-nvim'] local has_multicursors = mc and mc.hasCursors() local is_visual_mode = util.is_visual_mode() if preview_win then @@ -564,16 +564,16 @@ M.open_preview = function(opts, callback) end end - local entry_is_file = not vim.endswith(normalized_url, "/") + local entry_is_file = not vim.endswith(normalized_url, '/') local filebufnr if entry_is_file then if config.preview_win.disable_preview(normalized_url) then filebufnr = vim.api.nvim_create_buf(false, true) - vim.bo[filebufnr].bufhidden = "wipe" - vim.bo[filebufnr].buftype = "nofile" - util.render_text(filebufnr, "Preview disabled", { winid = preview_win }) + vim.bo[filebufnr].bufhidden = 'wipe' + vim.bo[filebufnr].buftype = 'nofile' + util.render_text(filebufnr, 'Preview disabled', { winid = preview_win }) elseif - config.preview_win.preview_method ~= "load" + config.preview_win.preview_method ~= 'load' and not util.file_matches_bufreadcmd(normalized_url) then filebufnr = @@ -584,7 +584,7 @@ M.open_preview = function(opts, callback) if not filebufnr then filebufnr = vim.fn.bufadd(normalized_url) if entry_is_file and vim.fn.bufloaded(filebufnr) == 0 then - vim.bo[filebufnr].bufhidden = "wipe" + vim.bo[filebufnr].bufhidden = 'wipe' vim.b[filebufnr].oil_preview_buffer = true end end @@ -596,8 +596,8 @@ M.open_preview = function(opts, callback) mods = mods, }) -- Ignore swapfile errors - if not ok and err and not err:match("^Vim:E325:") then - vim.api.nvim_echo({ { err, "Error" } }, true, {}) + if not ok and err and not err:match('^Vim:E325:') then + vim.api.nvim_echo({ { err, 'Error' } }, true, {}) end -- If we called open_preview during an autocmd, then the edit command may not trigger the @@ -606,10 +606,10 @@ M.open_preview = function(opts, callback) M.load_oil_buffer(filebufnr) end - vim.api.nvim_set_option_value("previewwindow", true, { scope = "local", win = 0 }) - vim.api.nvim_win_set_var(0, "oil_preview", true) + vim.api.nvim_set_option_value('previewwindow', true, { scope = 'local', win = 0 }) + vim.api.nvim_win_set_var(0, 'oil_preview', true) for k, v in pairs(config.preview_win.win_options) do - vim.api.nvim_set_option_value(k, v, { scope = "local", win = preview_win }) + vim.api.nvim_set_option_value(k, v, { scope = 'local', win = preview_win }) end vim.w.oil_entry_id = entry.id vim.w.oil_source_win = prev_win @@ -619,7 +619,7 @@ M.open_preview = function(opts, callback) elseif is_visual_mode then hack_set_win(prev_win) -- Restore the visual selection - vim.cmd.normal({ args = { "gv" }, bang = true }) + vim.cmd.normal({ args = { 'gv' }, bang = true }) else vim.api.nvim_set_current_win(prev_win) end @@ -639,12 +639,12 @@ end ---@param opts nil|oil.SelectOpts ---@param callback nil|fun(err: nil|string) Called once all entries have been opened M.select = function(opts, callback) - local cache = require("oil.cache") - local config = require("oil.config") - local constants = require("oil.constants") - local util = require("oil.util") + local cache = require('oil.cache') + local config = require('oil.config') + local constants = require('oil.constants') + local util = require('oil.util') local FIELD_META = constants.FIELD_META - opts = vim.tbl_extend("keep", opts or {}, {}) + opts = vim.tbl_extend('keep', opts or {}, {}) local function finish(err) if err then @@ -656,17 +656,17 @@ M.select = function(opts, callback) end if not opts.split and (opts.horizontal or opts.vertical) then if opts.horizontal then - opts.split = vim.o.splitbelow and "belowright" or "aboveleft" + opts.split = vim.o.splitbelow and 'belowright' or 'aboveleft' else - opts.split = vim.o.splitright and "belowright" or "aboveleft" + opts.split = vim.o.splitright and 'belowright' or 'aboveleft' end end if opts.tab and opts.split then - return finish("Cannot use split=true when tab = true") + return finish('Cannot use split=true when tab = true') end local adapter = util.get_adapter(0) if not adapter then - return finish("Not an oil buffer") + return finish('Not an oil buffer') end local visual_range = util.get_visual_range() @@ -687,7 +687,7 @@ M.select = function(opts, callback) end end if vim.tbl_isempty(entries) then - return finish("Could not find entry under cursor") + return finish('Could not find entry under cursor') end -- Check if any of these entries are moved from their original location @@ -713,7 +713,7 @@ M.select = function(opts, callback) end end if any_moved and config.prompt_save_on_select_new_entry then - local ok, choice = pcall(vim.fn.confirm, "Save changes?", "Yes\nNo", 1) + local ok, choice = pcall(vim.fn.confirm, 'Save changes?', 'Yes\nNo', 1) if not ok then return finish() elseif choice == 1 then @@ -738,7 +738,7 @@ M.select = function(opts, callback) -- entry. This prevents the case of MOVE /foo -> /bar + CREATE /foo. -- If you enter the new /foo, it will show the contents of the old /foo. if not entry.id and cache.list_url(bufname)[entry.name] then - return cb("Please save changes before entering new directory") + return cb('Please save changes before entering new directory') end else -- Close floating window before opening a file @@ -757,7 +757,7 @@ M.select = function(opts, callback) keepalt = false, } local filebufnr = vim.fn.bufadd(normalized_url) - local entry_is_file = not vim.endswith(normalized_url, "/") + local entry_is_file = not vim.endswith(normalized_url, '/') -- The :buffer command doesn't set buflisted=true -- So do that for normal files or for oil dirs if config set buflisted=true @@ -765,13 +765,13 @@ M.select = function(opts, callback) vim.bo[filebufnr].buflisted = true end - local cmd = "buffer" + local cmd = 'buffer' if opts.tab then vim.cmd.tabnew({ mods = mods }) -- Make sure the new buffer from tabnew gets cleaned up - vim.bo.bufhidden = "wipe" + vim.bo.bufhidden = 'wipe' elseif opts.split then - cmd = "sbuffer" + cmd = 'sbuffer' end if opts.handle_buffer_callback ~= nil then opts.handle_buffer_callback(filebufnr) @@ -783,8 +783,8 @@ M.select = function(opts, callback) mods = mods, }) -- Ignore swapfile errors - if not ok and err and not err:match("^Vim:E325:") then - vim.api.nvim_echo({ { err, "Error" } }, true, {}) + if not ok and err and not err:match('^Vim:E325:') then + vim.api.nvim_echo({ { err, 'Error' } }, true, {}) end end @@ -815,21 +815,21 @@ end ---@param bufnr integer ---@return boolean local function maybe_hijack_directory_buffer(bufnr) - local config = require("oil.config") - local fs = require("oil.fs") - local util = require("oil.util") + local config = require('oil.config') + local fs = require('oil.fs') + local util = require('oil.util') if not config.default_file_explorer then return false end local bufname = vim.api.nvim_buf_get_name(bufnr) - if bufname == "" then + if bufname == '' then return false end if util.parse_url(bufname) or vim.fn.isdirectory(bufname) == 0 then return false end local new_name = util.addslash( - config.adapter_to_scheme.files .. fs.os_to_posix_path(vim.fn.fnamemodify(bufname, ":p")) + config.adapter_to_scheme.files .. fs.os_to_posix_path(vim.fn.fnamemodify(bufname, ':p')) ) local replaced = util.rename_buffer(bufnr, new_name) return not replaced @@ -839,149 +839,149 @@ end M._get_highlights = function() return { { - name = "OilEmpty", - link = "Comment", - desc = "Empty column values", + name = 'OilEmpty', + link = 'Comment', + desc = 'Empty column values', }, { - name = "OilHidden", - link = "Comment", - desc = "Hidden entry in an oil buffer", + name = 'OilHidden', + link = 'Comment', + desc = 'Hidden entry in an oil buffer', }, { - name = "OilDir", - link = "Directory", - desc = "Directory names in an oil buffer", + name = 'OilDir', + link = 'Directory', + desc = 'Directory names in an oil buffer', }, { - name = "OilDirHidden", - link = "OilHidden", - desc = "Hidden directory names in an oil buffer", + name = 'OilDirHidden', + link = 'OilHidden', + desc = 'Hidden directory names in an oil buffer', }, { - name = "OilDirIcon", - link = "OilDir", - desc = "Icon for directories", + name = 'OilDirIcon', + link = 'OilDir', + desc = 'Icon for directories', }, { - name = "OilFileIcon", + name = 'OilFileIcon', link = nil, - desc = "Icon for files", + desc = 'Icon for files', }, { - name = "OilSocket", - link = "Keyword", - desc = "Socket files in an oil buffer", + name = 'OilSocket', + link = 'Keyword', + desc = 'Socket files in an oil buffer', }, { - name = "OilSocketHidden", - link = "OilHidden", - desc = "Hidden socket files in an oil buffer", + name = 'OilSocketHidden', + link = 'OilHidden', + desc = 'Hidden socket files in an oil buffer', }, { - name = "OilLink", + name = 'OilLink', link = nil, - desc = "Soft links in an oil buffer", + desc = 'Soft links in an oil buffer', }, { - name = "OilOrphanLink", + name = 'OilOrphanLink', link = nil, - desc = "Orphaned soft links in an oil buffer", + desc = 'Orphaned soft links in an oil buffer', }, { - name = "OilLinkHidden", - link = "OilHidden", - desc = "Hidden soft links in an oil buffer", + name = 'OilLinkHidden', + link = 'OilHidden', + desc = 'Hidden soft links in an oil buffer', }, { - name = "OilOrphanLinkHidden", - link = "OilLinkHidden", - desc = "Hidden orphaned soft links in an oil buffer", + name = 'OilOrphanLinkHidden', + link = 'OilLinkHidden', + desc = 'Hidden orphaned soft links in an oil buffer', }, { - name = "OilLinkTarget", - link = "Comment", - desc = "The target of a soft link", + name = 'OilLinkTarget', + link = 'Comment', + desc = 'The target of a soft link', }, { - name = "OilOrphanLinkTarget", - link = "DiagnosticError", - desc = "The target of an orphaned soft link", + name = 'OilOrphanLinkTarget', + link = 'DiagnosticError', + desc = 'The target of an orphaned soft link', }, { - name = "OilLinkTargetHidden", - link = "OilHidden", - desc = "The target of a hidden soft link", + name = 'OilLinkTargetHidden', + link = 'OilHidden', + desc = 'The target of a hidden soft link', }, { - name = "OilOrphanLinkTargetHidden", - link = "OilOrphanLinkTarget", - desc = "The target of an hidden orphaned soft link", + name = 'OilOrphanLinkTargetHidden', + link = 'OilOrphanLinkTarget', + desc = 'The target of an hidden orphaned soft link', }, { - name = "OilFile", + name = 'OilFile', link = nil, - desc = "Normal files in an oil buffer", + desc = 'Normal files in an oil buffer', }, { - name = "OilFileHidden", - link = "OilHidden", - desc = "Hidden normal files in an oil buffer", + name = 'OilFileHidden', + link = 'OilHidden', + desc = 'Hidden normal files in an oil buffer', }, { - name = "OilExecutable", - link = "DiagnosticOk", - desc = "Executable files in an oil buffer", + name = 'OilExecutable', + link = 'DiagnosticOk', + desc = 'Executable files in an oil buffer', }, { - name = "OilExecutableHidden", - link = "OilHidden", - desc = "Hidden executable files in an oil buffer", + name = 'OilExecutableHidden', + link = 'OilHidden', + desc = 'Hidden executable files in an oil buffer', }, { - name = "OilCreate", - link = "DiagnosticInfo", - desc = "Create action in the oil preview window", + name = 'OilCreate', + link = 'DiagnosticInfo', + desc = 'Create action in the oil preview window', }, { - name = "OilDelete", - link = "DiagnosticError", - desc = "Delete action in the oil preview window", + name = 'OilDelete', + link = 'DiagnosticError', + desc = 'Delete action in the oil preview window', }, { - name = "OilMove", - link = "DiagnosticWarn", - desc = "Move action in the oil preview window", + name = 'OilMove', + link = 'DiagnosticWarn', + desc = 'Move action in the oil preview window', }, { - name = "OilCopy", - link = "DiagnosticHint", - desc = "Copy action in the oil preview window", + name = 'OilCopy', + link = 'DiagnosticHint', + desc = 'Copy action in the oil preview window', }, { - name = "OilChange", - link = "Special", - desc = "Change action in the oil preview window", + name = 'OilChange', + link = 'Special', + desc = 'Change action in the oil preview window', }, { - name = "OilRestore", - link = "OilCreate", - desc = "Restore (from the trash) action in the oil preview window", + name = 'OilRestore', + link = 'OilCreate', + desc = 'Restore (from the trash) action in the oil preview window', }, { - name = "OilPurge", - link = "OilDelete", - desc = "Purge (Permanently delete a file from trash) action in the oil preview window", + name = 'OilPurge', + link = 'OilDelete', + desc = 'Purge (Permanently delete a file from trash) action in the oil preview window', }, { - name = "OilTrash", - link = "OilDelete", - desc = "Trash (delete a file to trash) action in the oil preview window", + name = 'OilTrash', + link = 'OilDelete', + desc = 'Trash (delete a file to trash) action in the oil preview window', }, { - name = "OilTrashSourcePath", - link = "Comment", - desc = "Virtual text that shows the original path of file in the trash", + name = 'OilTrashSourcePath', + link = 'Comment', + desc = 'Virtual text that shows the original path of file in the trash', }, } end @@ -995,14 +995,14 @@ local function set_colors() -- TODO can remove this call once we drop support for Neovim 0.8. FloatTitle was introduced as a -- built-in highlight group in 0.9, and we can start to rely on colorschemes setting it. ---@diagnostic disable-next-line: deprecated - if vim.fn.has("nvim-0.9") == 0 and not pcall(vim.api.nvim_get_hl_by_name, "FloatTitle", true) then + if vim.fn.has('nvim-0.9') == 0 and not pcall(vim.api.nvim_get_hl_by_name, 'FloatTitle', true) then ---@diagnostic disable-next-line: deprecated - local border = vim.api.nvim_get_hl_by_name("FloatBorder", true) + local border = vim.api.nvim_get_hl_by_name('FloatBorder', true) ---@diagnostic disable-next-line: deprecated - local normal = vim.api.nvim_get_hl_by_name("Normal", true) + local normal = vim.api.nvim_get_hl_by_name('Normal', true) vim.api.nvim_set_hl( 0, - "FloatTitle", + 'FloatTitle', { fg = normal.foreground, bg = border.background or normal.background } ) end @@ -1018,35 +1018,35 @@ M.save = function(opts, cb) opts = opts or {} if not cb then cb = function(err) - if err and err ~= "Canceled" then + if err and err ~= 'Canceled' then vim.notify(err, vim.log.levels.ERROR) end end end - local mutator = require("oil.mutator") + local mutator = require('oil.mutator') mutator.try_write_changes(opts.confirm, cb) end local function restore_alt_buf() - if vim.bo.filetype == "oil" then - require("oil.view").set_win_options() - vim.api.nvim_win_set_var(0, "oil_did_enter", true) + if vim.bo.filetype == 'oil' then + require('oil.view').set_win_options() + vim.api.nvim_win_set_var(0, 'oil_did_enter', true) elseif vim.w.oil_did_enter then - vim.api.nvim_win_del_var(0, "oil_did_enter") + vim.api.nvim_win_del_var(0, 'oil_did_enter') -- We are entering a non-oil buffer *after* having been in an oil buffer - local has_orig, orig_buffer = pcall(vim.api.nvim_win_get_var, 0, "oil_original_buffer") + local has_orig, orig_buffer = pcall(vim.api.nvim_win_get_var, 0, 'oil_original_buffer') if has_orig and vim.api.nvim_buf_is_valid(orig_buffer) then if vim.api.nvim_get_current_buf() ~= orig_buffer then -- If we are editing a new file after navigating around oil, set the alternate buffer -- to be the last buffer we were in before opening oil - vim.fn.setreg("#", orig_buffer) + vim.fn.setreg('#', orig_buffer) else -- If we are editing the same buffer that we started oil from, set the alternate to be -- what it was before we opened oil local has_orig_alt, alt_buffer = - pcall(vim.api.nvim_win_get_var, 0, "oil_original_alternate") + pcall(vim.api.nvim_win_get_var, 0, 'oil_original_alternate') if has_orig_alt and vim.api.nvim_buf_is_valid(alt_buffer) then - vim.fn.setreg("#", alt_buffer) + vim.fn.setreg('#', alt_buffer) end end end @@ -1056,11 +1056,11 @@ end ---@private ---@param bufnr integer M.load_oil_buffer = function(bufnr) - local config = require("oil.config") - local keymap_util = require("oil.keymap_util") - local loading = require("oil.loading") - local util = require("oil.util") - local view = require("oil.view") + local config = require('oil.config') + local keymap_util = require('oil.keymap_util') + local loading = require('oil.loading') + local util = require('oil.util') + local view = require('oil.view') local bufname = vim.api.nvim_buf_get_name(bufnr) local scheme, path = util.parse_url(bufname) if config.adapter_aliases[scheme] then @@ -1076,13 +1076,13 @@ M.load_oil_buffer = function(bufnr) local adapter = assert(config.get_adapter_by_scheme(scheme)) - if vim.endswith(bufname, "/") then + if vim.endswith(bufname, '/') then -- This is a small quality-of-life thing. If the buffer name ends with a `/`, we know it's a -- directory, and can set the filetype early. This is helpful for adapters with a lot of latency -- (e.g. ssh) because it will set up the filetype keybinds at the *beginning* of the loading -- process. - vim.bo[bufnr].filetype = "oil" - vim.bo[bufnr].buftype = "acwrite" + vim.bo[bufnr].filetype = 'oil' + vim.bo[bufnr].buftype = 'acwrite' keymap_util.set_keymaps(config.keymaps, bufnr) end loading.set_loading(bufnr, true) @@ -1109,19 +1109,19 @@ M.load_oil_buffer = function(bufnr) local new_scheme = util.parse_url(new_url) if not new_scheme then loading.set_loading(bufnr, false) - vim.cmd.doautocmd({ args = { "BufReadPre", new_url }, mods = { emsg_silent = true } }) - vim.cmd.doautocmd({ args = { "BufReadPost", new_url }, mods = { emsg_silent = true } }) + vim.cmd.doautocmd({ args = { 'BufReadPre', new_url }, mods = { emsg_silent = true } }) + vim.cmd.doautocmd({ args = { 'BufReadPost', new_url }, mods = { emsg_silent = true } }) return end bufname = new_url end - if vim.endswith(bufname, "/") then - vim.cmd.doautocmd({ args = { "BufReadPre", bufname }, mods = { emsg_silent = true } }) + if vim.endswith(bufname, '/') then + vim.cmd.doautocmd({ args = { 'BufReadPre', bufname }, mods = { emsg_silent = true } }) view.initialize(bufnr) - vim.cmd.doautocmd({ args = { "BufReadPost", bufname }, mods = { emsg_silent = true } }) + vim.cmd.doautocmd({ args = { 'BufReadPost', bufname }, mods = { emsg_silent = true } }) else - vim.bo[bufnr].buftype = "acwrite" + vim.bo[bufnr].buftype = 'acwrite' adapter.read_file(bufnr) end restore_alt_buf() @@ -1132,7 +1132,7 @@ M.load_oil_buffer = function(bufnr) end local function close_preview_window_if_not_in_oil() - local util = require("oil.util") + local util = require('oil.util') local preview_win_id = util.get_preview_win() if not preview_win_id or not vim.w[preview_win_id].oil_entry_id then return @@ -1154,13 +1154,13 @@ local _on_key_ns = 0 ---Initialize oil ---@param opts oil.setupOpts|nil M.setup = function(opts) - local Ringbuf = require("oil.ringbuf") - local config = require("oil.config") + local Ringbuf = require('oil.ringbuf') + local config = require('oil.config') config.setup(opts) set_colors() local callback = function(args) - local util = require("oil.util") + local util = require('oil.util') if args.smods.tab > 0 then vim.cmd.tabnew() end @@ -1170,23 +1170,23 @@ M.setup = function(opts) local i = 1 while i <= #args.fargs do local v = args.fargs[i] - if v == "--float" then + if v == '--float' then float = true table.remove(args.fargs, i) - elseif v == "--trash" then + elseif v == '--trash' then trash = true table.remove(args.fargs, i) - elseif v == "--preview" then + elseif v == '--preview' then -- In the future we may want to support specifying options for the preview window (e.g. -- vertical/horizontal), but if you want that level of control maybe just use the API preview = true table.remove(args.fargs, i) - elseif v == "--progress" then - local mutator = require("oil.mutator") + elseif v == '--progress' then + local mutator = require('oil.mutator') if mutator.is_mutating() then mutator.show_progress() else - vim.notify("No mutation in progress", vim.log.levels.WARN) + vim.notify('No mutation in progress', vim.log.levels.WARN) end return else @@ -1194,7 +1194,7 @@ M.setup = function(opts) end end - if not float and (args.smods.vertical or args.smods.horizontal or args.smods.split ~= "") then + if not float and (args.smods.vertical or args.smods.horizontal or args.smods.split ~= '') then local range = args.count > 0 and { args.count } or nil local cmdargs = { mods = { split = args.smods.split }, range = range } if args.smods.vertical then @@ -1204,13 +1204,13 @@ M.setup = function(opts) end end - local method = float and "open_float" or "open" + local method = float and 'open_float' or 'open' local path = args.fargs[1] local open_opts = {} if trash then local url = M.get_url_for_path(path, false) local _, new_path = util.parse_url(url) - path = "oil-trash://" .. new_path + path = 'oil-trash://' .. new_path end if preview then open_opts.preview = {} @@ -1218,32 +1218,32 @@ M.setup = function(opts) M[method](path, open_opts) end vim.api.nvim_create_user_command( - "Oil", + 'Oil', callback, - { desc = "Open oil file browser on a directory", nargs = "*", complete = "dir", count = true } + { desc = 'Open oil file browser on a directory', nargs = '*', complete = 'dir', count = true } ) - local aug = vim.api.nvim_create_augroup("Oil", {}) + local aug = vim.api.nvim_create_augroup('Oil', {}) if config.default_file_explorer then vim.g.loaded_netrw = 1 vim.g.loaded_netrwPlugin = 1 -- If netrw was already loaded, clear this augroup - if vim.fn.exists("#FileExplorer") then - vim.api.nvim_create_augroup("FileExplorer", { clear = true }) + if vim.fn.exists('#FileExplorer') then + vim.api.nvim_create_augroup('FileExplorer', { clear = true }) end end local patterns = {} local filetype_patterns = {} for scheme in pairs(config.adapters) do - table.insert(patterns, scheme .. "*") - filetype_patterns[scheme .. ".*"] = { "oil", { priority = 10 } } + table.insert(patterns, scheme .. '*') + filetype_patterns[scheme .. '.*'] = { 'oil', { priority = 10 } } end for scheme in pairs(config.adapter_aliases) do - table.insert(patterns, scheme .. "*") - filetype_patterns[scheme .. ".*"] = { "oil", { priority = 10 } } + table.insert(patterns, scheme .. '*') + filetype_patterns[scheme .. '.*'] = { 'oil', { priority = 10 } } end - local scheme_pattern = table.concat(patterns, ",") + local scheme_pattern = table.concat(patterns, ',') -- We need to add these patterns to the filetype matcher so the filetype doesn't get overridden -- by other patterns. See https://github.com/stevearc/oil.nvim/issues/47 vim.filetype.add({ @@ -1256,13 +1256,13 @@ M.setup = function(opts) keybuf:push(char) end, _on_key_ns) end - vim.api.nvim_create_autocmd("ColorScheme", { - desc = "Set default oil highlights", + vim.api.nvim_create_autocmd('ColorScheme', { + desc = 'Set default oil highlights', group = aug, - pattern = "*", + pattern = '*', callback = set_colors, }) - vim.api.nvim_create_autocmd("BufReadCmd", { + vim.api.nvim_create_autocmd('BufReadCmd', { group = aug, pattern = scheme_pattern, nested = true, @@ -1270,7 +1270,7 @@ M.setup = function(opts) M.load_oil_buffer(params.buf) end, }) - vim.api.nvim_create_autocmd("BufWriteCmd", { + vim.api.nvim_create_autocmd('BufWriteCmd', { group = aug, pattern = scheme_pattern, nested = true, @@ -1278,18 +1278,18 @@ M.setup = function(opts) local last_keys = keybuf:as_str() local winid = vim.api.nvim_get_current_win() -- If the user issued a :wq or similar, we should quit after saving - local quit_after_save = vim.endswith(last_keys, ":wq\r") - or vim.endswith(last_keys, ":x\r") - or vim.endswith(last_keys, "ZZ") - local quit_all = vim.endswith(last_keys, ":wqa\r") - or vim.endswith(last_keys, ":wqal\r") - or vim.endswith(last_keys, ":wqall\r") + local quit_after_save = vim.endswith(last_keys, ':wq\r') + or vim.endswith(last_keys, ':x\r') + or vim.endswith(last_keys, 'ZZ') + local quit_all = vim.endswith(last_keys, ':wqa\r') + or vim.endswith(last_keys, ':wqal\r') + or vim.endswith(last_keys, ':wqall\r') local bufname = vim.api.nvim_buf_get_name(params.buf) - if vim.endswith(bufname, "/") then - vim.cmd.doautocmd({ args = { "BufWritePre", params.file }, mods = { silent = true } }) + if vim.endswith(bufname, '/') then + vim.cmd.doautocmd({ args = { 'BufWritePre', params.file }, mods = { silent = true } }) M.save(nil, function(err) if err then - if err ~= "Canceled" then + if err ~= 'Canceled' then vim.notify(err, vim.log.levels.ERROR) end elseif winid == vim.api.nvim_get_current_win() then @@ -1300,46 +1300,46 @@ M.setup = function(opts) end end end) - vim.cmd.doautocmd({ args = { "BufWritePost", params.file }, mods = { silent = true } }) + vim.cmd.doautocmd({ args = { 'BufWritePost', params.file }, mods = { silent = true } }) else local adapter = assert(config.get_adapter_by_scheme(bufname)) adapter.write_file(params.buf) end end, }) - vim.api.nvim_create_autocmd("BufLeave", { - desc = "Save alternate buffer for later", + vim.api.nvim_create_autocmd('BufLeave', { + desc = 'Save alternate buffer for later', group = aug, - pattern = "*", + pattern = '*', callback = function() - local util = require("oil.util") + local util = require('oil.util') if not util.is_oil_bufnr(0) then vim.w.oil_original_buffer = vim.api.nvim_get_current_buf() vim.w.oil_original_view = vim.fn.winsaveview() ---@diagnostic disable-next-line: param-type-mismatch - vim.w.oil_original_alternate = vim.fn.bufnr("#") + vim.w.oil_original_alternate = vim.fn.bufnr('#') end end, }) - vim.api.nvim_create_autocmd("BufEnter", { - desc = "Set/unset oil window options and restore alternate buffer", + vim.api.nvim_create_autocmd('BufEnter', { + desc = 'Set/unset oil window options and restore alternate buffer', group = aug, - pattern = "*", + pattern = '*', callback = function() - local util = require("oil.util") + local util = require('oil.util') local bufname = vim.api.nvim_buf_get_name(0) local scheme = util.parse_url(bufname) local is_oil_buf = scheme and config.adapters[scheme] -- We want to filter out oil buffers that are not directories (i.e. ssh files) - local is_oil_dir_or_unknown = (vim.bo.filetype == "oil" or vim.bo.filetype == "") + local is_oil_dir_or_unknown = (vim.bo.filetype == 'oil' or vim.bo.filetype == '') if is_oil_buf and is_oil_dir_or_unknown then - local view = require("oil.view") + local view = require('oil.view') view.maybe_set_cursor() -- While we are in an oil buffer, set the alternate file to the buffer we were in prior to -- opening oil - local has_orig, orig_buffer = pcall(vim.api.nvim_win_get_var, 0, "oil_original_buffer") + local has_orig, orig_buffer = pcall(vim.api.nvim_win_get_var, 0, 'oil_original_buffer') if has_orig and vim.api.nvim_buf_is_valid(orig_buffer) then - vim.fn.setreg("#", orig_buffer) + vim.fn.setreg('#', orig_buffer) end view.set_win_options() vim.w.oil_did_enter = true @@ -1354,39 +1354,39 @@ M.setup = function(opts) end, }) - vim.api.nvim_create_autocmd({ "BufWinEnter", "WinNew", "WinEnter" }, { - desc = "Reset bufhidden when entering a preview buffer", + vim.api.nvim_create_autocmd({ 'BufWinEnter', 'WinNew', 'WinEnter' }, { + desc = 'Reset bufhidden when entering a preview buffer', group = aug, - pattern = "*", + pattern = '*', callback = function() -- If we have entered a "preview" buffer in a non-preview window, reset bufhidden if vim.b.oil_preview_buffer and not vim.wo.previewwindow then - vim.bo.bufhidden = vim.api.nvim_get_option_value("bufhidden", { scope = "global" }) + vim.bo.bufhidden = vim.api.nvim_get_option_value('bufhidden', { scope = 'global' }) vim.b.oil_preview_buffer = nil end end, }) if not config.silence_scp_warning then - vim.api.nvim_create_autocmd("BufNew", { - desc = "Warn about scp:// usage", + vim.api.nvim_create_autocmd('BufNew', { + desc = 'Warn about scp:// usage', group = aug, - pattern = "scp://*", + pattern = 'scp://*', once = true, callback = function() vim.notify( - "If you are trying to browse using Oil, use oil-ssh:// instead of scp://\nSet `silence_scp_warning = true` in oil.setup() to disable this message.\nSee https://github.com/stevearc/oil.nvim/issues/27 for more information.", + 'If you are trying to browse using Oil, use oil-ssh:// instead of scp://\nSet `silence_scp_warning = true` in oil.setup() to disable this message.\nSee https://github.com/stevearc/oil.nvim/issues/27 for more information.', vim.log.levels.WARN ) end, }) end - vim.api.nvim_create_autocmd("WinNew", { - desc = "Restore window options when splitting an oil window", + vim.api.nvim_create_autocmd('WinNew', { + desc = 'Restore window options when splitting an oil window', group = aug, - pattern = "*", + pattern = '*', nested = true, callback = function(params) - local util = require("oil.util") + local util = require('oil.util') if not util.is_oil_bufnr(params.buf) or vim.w.oil_did_enter then return end @@ -1405,7 +1405,7 @@ M.setup = function(opts) end if not parent_win then vim.notify( - "Oil split could not find parent window. Please try to replicate whatever you just did and report a bug on github", + 'Oil split could not find parent window. Please try to replicate whatever you just did and report a bug on github', vim.log.levels.WARN ) return @@ -1420,15 +1420,15 @@ M.setup = function(opts) }) -- mksession doesn't save oil buffers in a useful way. We have to manually load them after a -- session finishes loading. See https://github.com/stevearc/oil.nvim/issues/29 - vim.api.nvim_create_autocmd("SessionLoadPost", { - desc = "Load oil buffers after a session is loaded", + vim.api.nvim_create_autocmd('SessionLoadPost', { + desc = 'Load oil buffers after a session is loaded', group = aug, - pattern = "*", + pattern = '*', callback = function(params) if vim.g.SessionLoad ~= 1 then return end - local util = require("oil.util") + local util = require('oil.util') local scheme = util.parse_url(params.file) if config.adapters[scheme] and vim.api.nvim_buf_line_count(params.buf) == 1 then M.load_oil_buffer(params.buf) @@ -1437,10 +1437,10 @@ M.setup = function(opts) }) if config.default_file_explorer then - vim.api.nvim_create_autocmd("BufAdd", { - desc = "Detect directory buffer and open oil file browser", + vim.api.nvim_create_autocmd('BufAdd', { + desc = 'Detect directory buffer and open oil file browser', group = aug, - pattern = "*", + pattern = '*', nested = true, callback = function(params) maybe_hijack_directory_buffer(params.buf) diff --git a/lua/oil/keymap_util.lua b/lua/oil/keymap_util.lua index 8c58738..8d62c93 100644 --- a/lua/oil/keymap_util.lua +++ b/lua/oil/keymap_util.lua @@ -1,7 +1,7 @@ -local actions = require("oil.actions") -local config = require("oil.config") -local layout = require("oil.layout") -local util = require("oil.util") +local actions = require('oil.actions') +local config = require('oil.config') +local layout = require('oil.layout') +local util = require('oil.util') local M = {} ---@param rhs string|table|fun() @@ -9,14 +9,14 @@ local M = {} ---@return table opts ---@return string|nil mode local function resolve(rhs) - if type(rhs) == "string" and vim.startswith(rhs, "actions.") then - local action_name = vim.split(rhs, ".", { plain = true })[2] + if type(rhs) == 'string' and vim.startswith(rhs, 'actions.') then + local action_name = vim.split(rhs, '.', { plain = true })[2] local action = actions[action_name] 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 return resolve(action) - elseif type(rhs) == "table" then + elseif type(rhs) == 'table' then local opts = vim.deepcopy(rhs) -- 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]) @@ -25,17 +25,17 @@ local function resolve(rhs) if parent_opts.desc and not opts.desc then if opts.opts then 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 opts.desc = parent_opts.desc end end local mode = opts.mode - if type(rhs.callback) == "string" then + if type(rhs.callback) == 'string' then local action_opts, action_mode 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 end @@ -46,7 +46,7 @@ local function resolve(rhs) opts.deprecated = 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 opts.opts = nil local orig_callback = callback @@ -68,7 +68,7 @@ M.set_keymaps = function(keymaps, bufnr) for k, v in pairs(keymaps) do local rhs, opts, mode = resolve(v) 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 @@ -95,9 +95,9 @@ M.show_help = function(keymaps) local all_lhs = lhs_to_all_lhs[k] if all_lhs then 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)) - 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 table.sort(keymap_entries, function(a, b) @@ -108,20 +108,20 @@ M.show_help = function(keymaps) local highlights = {} local max_line = 1 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)) table.insert(lines, line) local start = 1 for _, key in ipairs(entry.all_lhs) do 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 end end local bufnr = vim.api.nvim_create_buf(false, true) 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 local hl_group, lnum, start_col, end_col = unpack(hl) 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, }) end - vim.keymap.set("n", "q", "close", { buffer = bufnr }) - vim.keymap.set("n", "", "close", { buffer = bufnr }) + vim.keymap.set('n', 'q', 'close', { buffer = bufnr }) + vim.keymap.set('n', '', 'close', { buffer = bufnr }) vim.bo[bufnr].modifiable = false - vim.bo[bufnr].bufhidden = "wipe" + vim.bo[bufnr].bufhidden = 'wipe' local editor_width = vim.o.columns local editor_height = layout.get_editor_height() local winid = vim.api.nvim_open_win(bufnr, true, { - relative = "editor", + relative = 'editor', row = math.max(0, (editor_height - #lines) / 2), col = math.max(0, (editor_width - max_line - 1) / 2), width = math.min(editor_width, max_line + 1), height = math.min(editor_height, #lines), zindex = 150, - style = "minimal", + style = 'minimal', border = config.keymaps_help.border, }) local function close() @@ -151,13 +151,13 @@ M.show_help = function(keymaps) vim.api.nvim_win_close(winid, true) end end - vim.api.nvim_create_autocmd("BufLeave", { + vim.api.nvim_create_autocmd('BufLeave', { callback = close, once = true, nested = true, buffer = bufnr, }) - vim.api.nvim_create_autocmd("WinLeave", { + vim.api.nvim_create_autocmd('WinLeave', { callback = close, once = true, nested = true, diff --git a/lua/oil/layout.lua b/lua/oil/layout.lua index 6d4563d..0f32fcd 100644 --- a/lua/oil/layout.lua +++ b/lua/oil/layout.lua @@ -43,7 +43,7 @@ local function calc_list(values, max_value, aggregator, limit) local ret = limit if not max_value or not values then return nil - elseif type(values) == "table" then + elseif type(values) == 'table' then for _, v in ipairs(values) do ret = aggregator(ret, calc_float(v, max_value)) end @@ -106,12 +106,12 @@ end ---@return vim.api.keyset.win_config 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_height = M.get_editor_height() 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 end 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 win_opts = { - relative = "editor", + relative = 'editor', width = width, height = height, row = row, @@ -144,8 +144,8 @@ end ---@return oil.WinLayout root_dim New dimensions of the original window ---@return oil.WinLayout new_dim New dimensions of the new window M.split_window = function(winid, direction, gap) - if direction == "auto" then - direction = vim.o.splitright and "right" or "left" + if direction == 'auto' then + direction = vim.o.splitright and 'right' or 'left' end 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, 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. dim_root.col = float_config.col[vim.val_idx] dim_root.row = float_config.row[vim.val_idx] end 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_root.width = dim_new.width else @@ -171,13 +171,13 @@ M.split_window = function(winid, direction, gap) dim_root.height = dim_new.height end - if direction == "left" then + if direction == 'left' then 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 - elseif direction == "above" then + elseif direction == 'above' then 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 end diff --git a/lua/oil/loading.lua b/lua/oil/loading.lua index 6e575c5..633af6e 100644 --- a/lua/oil/loading.lua +++ b/lua/oil/loading.lua @@ -1,4 +1,4 @@ -local util = require("oil.util") +local util = require('oil.util') local M = {} local timers = {} @@ -12,14 +12,14 @@ M.is_loading = function(bufnr) end local spinners = { - dots = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" }, + dots = { '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' }, } ---@param name_or_frames string|string[] ---@return fun(): string M.get_iter = function(name_or_frames) local frames - if type(name_or_frames) == "string" then + if type(name_or_frames) == 'string' then frames = spinners[name_or_frames] if not frames then error(string.format("Unrecognized spinner: '%s'", name_or_frames)) @@ -35,26 +35,26 @@ M.get_iter = function(name_or_frames) end 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, width = 20, }) local i = 0 return function() - local chars = { "[" } + local chars = { '[' } for _ = 1, opts.width - 2 do - table.insert(chars, " ") + table.insert(chars, ' ') end - table.insert(chars, "]") + table.insert(chars, ']') for j = i - opts.bar_size, i do if j > 1 and j < opts.width then - chars[j] = "=" + chars[j] = '=' end end i = (i + 1) % (opts.width + opts.bar_size) - return table.concat(chars, "") + return table.concat(chars, '') end end @@ -75,7 +75,7 @@ M.set_loading = function(bufnr, is_loading) return end 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) end) ) diff --git a/lua/oil/log.lua b/lua/oil/log.lua index 28a4f9c..a16394b 100644 --- a/lua/oil/log.lua +++ b/lua/oil/log.lua @@ -11,14 +11,14 @@ Log.level = vim.log.levels.WARN ---@return string 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 - stdpath = vim.fn.stdpath("cache") + stdpath = vim.fn.stdpath('cache') end - assert(type(stdpath) == "string") - return fs.join(stdpath, "oil.log") + assert(type(stdpath) == 'string') + return fs.join(stdpath, 'oil.log') end ---@param level integer @@ -29,19 +29,19 @@ local function format(level, msg, ...) local args = vim.F.pack_len(...) for i = 1, args.n do local v = args[i] - if type(v) == "table" then + if type(v) == 'table' then args[i] = vim.inspect(v) elseif v == nil then - args[i] = "nil" + args[i] = 'nil' end end local ok, text = pcall(string.format, msg, vim.F.unpack_len(args)) -- TODO figure out how to get formatted time inside luv callback -- local timestr = vim.fn.strftime("%Y-%m-%d %H:%M:%S") - local timestr = "" + local timestr = '' if ok then 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 return string.format( "%s[ERROR] error formatting log line: '%s' args %s", @@ -67,22 +67,22 @@ local function initialize() local stat = uv.fs_stat(filepath) if stat and stat.size > 10 * 1024 * 1024 then - local backup = filepath .. ".1" + local backup = filepath .. '.1' uv.fs_unlink(backup) uv.fs_rename(filepath, backup) end 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 - 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) else write = function(line) logfile:write(line) - logfile:write("\n") + logfile:write('\n') logfile:flush() end end diff --git a/lua/oil/lsp/helpers.lua b/lua/oil/lsp/helpers.lua index 45264dc..cadeca1 100644 --- a/lua/oil/lsp/helpers.lua +++ b/lua/oil/lsp/helpers.lua @@ -1,7 +1,7 @@ -local config = require("oil.config") -local fs = require("oil.fs") -local util = require("oil.util") -local workspace = require("oil.lsp.workspace") +local config = require('oil.config') +local fs = require('oil.fs') +local util = require('oil.util') +local workspace = require('oil.lsp.workspace') local M = {} @@ -12,7 +12,7 @@ M.will_perform_file_operations = function(actions) local creates = {} local deletes = {} 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) assert(src_path) 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)) src_path = fs.posix_to_os_path(src_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 - elseif src_adapter.name == "files" then + elseif src_adapter.name == 'files' then table.insert(deletes, src_path) - elseif dest_adapter.name == "files" then + elseif dest_adapter.name == 'files' then table.insert(creates, src_path) end - elseif action.type == "create" then + elseif action.type == 'create' then local scheme, path = util.parse_url(action.url) path = fs.posix_to_os_path(assert(path)) local adapter = assert(config.get_adapter_by_scheme(scheme)) - if adapter.name == "files" then + if adapter.name == 'files' then table.insert(creates, path) end - elseif action.type == "delete" then + elseif action.type == 'delete' then local scheme, path = util.parse_url(action.url) path = fs.posix_to_os_path(assert(path)) local adapter = assert(config.get_adapter_by_scheme(scheme)) - if adapter.name == "files" then + if adapter.name == 'files' then table.insert(deletes, path) end - elseif action.type == "copy" then + elseif action.type == 'copy' then local scheme, path = util.parse_url(action.dest_url) path = fs.posix_to_os_path(assert(path)) local adapter = assert(config.get_adapter_by_scheme(scheme)) - if adapter.name == "files" then + if adapter.name == 'files' then table.insert(creates, path) end end @@ -84,7 +84,7 @@ M.will_perform_file_operations = function(actions) accum(workspace.will_rename_files(moves, { timeout_ms = timeout_ms })) if final_err then 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 ) end @@ -102,7 +102,7 @@ M.will_perform_file_operations = function(actions) local bufnr = vim.uri_to_bufnr(uri) local was_open = buf_was_modified[bufnr] ~= nil 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 if should_save then vim.api.nvim_buf_call(bufnr, function() diff --git a/lua/oil/lsp/workspace.lua b/lua/oil/lsp/workspace.lua index 8e48276..a0e26bb 100644 --- a/lua/oil/lsp/workspace.lua +++ b/lua/oil/lsp/workspace.lua @@ -1,13 +1,13 @@ -local fs = require("oil.fs") -local ms = require("vim.lsp.protocol").Methods -if vim.fn.has("nvim-0.10") == 0 then +local fs = require('oil.fs') +local ms = require('vim.lsp.protocol').Methods +if vim.fn.has('nvim-0.10') == 0 then ms = { - workspace_willCreateFiles = "workspace/willCreateFiles", - workspace_didCreateFiles = "workspace/didCreateFiles", - workspace_willDeleteFiles = "workspace/willDeleteFiles", - workspace_didDeleteFiles = "workspace/didDeleteFiles", - workspace_willRenameFiles = "workspace/willRenameFiles", - workspace_didRenameFiles = "workspace/didRenameFiles", + workspace_willCreateFiles = 'workspace/willCreateFiles', + workspace_didCreateFiles = 'workspace/didCreateFiles', + workspace_willDeleteFiles = 'workspace/willDeleteFiles', + workspace_didDeleteFiles = 'workspace/didDeleteFiles', + workspace_willRenameFiles = 'workspace/willRenameFiles', + workspace_didRenameFiles = 'workspace/didRenameFiles', } end @@ -16,7 +16,7 @@ local M = {} ---@param method string ---@return vim.lsp.Client[] 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 }) else ---@diagnostic disable-next-line: deprecated @@ -32,7 +32,7 @@ end ---@return boolean local function match_glob(glob, path) -- 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 end @@ -59,7 +59,7 @@ local function get_matching_paths(client, filters, paths) local match_fns = {} 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 glob = pattern.glob 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) -- 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 - glob = glob:gsub("/", "\\") + if vim.fn.has('nvim-0.12') == 0 and fs.is_windows then + glob = glob:gsub('/', '\\') end ---@type string|vim.lpeg.Pattern local glob_to_match = glob if vim.glob and vim.glob.to_lpeg then - glob = glob:gsub("{(.-)}", function(s) - local patterns = vim.split(s, ",") + glob = glob:gsub('{(.-)}', function(s) + local patterns = vim.split(s, ',') local filtered = {} for _, pat in ipairs(patterns) do - if pat ~= "" then + if pat ~= '' then table.insert(filtered, pat) end end if #filtered == 0 then - return "" + return '' end -- HACK around https://github.com/neovim/neovim/issues/28931 -- 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) return a:len() > b:len() end) end - return "{" .. table.concat(filtered, ",") .. "}" + return '{' .. table.concat(filtered, ',') .. '}' end) glob_to_match = vim.glob.to_lpeg(glob) @@ -102,7 +102,7 @@ local function get_matching_paths(client, filters, paths) local matches = pattern.matches table.insert(match_fns, function(path) 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 end @@ -163,10 +163,10 @@ local function will_file_operation(method, capability_name, files, options) for _, client in ipairs(clients) do local filters = vim.tbl_get( client.server_capabilities, - "workspace", - "fileOperations", + 'workspace', + 'fileOperations', capability_name, - "filters" + 'filters' ) local matching_files = get_matching_paths(client, filters, files) if matching_files then @@ -178,7 +178,7 @@ local function will_file_operation(method, capability_name, files, options) end, matching_files), } 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) else ---@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 local filters = vim.tbl_get( client.server_capabilities, - "workspace", - "fileOperations", + 'workspace', + 'fileOperations', capability_name, - "filters" + 'filters' ) local matching_files = get_matching_paths(client, filters, files) if matching_files then @@ -219,7 +219,7 @@ local function did_file_operation(method, capability_name, 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) else ---@diagnostic disable-next-line: param-type-mismatch @@ -239,13 +239,13 @@ end ---@return nil|{edit: lsp.WorkspaceEdit, offset_encoding: string}[] ---@return nil|string|lsp.ResponseError err 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 --- Notify the server that files were created from within the client. ---@param files string[] The files and folders that will be created function M.did_create_files(files) - did_file_operation(ms.workspace_didCreateFiles, "didCreate", files) + did_file_operation(ms.workspace_didCreateFiles, 'didCreate', files) end --- 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|string|lsp.ResponseError err 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 --- Notify the server that files were deleted from within the client. ---@param files string[] The files and folders that were deleted function M.did_delete_files(files) - did_file_operation(ms.workspace_didDeleteFiles, "didDelete", files) + did_file_operation(ms.workspace_didDeleteFiles, 'didDelete', files) end --- 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 local filters = vim.tbl_get( client.server_capabilities, - "workspace", - "fileOperations", - "willRename", - "filters" + 'workspace', + 'fileOperations', + 'willRename', + 'filters' ) local matching_files = get_matching_paths(client, filters, vim.tbl_keys(files)) if matching_files then @@ -300,7 +300,7 @@ function M.will_rename_files(files, options) end, matching_files), } 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(ms.workspace_willRenameFiles, params, options.timeout_ms or 1000, 0) else @@ -327,7 +327,7 @@ function M.did_rename_files(files) local clients = get_clients(ms.workspace_didRenameFiles) for _, client in ipairs(clients) do 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)) if matching_files then local params = { @@ -338,7 +338,7 @@ function M.did_rename_files(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) else ---@diagnostic disable-next-line: param-type-mismatch diff --git a/lua/oil/mutator/confirmation.lua b/lua/oil/mutator/confirmation.lua index 8bc8020..0aff7a7 100644 --- a/lua/oil/mutator/confirmation.lua +++ b/lua/oil/mutator/confirmation.lua @@ -1,7 +1,7 @@ -local columns = require("oil.columns") -local config = require("oil.config") -local layout = require("oil.layout") -local util = require("oil.util") +local columns = require('oil.columns') +local config = require('oil.config') +local layout = require('oil.layout') +local util = require('oil.util') local M = {} ---@param actions oil.Action[] @@ -12,17 +12,17 @@ local function is_simple_edit(actions) local num_move = 0 for _, action in ipairs(actions) do -- If there are any deletes, it is not a simple edit - if action.type == "delete" then + if action.type == 'delete' then return false - elseif action.type == "create" then + elseif action.type == 'create' then num_create = num_create + 1 - elseif action.type == "copy" then + elseif action.type == 'copy' then num_copy = num_copy + 1 -- Cross-adapter copies are not simple if util.parse_url(action.src_url) ~= util.parse_url(action.dest_url) then return false end - elseif action.type == "move" then + elseif action.type == 'move' then num_move = num_move + 1 -- Cross-adapter moves are not simple if util.parse_url(action.src_url) ~= util.parse_url(action.dest_url) then @@ -46,10 +46,10 @@ end ---@param lines string[] local function render_lines(winid, bufnr, lines) util.render_text(bufnr, lines, { - v_align = "top", - h_align = "left", + v_align = 'top', + h_align = 'left', winid = winid, - actions = { "[Y]es", "[N]o" }, + actions = { '[Y]es', '[N]o' }, }) end @@ -70,48 +70,48 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb) -- Create the buffer local bufnr = vim.api.nvim_create_buf(false, true) - vim.bo[bufnr].bufhidden = "wipe" + vim.bo[bufnr].bufhidden = 'wipe' local lines = {} local max_line_width = 0 for _, action in ipairs(actions) do local adapter = util.get_adapter_for_action(action) local line - if action.type == "change" then + if action.type == 'change' then ---@cast action oil.ChangeAction line = columns.render_change_action(adapter, action) else line = adapter.render_action(action) end -- We can't handle lines with newlines in them - line = line:gsub("\n", "") + line = line:gsub('\n', '') table.insert(lines, line) local line_width = vim.api.nvim_strwidth(line) if line_width > max_line_width then max_line_width = line_width end end - table.insert(lines, "") + table.insert(lines, '') -- Create the floating window local width, height = layout.calculate_dims(max_line_width, #lines + 1, config.confirmation) local ok, winid = pcall(vim.api.nvim_open_win, bufnr, true, { - relative = "editor", + relative = 'editor', width = width, height = height, row = math.floor((layout.get_editor_height() - height) / 2), col = math.floor((layout.get_editor_width() - width) / 2), zindex = 152, -- render on top of the floating window title - style = "minimal", + style = 'minimal', border = config.confirmation.border, }) 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) end - vim.bo[bufnr].filetype = "oil_preview" - vim.bo[bufnr].syntax = "oil_preview" + vim.bo[bufnr].filetype = 'oil_preview' + vim.bo[bufnr].syntax = 'oil_preview' 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 render_lines(winid, bufnr, lines) @@ -137,7 +137,7 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb) end cancel = make_callback(false) confirm = make_callback(true) - vim.api.nvim_create_autocmd("BufLeave", { + vim.api.nvim_create_autocmd('BufLeave', { callback = function() cancel() end, @@ -145,7 +145,7 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb) nested = true, buffer = bufnr, }) - vim.api.nvim_create_autocmd("WinLeave", { + vim.api.nvim_create_autocmd('WinLeave', { callback = function() cancel() end, @@ -154,12 +154,12 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb) }) table.insert( autocmds, - vim.api.nvim_create_autocmd("VimResized", { + vim.api.nvim_create_autocmd('VimResized', { callback = function() if vim.api.nvim_win_is_valid(winid) then width, height = layout.calculate_dims(max_line_width, #lines, config.confirmation) vim.api.nvim_win_set_config(winid, { - relative = "editor", + relative = 'editor', width = width, height = height, 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 - local cancel_keys = { "n", "N", "c", "C", "q", "", "" } + local cancel_keys = { 'n', 'N', 'c', 'C', 'q', '', '' } for _, cancel_key in ipairs(cancel_keys) do - vim.keymap.set("n", cancel_key, function() + vim.keymap.set('n', cancel_key, function() cancel() end, { buffer = bufnr, nowait = true }) end -- 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 - vim.keymap.set("n", confirm_key, function() + vim.keymap.set('n', confirm_key, function() confirm() end, { buffer = bufnr, nowait = true }) end diff --git a/lua/oil/mutator/init.lua b/lua/oil/mutator/init.lua index f55957d..8e03340 100644 --- a/lua/oil/mutator/init.lua +++ b/lua/oil/mutator/init.lua @@ -1,16 +1,16 @@ -local Progress = require("oil.mutator.progress") -local Trie = require("oil.mutator.trie") -local cache = require("oil.cache") -local columns = require("oil.columns") -local config = require("oil.config") -local confirmation = require("oil.mutator.confirmation") -local constants = require("oil.constants") -local fs = require("oil.fs") -local lsp_helpers = require("oil.lsp.helpers") -local oil = require("oil") -local parser = require("oil.mutator.parser") -local util = require("oil.util") -local view = require("oil.view") +local Progress = require('oil.mutator.progress') +local Trie = require('oil.mutator.trie') +local cache = require('oil.cache') +local columns = require('oil.columns') +local config = require('oil.config') +local confirmation = require('oil.mutator.confirmation') +local constants = require('oil.constants') +local fs = require('oil.fs') +local lsp_helpers = require('oil.lsp.helpers') +local oil = require('oil') +local parser = require('oil.mutator.parser') +local util = require('oil.util') +local view = require('oil.view') local M = {} local FIELD_NAME = constants.FIELD_NAME @@ -73,7 +73,7 @@ M.create_actions_from_diffs = function(all_diffs) local function add_action(action) 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 action.type == "create" then + if action.type == 'create' then if seen_creates[action.url] then return else @@ -87,11 +87,11 @@ M.create_actions_from_diffs = function(all_diffs) for bufnr, diffs in pairs(all_diffs) do local adapter = util.get_adapter(bufnr, true) if not adapter then - error("Missing adapter") + error('Missing adapter') end local parent_url = vim.api.nvim_buf_get_name(bufnr) for _, diff in ipairs(diffs) do - if diff.type == "new" then + if diff.type == 'new' then if diff.id then local by_id = diff_by_id[diff.id] ---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) else -- 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 url = parent_url:gsub("/$", "") + local url = parent_url:gsub('/$', '') for i, v in ipairs(pieces) do local is_last = i == #pieces - local entry_type = is_last and diff.entry_type or "directory" - local alternation = v:match("{([^}]+)}") + local entry_type = is_last and diff.entry_type or 'directory' + local alternation = v:match('{([^}]+)}') if is_last and alternation then -- Parse alternations like foo.{js,test.js} - for _, alt in ipairs(vim.split(alternation, ",")) do - local alt_url = url .. "/" .. v:gsub("{[^}]+}", alt) + for _, alt in ipairs(vim.split(alternation, ',')) do + local alt_url = url .. '/' .. v:gsub('{[^}]+}', alt) add_action({ - type = "create", + type = 'create', url = alt_url, entry_type = entry_type, link = diff.link, }) end else - url = url .. "/" .. v + url = url .. '/' .. v add_action({ - type = "create", + type = 'create', url = url, entry_type = entry_type, link = diff.link, @@ -129,9 +129,9 @@ M.create_actions_from_diffs = function(all_diffs) end end end - elseif diff.type == "change" then + elseif diff.type == 'change' then add_action({ - type = "change", + type = 'change', url = parent_url .. diff.name, entry_type = diff.entry_type, column = diff.column, @@ -151,7 +151,7 @@ M.create_actions_from_diffs = function(all_diffs) for id, diffs in pairs(diff_by_id) do local entry = cache.get_entry_by_id(id) if not entry then - error(string.format("Could not find entry %d", id)) + error(string.format('Could not find entry %d', id)) end ---HACK: access the has_delete field on the list-like table of diffs ---@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 for i, diff in ipairs(diffs) do add_action({ - type = i == #diffs and "move" or "copy", + type = i == #diffs and 'move' or 'copy', entry_type = entry[FIELD_TYPE], ---HACK: access the dest field we set above ---@diagnostic disable-next-line: undefined-field @@ -172,7 +172,7 @@ M.create_actions_from_diffs = function(all_diffs) else -- DELETE when no create add_action({ - type = "delete", + type = 'delete', entry_type = entry[FIELD_TYPE], 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 for _, diff in ipairs(diffs) do add_action({ - type = "copy", + type = 'copy', entry_type = entry[FIELD_TYPE], src_url = cache.get_parent_url(id) .. entry[FIELD_NAME], ---HACK: access the dest field we set above @@ -201,9 +201,9 @@ M.enforce_action_order = function(actions) local src_trie = Trie.new() local dest_trie = Trie.new() 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) - elseif action.type == "create" then + elseif action.type == 'create' then dest_trie:insert_action(action.url, action) else dest_trie:insert_action(action.dest_url, action) @@ -223,18 +223,18 @@ M.enforce_action_order = function(actions) ---@param action oil.Action local function get_deps(action) local ret = {} - if action.type == "delete" then + if action.type == 'delete' then src_trie:accum_children_of(action.url, ret) - elseif action.type == "create" then + elseif action.type == 'create' then -- Finish operating on parents first -- e.g. NEW /a BEFORE NEW /a/b dest_trie:accum_first_parents_of(action.url, ret) -- Process remove path before creating new path -- e.g. DELETE /a BEFORE NEW /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) - elseif action.type == "change" then + elseif action.type == 'change' then -- Finish operating on parents first -- e.g. NEW /a BEFORE CHANGE /a/b dest_trie:accum_first_parents_of(action.url, ret) @@ -244,9 +244,9 @@ M.enforce_action_order = function(actions) -- Finish copy from operations first -- e.g. COPY /a -> /b BEFORE CHANGE /a src_trie:accum_actions_at(action.url, ret, function(entry) - return entry.type == "copy" + return entry.type == 'copy' end) - elseif action.type == "move" then + elseif action.type == 'move' then -- Finish operating on parents first -- e.g. NEW /a BEFORE MOVE /z -> /a/b 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 -- e.g. MOVE /a -> /b BEFORE MOVE /c -> /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) - elseif action.type == "copy" then + elseif action.type == 'copy' then -- Finish operating on parents first -- e.g. NEW /a BEFORE COPY /z -> /a/b 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 -- e.g. MOVE /a -> /b BEFORE COPY /c -> /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 return ret @@ -312,24 +312,24 @@ M.enforce_action_order = function(actions) if selected then to_remove = selected 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 vim.startswith(loop_action.dest_url, loop_action.src_url) then - error("Detected cycle in desired paths") + error('Detected cycle in desired paths') end -- We've detected a move cycle (e.g. MOVE /a -> /b + MOVE /b -> /a) -- Split one of the moves and retry 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 = { - type = "move", + type = 'move', entry_type = loop_action.entry_type, src_url = loop_action.src_url, dest_url = intermediate_url, } local move_2 = { - type = "move", + type = 'move', entry_type = loop_action.entry_type, src_url = intermediate_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) src_trie:insert_action(move_1.src_url, move_1) else - error("Detected cycle in desired paths") + error('Detected cycle in desired paths') end end if selected then - if selected.type == "move" or selected.type == "copy" then - if vim.startswith(selected.dest_url, selected.src_url .. "/") then + if selected.type == 'move' or selected.type == 'copy' then + if vim.startswith(selected.dest_url, selected.src_url .. '/') then error( string.format( - "Cannot move or copy parent into itself: %s -> %s", + 'Cannot move or copy parent into itself: %s -> %s', selected.src_url, selected.dest_url ) @@ -360,9 +360,9 @@ M.enforce_action_order = function(actions) end 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) - elseif to_remove.type == "create" then + elseif to_remove.type == 'create' then dest_trie:remove_action(to_remove.url, to_remove) else dest_trie:remove_action(to_remove.dest_url, to_remove) @@ -387,8 +387,8 @@ local progress ---@param cb fun(err: nil|string) M.process_actions = function(actions, cb) vim.api.nvim_exec_autocmds( - "User", - { pattern = "OilActionsPre", modeline = false, data = { actions = actions } } + 'User', + { pattern = 'OilActionsPre', modeline = false, data = { actions = actions } } ) local did_complete = nil @@ -398,14 +398,14 @@ M.process_actions = function(actions, cb) -- Convert some cross-adapter moves to a copy + delete 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) -- 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 - action.type = "copy" + action.type = 'copy' table.insert(actions, { - type = "delete", + type = 'delete', url = action.src_url, entry_type = action.entry_type, }) @@ -421,8 +421,8 @@ M.process_actions = function(actions, cb) progress:close() progress = nil vim.api.nvim_exec_autocmds( - "User", - { pattern = "OilActionsPost", modeline = false, data = { err = err, actions = actions } } + 'User', + { pattern = 'OilActionsPost', modeline = false, data = { err = err, actions = actions } } ) cb(err) end @@ -435,7 +435,7 @@ M.process_actions = function(actions, cb) -- TODO some actions are actually cancelable. -- We should stop them instead of stopping after the current action cancel = function() - finish("Canceled") + finish('Canceled') end, }) end @@ -472,7 +472,7 @@ M.process_actions = function(actions, cb) next_action() end end) - if action.type == "change" then + if action.type == 'change' then ---@cast action oil.ChangeAction columns.perform_change_action(adapter, action, callback) else @@ -502,7 +502,7 @@ M.try_write_changes = function(confirm, cb) cb = function(_err) end end if mutation_in_progress then - cb("Cannot perform mutation when already in progress") + cb('Cannot perform mutation when already in progress') return end local current_buf = vim.api.nvim_get_current_buf() @@ -537,7 +537,7 @@ M.try_write_changes = function(confirm, cb) mutation_in_progress = false end - local ns = vim.api.nvim_create_namespace("Oil") + local ns = vim.api.nvim_create_namespace('Oil') vim.diagnostic.reset(ns) if not vim.tbl_isempty(all_errors) then for bufnr, errors in pairs(all_errors) do @@ -564,7 +564,7 @@ M.try_write_changes = function(confirm, cb) end) end unlock() - cb("Error parsing oil buffers") + cb('Error parsing oil buffers') return end @@ -572,7 +572,7 @@ M.try_write_changes = function(confirm, cb) confirmation.show(actions, confirm, function(proceed) if not proceed then unlock() - cb("Canceled") + cb('Canceled') return end @@ -581,7 +581,7 @@ M.try_write_changes = function(confirm, cb) vim.schedule_wrap(function(err) view.unlock_buffers() 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() cb(err) 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 view.set_last_cursor( 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 view.rerender_all_oil_buffers(nil, function(render_err) vim.api.nvim_exec_autocmds( - "User", - { pattern = "OilMutationComplete", modeline = false } + 'User', + { pattern = 'OilMutationComplete', modeline = false } ) cb(render_err) end) diff --git a/lua/oil/mutator/parser.lua b/lua/oil/mutator/parser.lua index e45a629..45f7239 100644 --- a/lua/oil/mutator/parser.lua +++ b/lua/oil/mutator/parser.lua @@ -1,10 +1,10 @@ -local cache = require("oil.cache") -local columns = require("oil.columns") -local config = require("oil.config") -local constants = require("oil.constants") -local fs = require("oil.fs") -local util = require("oil.util") -local view = require("oil.view") +local cache = require('oil.cache') +local columns = require('oil.columns') +local config = require('oil.config') +local constants = require('oil.constants') +local fs = require('oil.fs') +local util = require('oil.util') +local view = require('oil.view') local M = {} local FIELD_ID = constants.FIELD_ID @@ -37,7 +37,7 @@ local FIELD_META = constants.FIELD_META ---@return string ---@return boolean 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 name = name:sub(1, name:len() - 1) end @@ -52,8 +52,8 @@ local function compare_link_target(meta, parsed_entry) return false end -- Make sure we trim off any trailing path slashes from both sources - local meta_name = meta.link:gsub("[/\\]$", "") - local parsed_name = parsed_entry.link_target:gsub("[/\\]$", "") + local meta_name = meta.link:gsub('[/\\]$', '') + local parsed_name = parsed_entry.link_target:gsub('[/\\]$', '') return meta_name == parsed_name end @@ -72,9 +72,9 @@ M.parse_line = function(adapter, line, column_defs) local ret = {} local ranges = {} local start = 1 - local value, rem = line:match("^/(%d+) (.+)$") + local value, rem = line:match('^/(%d+) (.+)$') if not value then - return nil, "Malformed ID at start of line" + return nil, 'Malformed ID at start of line' end ranges.id = { start, value:len() + 1 } start = ranges.id[2] + 1 @@ -97,7 +97,7 @@ M.parse_line = function(adapter, line, column_defs) local start_len = string.len(rem) value, rem = columns.parse_col(adapter, assert(rem), def) if not rem then - return nil, string.format("Parsing %s failed", name) + return nil, string.format('Parsing %s failed', name) end ret[name] = value 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 local isdir name, isdir = parsedir(vim.trim(name)) - if name ~= "" then + if name ~= '' then ret.name = name end - ret._type = isdir and "directory" or "file" + ret._type = isdir and 'directory' or 'file' end local entry = cache.get_entry_by_id(ret.id) ranges.name = { start, start + string.len(rem) - 1 } @@ -122,20 +122,20 @@ M.parse_line = function(adapter, line, column_defs) -- Parse the symlink syntax local meta = entry[FIELD_META] local entry_type = entry[FIELD_TYPE] - if entry_type == "link" and meta and meta.link then - local name_pieces = vim.split(ret.name, " -> ", { plain = true }) + if entry_type == 'link' and meta and meta.link then + local name_pieces = vim.split(ret.name, ' -> ', { plain = true }) if #name_pieces ~= 2 then - ret.name = "" + ret.name = '' return { data = ret, ranges = ranges } end ranges.name = { start, start + string.len(name_pieces[1]) - 1 } ret.name = parsedir(vim.trim(name_pieces[1])) ret.link_target = name_pieces[2] - ret._type = "link" + ret._type = 'link' end -- 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] end @@ -187,7 +187,7 @@ M.parse = function(bufnr) name = name:lower() end 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 seen_names[name] = true end @@ -197,7 +197,7 @@ M.parse = function(bufnr) -- hack to be compatible with Lua 5.1 -- use return instead of goto (function() - if line:match("^/%d+") then + if line:match('^/%d+') then -- Parse the line for an existing entry local result, err = M.parse_line(adapter, line, column_defs) if not result or err then @@ -217,11 +217,11 @@ M.parse = function(bufnr) local err_message if not parsed_entry.name then - err_message = "No filename found" + err_message = 'No filename found' elseif not entry then - err_message = "Could not find existing entry (was the ID changed?)" - elseif parsed_entry.name:match("/") or parsed_entry.name:match(fs.sep) then - err_message = "Filename cannot contain path separator" + err_message = 'Could not find existing entry (was the ID changed?)' + elseif parsed_entry.name:match('/') or parsed_entry.name:match(fs.sep) then + err_message = 'Filename cannot contain path separator' end if err_message then table.insert(errors, { @@ -237,16 +237,16 @@ M.parse = function(bufnr) check_dupe(parsed_entry.name, i) local meta = entry[FIELD_META] 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, { - type = "new", + type = 'new', name = parsed_entry.name, - entry_type = "link", + entry_type = 'link', link = parsed_entry.link_target, }) elseif entry[FIELD_TYPE] ~= parsed_entry._type then table.insert(diffs, { - type = "new", + type = 'new', name = parsed_entry.name, entry_type = parsed_entry._type, }) @@ -255,7 +255,7 @@ M.parse = function(bufnr) end else table.insert(diffs, { - type = "new", + type = 'new', name = parsed_entry.name, entry_type = parsed_entry._type, id = parsed_entry.id, @@ -267,7 +267,7 @@ M.parse = function(bufnr) local col_name = util.split_config(col_def) if columns.compare(adapter, col_name, entry, parsed_entry[col_name]) then table.insert(diffs, { - type = "change", + type = 'change', name = parsed_entry.name, entry_type = entry[FIELD_TYPE], column = col_name, @@ -278,7 +278,7 @@ M.parse = function(bufnr) else -- Parse a new entry local name, isdir = parsedir(vim.trim(line)) - if vim.startswith(name, "/") then + if vim.startswith(name, '/') then table.insert(errors, { message = "Paths cannot start with '/'", lnum = i - 1, @@ -287,17 +287,17 @@ M.parse = function(bufnr) }) return end - if name ~= "" then - local link_pieces = vim.split(name, " -> ", { plain = true }) - local entry_type = isdir and "directory" or "file" + if name ~= '' then + local link_pieces = vim.split(name, ' -> ', { plain = true }) + local entry_type = isdir and 'directory' or 'file' local link if #link_pieces == 2 then - entry_type = "link" + entry_type = 'link' name, link = unpack(link_pieces) end check_dupe(name, i) table.insert(diffs, { - type = "new", + type = 'new', name = name, entry_type = entry_type, link = link, @@ -309,7 +309,7 @@ M.parse = function(bufnr) for name, child_id in pairs(original_entries) do table.insert(diffs, { - type = "delete", + type = 'delete', name = name, id = child_id, }) diff --git a/lua/oil/mutator/progress.lua b/lua/oil/mutator/progress.lua index 057f0a0..81b6b5c 100644 --- a/lua/oil/mutator/progress.lua +++ b/lua/oil/mutator/progress.lua @@ -1,17 +1,17 @@ -local columns = require("oil.columns") -local config = require("oil.config") -local layout = require("oil.layout") -local loading = require("oil.loading") -local util = require("oil.util") +local columns = require('oil.columns') +local config = require('oil.config') +local layout = require('oil.layout') +local loading = require('oil.loading') +local util = require('oil.util') local Progress = {} local FPS = 20 function Progress.new() return setmetatable({ - lines = { "", "" }, - count = "", - spinner = "", + lines = { '', '' }, + count = '', + spinner = '', bufnr = nil, winid = nil, min_bufnr = nil, @@ -40,18 +40,18 @@ function Progress:show(opts) return end local bufnr = vim.api.nvim_create_buf(false, true) - vim.bo[bufnr].bufhidden = "wipe" + vim.bo[bufnr].bufhidden = 'wipe' self.bufnr = bufnr self.cancel = opts.cancel or self.cancel local loading_iter = loading.get_bar_iter() - local spinner = loading.get_iter("dots") + local spinner = loading.get_iter('dots') if not self.timer then self.timer = vim.loop.new_timer() self.timer:start( 0, math.floor(1000 / FPS), 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:_render() end) @@ -59,22 +59,22 @@ function Progress:show(opts) end local width, height = layout.calculate_dims(120, 10, config.progress) self.winid = vim.api.nvim_open_win(self.bufnr, true, { - relative = "editor", + relative = 'editor', width = width, height = height, row = math.floor((layout.get_editor_height() - height) / 2), col = math.floor((layout.get_editor_width() - width) / 2), zindex = 152, -- render on top of the floating window title - style = "minimal", + style = 'minimal', 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 - 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 table.insert( self.autocmds, - vim.api.nvim_create_autocmd("VimResized", { + vim.api.nvim_create_autocmd('VimResized', { callback = function() self:_reposition() end, @@ -82,7 +82,7 @@ function Progress:show(opts) ) table.insert( self.autocmds, - vim.api.nvim_create_autocmd("WinLeave", { + vim.api.nvim_create_autocmd('WinLeave', { callback = function() self:minimize() end, @@ -94,17 +94,17 @@ function Progress:show(opts) vim.api.nvim_win_close(self.winid, true) 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", "m", minimize, { buffer = self.bufnr, nowait = true }) - vim.keymap.set("n", "M", minimize, { 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 }) end function Progress:restore() if self.closing then return elseif not self:is_minimized() then - error("Cannot restore progress window: not minimized") + error('Cannot restore progress window: not minimized') end self:_cleanup_minimized_win() self:show() @@ -115,14 +115,14 @@ function Progress:_render() util.render_text( self.bufnr, self.lines, - { winid = self.winid, actions = { "[M]inimize", "[C]ancel" } } + { winid = self.winid, actions = { '[M]inimize', '[C]ancel' } } ) end if self.min_bufnr and vim.api.nvim_buf_is_valid(self.min_bufnr) then util.render_text( self.min_bufnr, - { string.format("%sOil: %s", self.spinner, self.count) }, - { winid = self.min_winid, h_align = "left" } + { string.format('%sOil: %s', self.spinner, self.count) }, + { winid = self.min_winid, h_align = 'left' } ) end end @@ -136,7 +136,7 @@ function Progress:_reposition() end local width, height = layout.calculate_dims(min_width, 10, config.progress) vim.api.nvim_win_set_config(self.winid, { - relative = "editor", + relative = 'editor', width = width, height = height, row = math.floor((layout.get_editor_height() - height) / 2), @@ -174,22 +174,22 @@ function Progress:minimize() end self:_cleanup_main_win() 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, { - relative = "editor", + relative = 'editor', width = 16, height = 1, - anchor = "SE", + anchor = 'SE', row = layout.get_editor_height(), col = layout.get_editor_width(), zindex = 152, -- render on top of the floating window title - style = "minimal", + style = 'minimal', border = config.progress.minimized_border, }) self.min_bufnr = bufnr self.min_winid = winid self:_render() - vim.notify_once("Restore progress window with :Oil --progress") + vim.notify_once('Restore progress window with :Oil --progress') end ---@param action oil.Action @@ -198,14 +198,14 @@ end function Progress:set_action(action, idx, total) local adapter = util.get_adapter_for_action(action) local change_line - if action.type == "change" then + if action.type == 'change' then ---@cast action oil.ChangeAction change_line = columns.render_change_action(adapter, action) else change_line = adapter.render_action(action) end self.lines[1] = change_line - self.count = string.format("%d/%d", idx, total) + self.count = string.format('%d/%d', idx, total) self:_reposition() self:_render() end diff --git a/lua/oil/mutator/trie.lua b/lua/oil/mutator/trie.lua index 7bac161..95ab979 100644 --- a/lua/oil/mutator/trie.lua +++ b/lua/oil/mutator/trie.lua @@ -1,4 +1,4 @@ -local util = require("oil.util") +local util = require('oil.util') ---@class (exact) oil.Trie ---@field new fun(): oil.Trie @@ -20,7 +20,7 @@ end function Trie:_url_to_path_pieces(url) local scheme, path = util.parse_url(url) assert(path) - local pieces = vim.split(path, "/") + local pieces = vim.split(path, '/') table.insert(pieces, 1, scheme) return pieces end @@ -75,7 +75,7 @@ function Trie:remove(path_pieces, value) return end end - error("Value not present in trie: " .. vim.inspect(value)) + error('Value not present in trie: ' .. vim.inspect(value)) end ---Add the first action that affects a parent path of the url diff --git a/lua/oil/pathutil.lua b/lua/oil/pathutil.lua index b030389..cfd4d82 100644 --- a/lua/oil/pathutil.lua +++ b/lua/oil/pathutil.lua @@ -3,26 +3,26 @@ local M = {} ---@param path string ---@return string M.parent = function(path) - if path == "/" then - return "/" - elseif path == "" then - return "" - elseif vim.endswith(path, "/") then - return path:match("^(.*/)[^/]*/$") or "" + if path == '/' then + return '/' + elseif path == '' then + return '' + elseif vim.endswith(path, '/') then + return path:match('^(.*/)[^/]*/$') or '' else - return path:match("^(.*/)[^/]*$") or "" + return path:match('^(.*/)[^/]*$') or '' end end ---@param path string ---@return nil|string M.basename = function(path) - if path == "/" or path == "" then + if path == '/' or path == '' then return - elseif vim.endswith(path, "/") then - return path:match("^.*/([^/]*)/$") + elseif vim.endswith(path, '/') then + return path:match('^.*/([^/]*)/$') else - return path:match("^.*/([^/]*)$") + return path:match('^.*/([^/]*)$') end end diff --git a/lua/oil/ringbuf.lua b/lua/oil/ringbuf.lua index 0f09987..66cd912 100644 --- a/lua/oil/ringbuf.lua +++ b/lua/oil/ringbuf.lua @@ -23,11 +23,11 @@ end ---@return string function Ringbuf:as_str() - local postfix = "" + local postfix = '' for i = 1, self.tail, 1 do postfix = postfix .. self.buf[i] end - local prefix = "" + local prefix = '' for i = self.tail + 1, #self.buf, 1 do prefix = prefix .. self.buf[i] end diff --git a/lua/oil/shell.lua b/lua/oil/shell.lua index b04b27b..9439846 100644 --- a/lua/oil/shell.lua +++ b/lua/oil/shell.lua @@ -9,7 +9,7 @@ M.run = function(cmd, opts, callback) local stderr = {} local jid = vim.fn.jobstart( cmd, - vim.tbl_deep_extend("keep", opts, { + vim.tbl_deep_extend('keep', opts, { stdout_buffered = true, stderr_buffered = true, on_stdout = function(j, output) @@ -22,19 +22,19 @@ M.run = function(cmd, opts, callback) if code == 0 then callback(nil, stdout) else - local err = table.concat(stderr, "\n") - if err == "" then - err = "Unknown error" + local err = table.concat(stderr, '\n') + if err == '' then + err = 'Unknown error' 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)) end end), }) ) local exe - if type(cmd) == "string" then - exe = vim.split(cmd, "%s+")[1] + if type(cmd) == 'string' then + exe = vim.split(cmd, '%s+')[1] else exe = cmd[1] end diff --git a/lua/oil/util.lua b/lua/oil/util.lua index 37e73cc..d9bca22 100644 --- a/lua/oil/util.lua +++ b/lua/oil/util.lua @@ -1,5 +1,5 @@ -local config = require("oil.config") -local constants = require("oil.constants") +local config = require('oil.config') +local constants = require('oil.constants') local M = {} @@ -14,7 +14,7 @@ local FIELD_META = constants.FIELD_META ---@return nil|string ---@return nil|string M.parse_url = function(url) - return url:match("^(.*://)(.*)$") + return url:match('^(.*://)(.*)$') end ---Escapes a filename for use in :edit @@ -26,51 +26,51 @@ M.escape_filename = function(filename) end local _url_escape_to_char = { - ["20"] = " ", - ["22"] = "“", - ["23"] = "#", - ["24"] = "$", - ["25"] = "%", - ["26"] = "&", - ["27"] = "‘", - ["2B"] = "+", - ["2C"] = ",", - ["2F"] = "/", - ["3A"] = ":", - ["3B"] = ";", - ["3C"] = "<", - ["3D"] = "=", - ["3E"] = ">", - ["3F"] = "?", - ["40"] = "@", - ["5B"] = "[", - ["5C"] = "\\", - ["5D"] = "]", - ["5E"] = "^", - ["60"] = "`", - ["7B"] = "{", - ["7C"] = "|", - ["7D"] = "}", - ["7E"] = "~", + ['20'] = ' ', + ['22'] = '“', + ['23'] = '#', + ['24'] = '$', + ['25'] = '%', + ['26'] = '&', + ['27'] = '‘', + ['2B'] = '+', + ['2C'] = ',', + ['2F'] = '/', + ['3A'] = ':', + ['3B'] = ';', + ['3C'] = '<', + ['3D'] = '=', + ['3E'] = '>', + ['3F'] = '?', + ['40'] = '@', + ['5B'] = '[', + ['5C'] = '\\', + ['5D'] = ']', + ['5E'] = '^', + ['60'] = '`', + ['7B'] = '{', + ['7C'] = '|', + ['7D'] = '}', + ['7E'] = '~', } local _char_to_url_escape = {} for k, v in pairs(_url_escape_to_char) do - _char_to_url_escape[v] = "%" .. k + _char_to_url_escape[v] = '%' .. k end -- TODO this uri escape handling is very incomplete ---@param string string ---@return string M.url_escape = function(string) - return (string:gsub(".", _char_to_url_escape)) + return (string:gsub('.', _char_to_url_escape)) end ---@param string string ---@return string M.url_unescape = function(string) return ( - string:gsub("%%([0-9A-Fa-f][0-9A-Fa-f])", function(seq) - return _url_escape_to_char[seq:upper()] or ("%" .. seq) + string:gsub('%%([0-9A-Fa-f][0-9A-Fa-f])', function(seq) + return _url_escape_to_char[seq:upper()] or ('%' .. seq) end) ) end @@ -105,14 +105,14 @@ M.pad_align = function(text, width, align) return text, 0 end - if align == "right" then - return string.rep(" ", total_pad) .. text, total_pad - elseif align == "center" then + if align == 'right' then + return string.rep(' ', total_pad) .. text, total_pad + elseif align == 'center' then local left_pad = math.floor(total_pad / 2) 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 - return text .. string.rep(" ", total_pad), 0 + return text .. string.rep(' ', total_pad), 0 end end @@ -150,7 +150,7 @@ end ---@param dest_buf_name string ---@return boolean True if the buffer was replaced instead of renamed 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) if not vim.api.nvim_buf_is_loaded(src_bufnr) then 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. if not vim.loop.fs_stat(dest_buf_name) then ---@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 local ok = pcall(vim.api.nvim_buf_set_name, src_bufnr, dest_buf_name) if ok then @@ -173,7 +173,7 @@ M.rename_buffer = function(src_bufnr, dest_buf_name) -- where Neovim doesn't allow buffer modifications. pcall(vim.api.nvim_buf_delete, vim.fn.bufadd(bufname), {}) if altbuf and vim.api.nvim_buf_is_valid(altbuf) then - vim.fn.setreg("#", altbuf) + vim.fn.setreg('#', altbuf) end return false @@ -229,6 +229,7 @@ end M.cb_collect = function(count, cb) return function(err) if err then + -- selene: allow(mismatched_arg_count) cb(err) cb = function() end else @@ -243,9 +244,9 @@ end ---@param url string ---@return string[] 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) - if config.adapters[scheme] == "files" then + if config.adapters[scheme] == 'files' then assert(path) return { fs.posix_to_os_path(path) } end @@ -258,7 +259,7 @@ end M.update_moved_buffers = function(entry_type, src_url, dest_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] - if entry_type ~= "directory" then + if entry_type ~= 'directory' then for _, src_buf_name in ipairs(src_buf_names) do M.rename_buffer(src_buf_name, dest_buf_name) end @@ -272,13 +273,13 @@ M.update_moved_buffers = function(entry_type, src_url, dest_url) if vim.startswith(bufname, src_url) then -- Handle oil directory buffers 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 local scheme = M.parse_url(bufname) -- If the buffer is a local file, make sure we're using the absolute path if not scheme then - bufname = vim.fn.fnamemodify(bufname, ":p") + bufname = vim.fn.fnamemodify(bufname, ':p') end for _, src_buf_name in ipairs(src_buf_names) do @@ -296,13 +297,13 @@ end ---@return string ---@return table|nil 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 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 - name_or_config[1] = name_or_config["1"] - name_or_config["1"] = nil + name_or_config[1] = name_or_config['1'] + name_or_config['1'] = nil end return name_or_config[1], name_or_config end @@ -324,7 +325,7 @@ M.render_table = function(lines, col_width, col_align) local pieces = {} for i, chunk in ipairs(cols) do local text, hl - if type(chunk) == "table" then + if type(chunk) == 'table' then text = chunk[1] hl = chunk[2] else @@ -333,11 +334,11 @@ M.render_table = function(lines, col_width, col_align) local unpadded_len = text:len() 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) 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 }[] -- Notice that col_start and col_end are relative position inside -- that col, so we need to add the offset to them @@ -355,7 +356,7 @@ M.render_table = function(lines, col_width, col_align) end col = col + text:len() + 1 end - table.insert(str_lines, table.concat(pieces, " ")) + table.insert(str_lines, table.concat(pieces, ' ')) end return str_lines, highlights end @@ -363,7 +364,7 @@ end ---@param bufnr integer ---@param highlights any[][] List of highlights {group, lnum, col_start, col_end} 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) for _, hl in ipairs(highlights) do local group, line, col_start, col_end = unpack(hl) @@ -379,12 +380,12 @@ end ---@param os_slash? boolean use os filesystem slash instead of posix slash ---@return string M.addslash = function(path, os_slash) - local slash = "/" - if os_slash and require("oil.fs").is_windows then - slash = "\\" + local slash = '/' + if os_slash and require('oil.fs').is_windows then + slash = '\\' end - local endslash = path:match(slash .. "$") + local endslash = path:match(slash .. '$') if not endslash then return path .. slash else @@ -395,7 +396,7 @@ end ---@param winid nil|integer ---@return boolean 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 ---Recalculate the window title for the current buffer @@ -410,10 +411,10 @@ M.get_title = function(winid) local title = vim.api.nvim_buf_get_name(src_buf) local scheme, path = M.parse_url(title) - if config.adapters[scheme] == "files" then + if config.adapters[scheme] == 'files' then assert(path) - local fs = require("oil.fs") - title = vim.fn.fnamemodify(fs.posix_to_os_path(path), ":~") + local fs = require('oil.fs') + title = vim.fn.fnamemodify(fs.posix_to_os_path(path), ':~') end return title end @@ -421,7 +422,7 @@ end local winid_map = {} M.add_title_to_win = function(winid, opts) 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 return end @@ -438,18 +439,18 @@ M.add_title_to_win = function(winid, opts) else bufnr = vim.api.nvim_create_buf(false, true) 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) - elseif opts.align == "right" then + elseif opts.align == 'right' then col = vim.api.nvim_win_get_width(winid) - 1 - width - elseif opts.align ~= "left" then + elseif opts.align ~= 'left' then vim.notify( string.format("Unknown oil window title alignment: '%s'", opts.align), vim.log.levels.ERROR ) end title_winid = vim.api.nvim_open_win(bufnr, false, { - relative = "win", + relative = 'win', win = winid, width = width, height = 1, @@ -457,20 +458,20 @@ M.add_title_to_win = function(winid, opts) col = col, focusable = false, zindex = 151, - style = "minimal", + style = 'minimal', noautocmd = true, }) winid_map[winid] = title_winid vim.api.nvim_set_option_value( - "winblend", + '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", { - desc = "Update oil floating window title when buffer changes", - pattern = "*", + local update_autocmd = vim.api.nvim_create_autocmd('BufWinEnter', { + desc = 'Update oil floating window title when buffer changes', + pattern = '*', callback = function(params) local winbuf = params.buf if vim.api.nvim_win_get_buf(winid) ~= winbuf then @@ -479,17 +480,17 @@ M.add_title_to_win = function(winid, opts) local new_title = M.get_title(winid) local new_width = 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.api.nvim_win_set_width(title_winid, new_width) 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) - elseif opts.align == "right" then + elseif opts.align == 'right' then new_col = vim.api.nvim_win_get_width(winid) - 1 - new_width end vim.api.nvim_win_set_config(title_winid, { - relative = "win", + relative = 'win', win = winid, row = -1, col = new_col, @@ -498,8 +499,8 @@ M.add_title_to_win = function(winid, opts) }) end, }) - vim.api.nvim_create_autocmd("WinClosed", { - desc = "Close oil floating window title when floating window closes", + vim.api.nvim_create_autocmd('WinClosed', { + desc = 'Close oil floating window title when floating window closes', pattern = tostring(winid), callback = function() if title_winid and vim.api.nvim_win_is_valid(title_winid) then @@ -512,12 +513,12 @@ M.add_title_to_win = function(winid, opts) nested = true, }) 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.api.nvim_set_option_value( - "winhighlight", - "Normal:FloatTitle,NormalFloat:FloatTitle", - { scope = "local", win = title_winid } + 'winhighlight', + 'Normal:FloatTitle,NormalFloat:FloatTitle', + { scope = 'local', win = title_winid } ) end @@ -542,7 +543,7 @@ M.get_adapter_for_action = function(action) else error( 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.dest_url ) @@ -559,12 +560,12 @@ end ---@return string ---@return integer 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) - return string.rep(" ", padding) .. str, padding - elseif align == "right" then + return string.rep(' ', padding) .. str, padding + elseif align == 'right' then local padding = width - vim.api.nvim_strwidth(str) - return string.rep(" ", padding) .. str, padding + return string.rep(' ', padding) .. str, padding else return str, 0 end @@ -578,15 +579,15 @@ end --- actions nil|string[] --- winid nil|integer M.render_text = function(bufnr, text, opts) - opts = vim.tbl_deep_extend("keep", opts or {}, { - h_align = "center", - v_align = "center", + opts = vim.tbl_deep_extend('keep', opts or {}, { + h_align = 'center', + v_align = 'center', }) ---@cast opts -nil if not vim.api.nvim_buf_is_valid(bufnr) then return end - if type(text) == "string" then + if type(text) == 'string' then text = { text } end local height = 40 @@ -608,17 +609,17 @@ M.render_text = function(bufnr, text, opts) local lines = {} -- 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 - table.insert(lines, "") + table.insert(lines, '') end - elseif opts.v_align == "bottom" then + elseif opts.v_align == 'bottom' then local num_lines = height if opts.actions then num_lines = num_lines - 2 end while #lines + #text < num_lines do - table.insert(lines, "") + table.insert(lines, '') end end @@ -632,12 +633,12 @@ M.render_text = function(bufnr, text, opts) local highlights = {} if opts.actions then while #lines < height - 1 do - table.insert(lines, "") + table.insert(lines, '') 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 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 end table.insert(lines, last_line) @@ -656,10 +657,10 @@ end M.run_in_fullscreen_win = function(bufnr, callback) if not bufnr then bufnr = vim.api.nvim_create_buf(false, true) - vim.bo[bufnr].bufhidden = "wipe" + vim.bo[bufnr].bufhidden = 'wipe' end local winid = vim.api.nvim_open_win(bufnr, false, { - relative = "editor", + relative = 'editor', width = vim.o.columns, height = vim.o.lines, row = 0, @@ -667,7 +668,7 @@ M.run_in_fullscreen_win = function(bufnr, callback) noautocmd = true, }) 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() vim.cmd.close({ count = winnr, mods = { noautocmd = true, emsg_silent = true } }) end @@ -676,9 +677,9 @@ end ---@return boolean M.is_oil_bufnr = function(bufnr) local filetype = vim.bo[bufnr].filetype - if filetype == "oil" then + if filetype == 'oil' then return true - elseif filetype ~= "" then + elseif filetype ~= '' then -- If the filetype is set and is NOT "oil", then it's not an oil buffer return false end @@ -693,10 +694,10 @@ M.hack_around_termopen_autocmd = function(prev_mode) vim.defer_fn(function() local new_mode = vim.api.nvim_get_mode().mode if new_mode ~= prev_mode then - if string.find(new_mode, "i") == 1 then - vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, true, true), "n", false) - if string.find(prev_mode, "v") == 1 or string.find(prev_mode, "V") == 1 then - vim.cmd.normal({ bang = true, args = { "gv" } }) + if string.find(new_mode, 'i') == 1 then + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('', true, true, true), 'n', false) + if string.find(prev_mode, 'v') == 1 or string.find(prev_mode, 'V') == 1 then + vim.cmd.normal({ bang = true, args = { 'gv' } }) end end end @@ -712,7 +713,7 @@ M.get_preview_win = function(opts) if vim.api.nvim_win_is_valid(winid) 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 return winid end @@ -721,13 +722,13 @@ end ---@return fun() restore Function that restores the cursor 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 - vim.go.guicursor = "a:OilPreviewCursor/OilPreviewCursor" + vim.go.guicursor = 'a:OilPreviewCursor/OilPreviewCursor' return function() -- HACK: see https://github.com/neovim/neovim/issues/21018 - vim.go.guicursor = "a:" + vim.go.guicursor = 'a:' vim.cmd.redrawstatus() vim.go.guicursor = original_guicursor end @@ -762,7 +763,7 @@ end ---@param opts {columns?: string[], no_cache?: boolean} ---@param callback fun(err: nil|string, entries: nil|oil.InternalEntry[]) 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 local entries = cache.list_url(url) if not vim.tbl_isempty(entries) then @@ -790,23 +791,23 @@ end ---based on the provided options. ---@param opts {target?: "qflist"|"loclist", action?: "r"|"a", only_matching_search?: boolean} M.send_to_quickfix = function(opts) - if type(opts) ~= "table" then + if type(opts) ~= 'table' then opts = {} end - local oil = require("oil") + local oil = require('oil') local dir = oil.get_current_dir() - if type(dir) ~= "string" then + if type(dir) ~= 'string' then return end local range = M.get_visual_range() if not range then - range = { start_lnum = 1, end_lnum = vim.fn.line("$") } + range = { start_lnum = 1, end_lnum = vim.fn.line('$') } end local match_all = not opts.only_matching_search local qf_entries = {} for i = range.start_lnum, range.end_lnum do 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 = { filename = dir .. entry.name, lnum = 1, @@ -817,26 +818,26 @@ M.send_to_quickfix = function(opts) end end 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 end - vim.api.nvim_exec_autocmds("QuickFixCmdPre", {}) - local qf_title = "oil files" - local action = opts.action == "a" and "a" or "r" - if opts.target == "loclist" then + vim.api.nvim_exec_autocmds('QuickFixCmdPre', {}) + local qf_title = 'oil files' + local action = opts.action == 'a' and 'a' or 'r' + if opts.target == 'loclist' then vim.fn.setloclist(0, {}, action, { title = qf_title, items = qf_entries }) vim.cmd.lopen() else vim.fn.setqflist({}, action, { title = qf_title, items = qf_entries }) vim.cmd.copen() end - vim.api.nvim_exec_autocmds("QuickFixCmdPost", {}) + vim.api.nvim_exec_autocmds('QuickFixCmdPost', {}) end ---@return boolean M.is_visual_mode = function() local mode = vim.api.nvim_get_mode().mode - return mode:match("^[vV]") ~= nil + return mode:match('^[vV]') ~= nil end ---Get the current visual selection range. If not in visual mode, return nil. @@ -847,7 +848,7 @@ M.get_visual_range = function() end -- This is the best way to get the visual selection at the moment -- 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()) if start_lnum > end_lnum then start_lnum, end_lnum = end_lnum, start_lnum @@ -863,7 +864,7 @@ M.is_matching = function(entry) if search_highlighting_is_off then return true end - local pattern = vim.fn.getreg("/") + local pattern = vim.fn.getreg('/') local position_of_match = vim.fn.match(entry.name, pattern) return position_of_match ~= -1 end @@ -877,8 +878,8 @@ M.run_after_load = function(bufnr, callback) if vim.b[bufnr].oil_ready then callback() else - vim.api.nvim_create_autocmd("User", { - pattern = "OilEnter", + vim.api.nvim_create_autocmd('User', { + pattern = 'OilEnter', callback = function(args) if args.data.buf == bufnr then vim.api.nvim_buf_call(bufnr, callback) @@ -892,12 +893,12 @@ end ---@param entry oil.Entry ---@return boolean M.is_directory = function(entry) - local is_directory = entry.type == "directory" + local is_directory = entry.type == 'directory' or ( - entry.type == "link" + entry.type == 'link' and entry.meta and entry.meta.link_stat - and entry.meta.link_stat.type == "directory" + and entry.meta.link_stat.type == 'directory' ) return is_directory == true end @@ -907,7 +908,7 @@ end ---@param entry oil.Entry ---@param callback fun(normalized_url: string) 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 scheme, dir = M.parse_url(bufname) @@ -916,10 +917,10 @@ M.get_edit_path = function(bufnr, entry, callback) local url = scheme .. dir .. entry.name if M.is_directory(entry) then - url = url .. "/" + url = url .. '/' end - if entry.name == ".." then + if entry.name == '..' then callback(scheme .. pathutil.parent(dir)) elseif adapter.get_entry_path then adapter.get_entry_path(url, entry, callback) @@ -932,33 +933,34 @@ end ---@return (oil.IconProvider)? M.get_icon_provider = function() -- 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 - if _G.MiniIcons then -- `_G.MiniIcons` is a better check to see if the module is setup + if _G.MiniIcons then return function(type, name, conf, ft) if ft then - return mini_icons.get("filetype", ft) + return mini_icons.get('filetype', ft) 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 -- fallback to `nvim-web-devicons` - local has_devicons, devicons = pcall(require, "nvim-web-devicons") + local has_devicons, devicons = pcall(require, 'nvim-web-devicons') if has_devicons then return function(type, name, conf, ft) - if type == "directory" then - return conf and conf.directory or "", "OilDirIcon" + 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 + 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 "") + hl = hl or 'OilFileIcon' + icon = icon or (conf and conf.default_file or '') return icon, hl end end @@ -975,24 +977,25 @@ M.read_file_to_scratch_buffer = function(path, preview_method) return end - vim.bo[bufnr].bufhidden = "wipe" - vim.bo[bufnr].buftype = "nofile" + vim.bo[bufnr].bufhidden = 'wipe' + vim.bo[bufnr].buftype = 'nofile' local has_lines, read_res - if preview_method == "fast_scratch" then - has_lines, read_res = pcall(vim.fn.readfile, path, "", vim.o.lines) + if preview_method == 'fast_scratch' then + has_lines, read_res = pcall(vim.fn.readfile, path, '', vim.o.lines) else has_lines, read_res = pcall(vim.fn.readfile, path) 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) if not ok then return end 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) + -- selene: allow(empty_if) if not pcall(vim.treesitter.start, bufnr, lang) then vim.bo[bufnr].syntax = ft else @@ -1000,8 +1003,8 @@ M.read_file_to_scratch_buffer = function(path, preview_method) end -- Replace the scratch buffer with a real buffer if we enter it - vim.api.nvim_create_autocmd("BufEnter", { - desc = "oil.nvim replace scratch buffer with real buffer", + vim.api.nvim_create_autocmd('BufEnter', { + desc = 'oil.nvim replace scratch buffer with real buffer', buffer = bufnr, callback = function() local winid = vim.api.nvim_get_current_win() @@ -1013,7 +1016,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 -- preview if vim.wo.previewwindow then - vim.bo.bufhidden = "wipe" + vim.bo.bufhidden = 'wipe' vim.b.oil_preview_buffer = true end end @@ -1030,7 +1033,7 @@ local _regcache = {} ---@return boolean M.file_matches_bufreadcmd = function(filename) local autocmds = vim.api.nvim_get_autocmds({ - event = "BufReadCmd", + event = 'BufReadCmd', }) for _, au in ipairs(autocmds) do local pat = _regcache[au.pattern] diff --git a/lua/oil/view.lua b/lua/oil/view.lua index bc21dfb..e477009 100644 --- a/lua/oil/view.lua +++ b/lua/oil/view.lua @@ -1,12 +1,12 @@ local uv = vim.uv or vim.loop -local cache = require("oil.cache") -local columns = require("oil.columns") -local config = require("oil.config") -local constants = require("oil.constants") -local fs = require("oil.fs") -local keymap_util = require("oil.keymap_util") -local loading = require("oil.loading") -local util = require("oil.util") +local cache = require('oil.cache') +local columns = require('oil.columns') +local config = require('oil.config') +local constants = require('oil.constants') +local fs = require('oil.fs') +local keymap_util = require('oil.keymap_util') +local loading = require('oil.loading') +local util = require('oil.util') local M = {} local FIELD_ID = constants.FIELD_ID @@ -41,7 +41,7 @@ end ---Set the cursor to the last_cursor_entry if one exists M.maybe_set_cursor = function() - local oil = require("oil") + local oil = require('oil') local bufname = vim.api.nvim_buf_get_name(0) local entry_name = last_cursor_entry[bufname] if not entry_name then @@ -52,7 +52,7 @@ M.maybe_set_cursor = function() local entry = oil.get_entry_on_line(0, lnum) if entry and entry.name == entry_name then 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) vim.api.nvim_win_set_cursor(0, { lnum, col - 1 }) M.set_last_cursor(bufname, nil) @@ -78,14 +78,14 @@ local function are_any_modified() end local function is_unix_executable(entry) - if entry[FIELD_TYPE] == "directory" then + if entry[FIELD_TYPE] == 'directory' then return false end local meta = entry[FIELD_META] if not meta or not meta.stat then return false end - if meta.stat.type == "directory" then + if meta.stat.type == 'directory' then return false end @@ -98,7 +98,7 @@ end M.toggle_hidden = function() local any_modified = are_any_modified() 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 config.view_options.show_hidden = not config.view_options.show_hidden M.rerender_all_oil_buffers({ refetch = false }) @@ -109,7 +109,7 @@ end M.set_is_hidden_file = function(is_hidden_file) local any_modified = are_any_modified() 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 config.view_options.is_hidden_file = is_hidden_file M.rerender_all_oil_buffers({ refetch = false }) @@ -119,7 +119,7 @@ end M.set_columns = function(cols) local any_modified = are_any_modified() 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 config.columns = cols -- 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) local any_modified = are_any_modified() 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 config.view_options.sort = new_sort -- 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() -- 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 - 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 if vim.wo[winid].previewwindow then -- apply preview window options last 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 @@ -251,7 +251,7 @@ M.delete_hidden_buffers = function() not visible_buffers or not hidden_buffers or not vim.tbl_isempty(visible_buffers) - or vim.fn.win_gettype() == "command" + or vim.fn.win_gettype() == 'command' then return end @@ -283,15 +283,15 @@ end --- @param cur integer[] --- @return integer[] | nil 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 column_defs = columns.get_supported_columns(adapter) local result = parser.parse_line(adapter, line, column_defs) if result and result.ranges then local min_col - if mode == "editable" then + if mode == 'editable' then min_col = get_first_mutable_column_col(adapter, result.ranges) - elseif mode == "name" then + elseif mode == 'name' then min_col = result.ranges.name[1] else error(string.format('Unexpected value "%s" for option constrain_cursor', mode)) @@ -318,7 +318,7 @@ local function constrain_cursor(bufnr, mode) return end - local mc = package.loaded["multicursor-nvim"] + local mc = package.loaded['multicursor-nvim'] if mc then mc.onSafeState(function() 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 return end - local parser = require("oil.mutator.parser") + local parser = require('oil.mutator.parser') local adapter = util.get_adapter(bufnr, true) - if not adapter or adapter.name ~= "trash" then + if not adapter or adapter.name ~= 'trash' then return end 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 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) local column_defs = columns.get_supported_columns(adapter) 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, { virt_text = { { - "➜ " .. fs.shorten_path(trash_info.original_path, os_path), - "OilTrashSourcePath", + '➜ ' .. fs.shorten_path(trash_info.original_path, os_path), + 'OilTrashSourcePath', }, }, }) @@ -387,13 +387,13 @@ M.initialize = function(bufnr) end vim.api.nvim_clear_autocmds({ buffer = bufnr, - group = "Oil", + group = 'Oil', }) - vim.bo[bufnr].buftype = "acwrite" + vim.bo[bufnr].buftype = 'acwrite' vim.bo[bufnr].readonly = false vim.bo[bufnr].swapfile = false - vim.bo[bufnr].syntax = "oil" - vim.bo[bufnr].filetype = "oil" + vim.bo[bufnr].syntax = 'oil' + vim.bo[bufnr].filetype = 'oil' vim.b[bufnr].EditorConfig_disable = 1 session[bufnr] = session[bufnr] or {} for k, v in pairs(config.buf_options) do @@ -401,9 +401,9 @@ M.initialize = function(bufnr) end vim.api.nvim_buf_call(bufnr, M.set_win_options) - vim.api.nvim_create_autocmd("BufHidden", { - desc = "Delete oil buffers when no longer in use", - group = "Oil", + vim.api.nvim_create_autocmd('BufHidden', { + desc = 'Delete oil buffers when no longer in use', + group = 'Oil', nested = true, buffer = bufnr, callback = function() @@ -413,7 +413,7 @@ M.initialize = function(bufnr) -- Only delete oil buffers if none of them are visible if visible_buffers and vim.tbl_isempty(visible_buffers) then -- 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 vim.defer_fn(function() M.delete_hidden_buffers() @@ -426,8 +426,8 @@ M.initialize = function(bufnr) end, 100) end, }) - vim.api.nvim_create_autocmd("BufUnload", { - group = "Oil", + vim.api.nvim_create_autocmd('BufUnload', { + group = 'Oil', nested = true, once = true, buffer = bufnr, @@ -439,8 +439,8 @@ M.initialize = function(bufnr) end end, }) - vim.api.nvim_create_autocmd("BufEnter", { - group = "Oil", + vim.api.nvim_create_autocmd('BufEnter', { + group = 'Oil', buffer = bufnr, callback = function(args) local opts = vim.b[args.buf].oil_dirty @@ -451,9 +451,9 @@ M.initialize = function(bufnr) end, }) local timer - vim.api.nvim_create_autocmd("InsertEnter", { - desc = "Constrain oil cursor position", - group = "Oil", + vim.api.nvim_create_autocmd('InsertEnter', { + desc = 'Constrain oil cursor position', + group = 'Oil', buffer = bufnr, callback = function() -- 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) end, }) - vim.api.nvim_create_autocmd({ "CursorMoved", "ModeChanged" }, { - desc = "Update oil preview window", - group = "Oil", + vim.api.nvim_create_autocmd({ 'CursorMoved', 'ModeChanged' }, { + desc = 'Update oil preview window', + group = 'Oil', buffer = bufnr, callback = function() - local oil = require("oil") + local oil = require('oil') if vim.wo.previewwindow then return end @@ -513,7 +513,7 @@ M.initialize = function(bufnr) -- Set up a watcher that will refresh the directory if adapter - and adapter.name == "files" + and adapter.name == 'files' and config.watch_for_changes and not session[bufnr].fs_event then @@ -532,7 +532,7 @@ M.initialize = function(bufnr) fs_event:stop() return 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 return end @@ -553,11 +553,11 @@ M.initialize = function(bufnr) end -- 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 pending = false - vim.api.nvim_create_autocmd("TextChanged", { - desc = "Update oil virtual text of original path", + vim.api.nvim_create_autocmd('TextChanged', { + desc = 'Update oil virtual text of original path', buffer = bufnr, callback = function() -- 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) if err then 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 ) else vim.b[bufnr].oil_ready = true vim.api.nvim_exec_autocmds( - "User", - { pattern = "OilEnter", modeline = false, data = { buf = bufnr } } + 'User', + { pattern = 'OilEnter', modeline = false, data = { buf = bufnr } } ) end end) @@ -606,12 +606,12 @@ local function get_sort_function(adapter, num_entries) -- If empty, default to type + name sorting if vim.tbl_isempty(sort_config) then - sort_config = { { "type", "asc" }, { "name", "asc" } } + sort_config = { { 'type', 'asc' }, { 'name', 'asc' } } end for _, sort_pair in ipairs(sort_config) do local col_name, order = unpack(sort_pair) - if order ~= "asc" and order ~= "desc" then + if order ~= 'asc' and order ~= 'desc' then vim.notify_once( string.format( "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 b_val = get_sort_value(b) if a_val ~= b_val then - if order == "desc" then + if order == 'desc' then return a_val > b_val else return a_val < b_val @@ -663,7 +663,7 @@ local function render_buffer(bufnr, opts) return false end 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_first = false, }) @@ -693,10 +693,10 @@ local function render_buffer(bufnr, opts) for i, col_def in ipairs(column_defs) do col_width[i + 1] = 1 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 - local parent_entry = { 0, "..", "directory" } + local parent_entry = { 0, '..', 'directory' } if M.should_display(bufnr, parent_entry) then local cols = M.format_entry_cols(parent_entry, column_defs, col_width, adapter, true, bufnr) table.insert(line_table, cols) @@ -732,7 +732,7 @@ local function render_buffer(bufnr, opts) if jump_idx then local lnum = jump_idx 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) if id then local entry = cache.get_entry_by_id(id) @@ -745,7 +745,7 @@ local function render_buffer(bufnr, opts) end end - constrain_cursor(bufnr, "name") + constrain_cursor(bufnr, 'name') end end end) @@ -760,13 +760,13 @@ end local function get_link_text(name, meta) local link_text if meta then - if meta.link_stat and meta.link_stat.type == "directory" then - name = name .. "/" + if meta.link_stat and meta.link_stat.type == 'directory' then + name = name .. '/' end if meta.link then - link_text = "-> " .. meta.link:gsub("\n", "") - if meta.link_stat and meta.link_stat.type == "directory" then + link_text = '-> ' .. meta.link:gsub('\n', '') + if meta.link_stat and meta.link_stat.type == 'directory' then link_text = util.addslash(link_text) end end @@ -786,15 +786,15 @@ end M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden, bufnr) local name = entry[FIELD_NAME] local meta = entry[FIELD_META] - local hl_suffix = "" + local hl_suffix = '' if is_hidden then - hl_suffix = "Hidden" + hl_suffix = 'Hidden' end if meta and meta.display_name then name = meta.display_name end -- 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 local cols = {} 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 for i, column in ipairs(column_defs) do 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 col_width[i + 1] = math.max(col_width[i + 1], vim.api.nvim_strwidth(text)) 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 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) local is_orphan = not (meta and meta.link_stat) 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) if hl then -- Add the trailing / if this is a directory, this is important - if entry_type == "directory" then - name = name .. "/" + if entry_type == 'directory' then + name = name .. '/' end table.insert(cols, { name, hl }) return cols @@ -840,49 +840,50 @@ M.format_entry_cols = function(entry, column_defs, col_width, adapter, is_hidden end local highlight_as_executable = false - if entry_type ~= "directory" then + if entry_type ~= 'directory' then local lower = name:lower() if - lower:match("%.exe$") - or lower:match("%.bat$") - or lower:match("%.cmd$") - or lower:match("%.com$") - or lower:match("%.ps1$") + lower:match('%.exe$') + or lower:match('%.bat$') + or lower:match('%.cmd$') + or lower:match('%.com$') + or lower:match('%.ps1$') then highlight_as_executable = true + -- selene: allow(if_same_then_else) elseif is_unix_executable(entry) then highlight_as_executable = true end end - if entry_type == "directory" then - table.insert(cols, { name .. "/", "OilDir" .. hl_suffix }) - elseif entry_type == "socket" then - table.insert(cols, { name, "OilSocket" .. hl_suffix }) - elseif entry_type == "link" then + if entry_type == 'directory' then + table.insert(cols, { name .. '/', 'OilDir' .. hl_suffix }) + elseif entry_type == 'socket' then + table.insert(cols, { name, 'OilSocket' .. hl_suffix }) + elseif entry_type == 'link' then if not link_name then link_name, link_target = get_link_text(name, meta) end local is_orphan = not (meta and meta.link_stat) if not link_name_hl then if highlight_as_executable then - link_name_hl = "OilExecutable" .. hl_suffix + link_name_hl = 'OilExecutable' .. hl_suffix 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 table.insert(cols, { link_name, link_name_hl }) if link_target 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 table.insert(cols, { link_target, link_target_hl }) end elseif highlight_as_executable then - table.insert(cols, { name, "OilExecutable" .. hl_suffix }) + table.insert(cols, { name, 'OilExecutable' .. hl_suffix }) else - table.insert(cols, { name, "OilFile" .. hl_suffix }) + table.insert(cols, { name, 'OilFile' .. hl_suffix }) end return cols @@ -914,8 +915,8 @@ M.render_buffer_async = function(bufnr, opts, caller_callback) local function callback(err) if not err then vim.api.nvim_exec_autocmds( - "User", - { pattern = "OilReadPost", modeline = false, data = { buf = bufnr } } + 'User', + { pattern = 'OilReadPost', modeline = false, data = { buf = bufnr } } ) end if caller_callback then @@ -923,7 +924,7 @@ M.render_buffer_async = function(bufnr, opts, caller_callback) end end - opts = vim.tbl_deep_extend("keep", opts or {}, { + opts = vim.tbl_deep_extend('keep', opts or {}, { refetch = true, }) ---@cast opts -nil @@ -949,8 +950,8 @@ M.render_buffer_async = function(bufnr, opts, caller_callback) vim.bo[bufnr].undolevels = -1 local handle_error = vim.schedule_wrap(function(message) vim.b[bufnr].oil_rendering = false - vim.bo[bufnr].undolevels = vim.api.nvim_get_option_value("undolevels", { scope = "global" }) - util.render_text(bufnr, { "Error: " .. message }) + vim.bo[bufnr].undolevels = vim.api.nvim_get_option_value('undolevels', { scope = 'global' }) + util.render_text(bufnr, { 'Error: ' .. message }) if pending_renders[bufnr] then for _, cb in ipairs(pending_renders[bufnr]) do cb(message) @@ -987,7 +988,7 @@ M.render_buffer_async = function(bufnr, opts, caller_callback) loading.set_loading(bufnr, false) render_buffer(bufnr, { jump = true }) 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) if callback then callback() diff --git a/lua/resession/extensions/oil.lua b/lua/resession/extensions/oil.lua index 427843d..b3ba754 100644 --- a/lua/resession/extensions/oil.lua +++ b/lua/resession/extensions/oil.lua @@ -1,7 +1,7 @@ local M = {} M.is_win_supported = function(winid, bufnr) - return vim.bo[bufnr].filetype == "oil" + return vim.bo[bufnr].filetype == 'oil' end M.save_win = function(winid) @@ -11,7 +11,7 @@ M.save_win = function(winid) end M.load_win = function(winid, config) - require("oil").open(config.bufname) + require('oil').open(config.bufname) end return M diff --git a/oil.nvim-scm-1.rockspec b/oil.nvim-scm-1.rockspec new file mode 100644 index 0000000..8e1bb80 --- /dev/null +++ b/oil.nvim-scm-1.rockspec @@ -0,0 +1,30 @@ +rockspec_format = '3.0' +package = 'oil.nvim' +version = 'scm-1' + +source = { + url = 'git+https://github.com/barrettruth/oil.nvim.git', +} + +description = { + summary = 'Neovim file explorer: edit your filesystem like a buffer', + homepage = 'https://github.com/barrettruth/oil.nvim', + license = 'MIT', +} + +dependencies = { + 'lua >= 5.1', +} + +test_dependencies = { + 'nlua', + 'busted >= 2.1.1', +} + +test = { + type = 'busted', +} + +build = { + type = 'builtin', +} diff --git a/perf/bootstrap.lua b/perf/bootstrap.lua index 5f10c06..19e17e8 100644 --- a/perf/bootstrap.lua +++ b/perf/bootstrap.lua @@ -1,7 +1,7 @@ -vim.opt.runtimepath:prepend("scripts/benchmark.nvim") -vim.opt.runtimepath:prepend(".") +vim.opt.runtimepath:prepend('scripts/benchmark.nvim') +vim.opt.runtimepath:prepend('.') -local bm = require("benchmark") +local bm = require('benchmark') bm.sandbox() ---@module 'oil' @@ -14,50 +14,53 @@ local DIR_SIZE = tonumber(vim.env.DIR_SIZE) or 100000 local ITERATIONS = tonumber(vim.env.ITERATIONS) or 10 local WARM_UP = tonumber(vim.env.WARM_UP) or 1 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") -require("benchmark.files").create_files(TEST_DIR, "file %d.txt", DIR_SIZE) +vim.fn.mkdir(TEST_DIR, 'p') +require('benchmark.files').create_files(TEST_DIR, 'file %d.txt', DIR_SIZE) +-- selene: allow(global_usage) function _G.jit_profile() - require("oil").setup(setup_opts) - local finish = bm.jit_profile({ filename = TEST_DIR .. "/profile.txt" }) - bm.wait_for_user_event("OilEnter", function() + require('oil').setup(setup_opts) + local finish = bm.jit_profile({ filename = TEST_DIR .. '/profile.txt' }) + bm.wait_for_user_event('OilEnter', function() finish() end) - require("oil").open(TEST_DIR) + require('oil').open(TEST_DIR) end +-- selene: allow(global_usage) function _G.flame_profile() local start, stop = bm.flame_profile({ - pattern = "oil*", - filename = "profile.json", + pattern = 'oil*', + filename = 'profile.json', }) - require("oil").setup(setup_opts) + require('oil').setup(setup_opts) start() - bm.wait_for_user_event("OilEnter", function() + bm.wait_for_user_event('OilEnter', function() stop(function() vim.cmd.qall({ mods = { silent = true } }) end) end) - require("oil").open(TEST_DIR) + require('oil').open(TEST_DIR) end +-- selene: allow(global_usage) function _G.benchmark() - require("oil").setup(setup_opts) - bm.run({ title = "oil.nvim", iterations = ITERATIONS, warm_up = WARM_UP }, function(callback) - bm.wait_for_user_event("OilEnter", callback) - require("oil").open(TEST_DIR) + require('oil').setup(setup_opts) + bm.run({ title = 'oil.nvim', iterations = ITERATIONS, warm_up = WARM_UP }, function(callback) + bm.wait_for_user_event('OilEnter', callback) + require('oil').open(TEST_DIR) end, function(times) local avg = bm.avg(times, { trim_outliers = OUTLIERS }) local std_dev = bm.std_dev(times, { trim_outliers = OUTLIERS }) local lines = { - table.concat(vim.tbl_map(bm.format_time, times), " "), - string.format("Average: %s", bm.format_time(avg)), - string.format("Std deviation: %s", bm.format_time(std_dev)), + table.concat(vim.tbl_map(bm.format_time, times), ' '), + string.format('Average: %s', bm.format_time(avg)), + 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 } }) end) end diff --git a/plugin/oil.lua b/plugin/oil.lua index 2b1c352..bcb530d 100644 --- a/plugin/oil.lua +++ b/plugin/oil.lua @@ -1,3 +1,3 @@ if vim.g.oil ~= nil then - require("oil").setup() + require('oil').setup() end diff --git a/run_tests.sh b/run_tests.sh deleted file mode 100755 index 3018bc0..0000000 --- a/run_tests.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/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" diff --git a/selene.toml b/selene.toml new file mode 100644 index 0000000..ab57300 --- /dev/null +++ b/selene.toml @@ -0,0 +1,5 @@ +std = 'vim' + +[lints] +mixed_table = 'allow' +unused_variable = 'allow' diff --git a/spec/altbuf_spec.lua b/spec/altbuf_spec.lua new file mode 100644 index 0000000..cd2cf4b --- /dev/null +++ b/spec/altbuf_spec.lua @@ -0,0 +1,150 @@ +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' }, 10) + oil.select() + test_util.wait_for_autocmd('BufEnter') + assert.equals('LICENSE', vim.fn.expand('%:.')) + assert.equals('foo', vim.fn.expand('#')) + end) + end) +end) diff --git a/spec/close_spec.lua b/spec/close_spec.lua new file mode 100644 index 0000000..0e15ab7 --- /dev/null +++ b/spec/close_spec.lua @@ -0,0 +1,44 @@ +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({ '' }, 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('', 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) diff --git a/tests/config_spec.lua b/spec/config_spec.lua similarity index 66% rename from tests/config_spec.lua rename to spec/config_spec.lua index aac4eb1..c565c72 100644 --- a/tests/config_spec.lua +++ b/spec/config_spec.lua @@ -1,25 +1,25 @@ -local config = require("oil.config") +local config = require('oil.config') -describe("config", function() +describe('config', function() after_each(function() vim.g.oil = nil 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 } config.setup() assert.is_true(config.delete_to_trash) assert.equals(5000, config.cleanup_delay_ms) 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 config.setup() assert.is_false(config.delete_to_trash) assert.equals(2000, config.cleanup_delay_ms) 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 } config.setup({ delete_to_trash = false }) assert.is_false(config.delete_to_trash) diff --git a/spec/files_spec.lua b/spec/files_spec.lua new file mode 100644 index 0000000..299b434 --- /dev/null +++ b/spec/files_spec.lua @@ -0,0 +1,166 @@ +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) diff --git a/tests/manual_progress.lua b/spec/manual_progress.lua similarity index 65% rename from tests/manual_progress.lua rename to spec/manual_progress.lua index bb838e2..593a748 100644 --- a/tests/manual_progress.lua +++ b/spec/manual_progress.lua @@ -1,5 +1,5 @@ -- Manual test for minimizing/restoring progress window -local Progress = require("oil.mutator.progress") +local Progress = require('oil.mutator.progress') local progress = Progress.new() @@ -12,9 +12,9 @@ progress:show({ for i = 1, 10, 1 do vim.defer_fn(function() progress:set_action({ - type = "create", - url = string.format("oil:///tmp/test_%d.txt", i), - entry_type = "file", + type = 'create', + url = string.format('oil:///tmp/test_%d.txt', i), + entry_type = 'file', }, i, 10) end, (i - 1) * 1000) end @@ -23,6 +23,6 @@ vim.defer_fn(function() progress:close() end, 10000) -vim.keymap.set("n", "R", function() +vim.keymap.set('n', 'R', function() progress:restore() end, {}) diff --git a/spec/minimal_init.lua b/spec/minimal_init.lua new file mode 100644 index 0000000..09f0cd3 --- /dev/null +++ b/spec/minimal_init.lua @@ -0,0 +1,9 @@ +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() diff --git a/spec/move_rename_spec.lua b/spec/move_rename_spec.lua new file mode 100644 index 0000000..5b7348b --- /dev/null +++ b/spec/move_rename_spec.lua @@ -0,0 +1,59 @@ +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) diff --git a/spec/mutator_spec.lua b/spec/mutator_spec.lua new file mode 100644 index 0000000..74ce935 --- /dev/null +++ b/spec/mutator_spec.lua @@ -0,0 +1,419 @@ +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) diff --git a/spec/parser_spec.lua b/spec/parser_spec.lua new file mode 100644 index 0000000..563c001 --- /dev/null +++ b/spec/parser_spec.lua @@ -0,0 +1,249 @@ +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) diff --git a/tests/path_spec.lua b/spec/path_spec.lua similarity index 55% rename from tests/path_spec.lua rename to spec/path_spec.lua index a92a1ca..ab83fe4 100644 --- a/tests/path_spec.lua +++ b/spec/path_spec.lua @@ -1,13 +1,13 @@ -local pathutil = require("oil.pathutil") -describe("pathutil", function() - it("calculates parent path", function() +local pathutil = require('oil.pathutil') +describe('pathutil', function() + it('calculates parent path', function() local cases = { - { "/foo/bar", "/foo/" }, - { "/foo/bar/", "/foo/" }, - { "/", "/" }, - { "", "" }, - { "foo/bar/", "foo/" }, - { "foo", "" }, + { '/foo/bar', '/foo/' }, + { '/foo/bar/', '/foo/' }, + { '/', '/' }, + { '', '' }, + { 'foo/bar/', 'foo/' }, + { 'foo', '' }, } for _, case in ipairs(cases) do local input, expected = unpack(case) @@ -16,12 +16,12 @@ describe("pathutil", function() end end) - it("calculates basename", function() + it('calculates basename', function() local cases = { - { "/foo/bar", "bar" }, - { "/foo/bar/", "bar" }, - { "/", nil }, - { "", nil }, + { '/foo/bar', 'bar' }, + { '/foo/bar/', 'bar' }, + { '/', nil }, + { '', nil }, } for _, case in ipairs(cases) do local input, expected = unpack(case) diff --git a/tests/preview_spec.lua b/spec/preview_spec.lua similarity index 55% rename from tests/preview_spec.lua rename to spec/preview_spec.lua index 08aba78..030332d 100644 --- a/tests/preview_spec.lua +++ b/spec/preview_spec.lua @@ -1,41 +1,40 @@ -require("plenary.async").tests.add_to_env() -local TmpDir = require("tests.tmpdir") -local oil = require("oil") -local test_util = require("tests.test_util") -local util = require("oil.util") +local TmpDir = require('spec.tmpdir') +local oil = require('oil') +local test_util = require('spec.test_util') +local util = require('oil.util') -a.describe("oil preview", function() +describe('oil preview', function() local tmpdir - a.before_each(function() + before_each(function() tmpdir = TmpDir.new() end) - a.after_each(function() + after_each(function() if tmpdir then tmpdir:dispose() end test_util.reset_editor() end) - a.it("opens preview window", function() - tmpdir:create({ "a.txt" }) + it('opens preview window', function() + tmpdir:create({ 'a.txt' }) test_util.oil_open(tmpdir.path) - a.wrap(oil.open_preview, 2)() + test_util.await(oil.open_preview, 2) local preview_win = util.get_preview_win() assert.not_nil(preview_win) assert(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) - assert.are.same({ "a.txt" }, preview_lines) + assert.are.same({ 'a.txt' }, preview_lines) end) - a.it("opens preview window when open(preview={})", function() - tmpdir:create({ "a.txt" }) + it('opens preview window when open(preview={})', function() + tmpdir:create({ 'a.txt' }) test_util.oil_open(tmpdir.path, { preview = {} }) local preview_win = util.get_preview_win() assert.not_nil(preview_win) assert(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) - assert.are.same({ "a.txt" }, preview_lines) + assert.are.same({ 'a.txt' }, preview_lines) end) end) diff --git a/spec/regression_spec.lua b/spec/regression_spec.lua new file mode 100644 index 0000000..bfa5e53 --- /dev/null +++ b/spec/regression_spec.lua @@ -0,0 +1,137 @@ +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/', '' }, 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) diff --git a/tests/select_spec.lua b/spec/select_spec.lua similarity index 56% rename from tests/select_spec.lua rename to spec/select_spec.lua index 9de2eb4..47c95ac 100644 --- a/tests/select_spec.lua +++ b/spec/select_spec.lua @@ -1,90 +1,85 @@ -require("plenary.async").tests.add_to_env() -local oil = require("oil") -local test_util = require("tests.test_util") +local oil = require('oil') +local test_util = require('spec.test_util') -a.describe("oil select", function() +describe('oil select', function() after_each(function() test_util.reset_editor() end) - a.it("opens file under cursor", function() + it('opens file under cursor', function() test_util.oil_open() - -- Go to the bottom, so the cursor is not on a directory - vim.cmd.normal({ args = { "G" } }) - a.wrap(oil.select, 2)() + vim.cmd.normal({ args = { 'G' } }) + test_util.await(oil.select, 2) 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) - a.it("opens file in new tab", function() + it('opens file in new tab', function() test_util.oil_open() local tabpage = vim.api.nvim_get_current_tabpage() - a.wrap(oil.select, 2)({ tab = true }) + test_util.await(oil.select, 2, { tab = true }) assert.equals(2, #vim.api.nvim_list_tabpages()) assert.equals(1, #vim.api.nvim_tabpage_list_wins(0)) assert.not_equals(tabpage, vim.api.nvim_get_current_tabpage()) end) - a.it("opens file in new split", function() + it('opens file in new split', function() test_util.oil_open() local winid = vim.api.nvim_get_current_win() - a.wrap(oil.select, 2)({ vertical = true }) + test_util.await(oil.select, 2, { vertical = true }) assert.equals(1, #vim.api.nvim_list_tabpages()) assert.equals(2, #vim.api.nvim_tabpage_list_wins(0)) assert.not_equals(winid, vim.api.nvim_get_current_win()) end) - a.it("opens multiple files in new tabs", function() + it('opens multiple files in new tabs', function() 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() - a.wrap(oil.select, 2)({ tab = true }) + test_util.await(oil.select, 2, { tab = true }) assert.equals(3, #vim.api.nvim_list_tabpages()) assert.equals(1, #vim.api.nvim_tabpage_list_wins(0)) assert.not_equals(tabpage, vim.api.nvim_get_current_tabpage()) end) - a.it("opens multiple files in new splits", function() + it('opens multiple files in new splits', function() 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() - a.wrap(oil.select, 2)({ vertical = true }) + test_util.await(oil.select, 2, { vertical = true }) assert.equals(1, #vim.api.nvim_list_tabpages()) assert.equals(3, #vim.api.nvim_tabpage_list_wins(0)) assert.not_equals(winid, vim.api.nvim_get_current_win()) end) - a.describe("close after open", function() - a.it("same window", function() - vim.cmd.edit({ args = { "foo" } }) + describe('close after open', function() + it('same window', function() + vim.cmd.edit({ args = { 'foo' } }) local bufnr = vim.api.nvim_get_current_buf() test_util.oil_open() - -- Go to the bottom, so the cursor is not on a directory - vim.cmd.normal({ args = { "G" } }) - a.wrap(oil.select, 2)({ close = true }) + vim.cmd.normal({ args = { 'G' } }) + test_util.await(oil.select, 2, { close = true }) 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("oil", vim.bo.filetype) + assert.not_equals('oil', vim.bo.filetype) end) - a.it("split", function() - vim.cmd.edit({ args = { "foo" } }) + it('split', function() + vim.cmd.edit({ args = { 'foo' } }) local bufnr = vim.api.nvim_get_current_buf() local winid = vim.api.nvim_get_current_win() test_util.oil_open() - a.wrap(oil.select, 2)({ vertical = true, close = true }) + test_util.await(oil.select, 2, { vertical = true, close = true }) assert.equals(2, #vim.api.nvim_tabpage_list_wins(0)) assert.equals(bufnr, vim.api.nvim_win_get_buf(winid)) end) - a.it("tab", function() - vim.cmd.edit({ args = { "foo" } }) + it('tab', function() + vim.cmd.edit({ args = { 'foo' } }) local bufnr = vim.api.nvim_get_current_buf() local tabpage = vim.api.nvim_get_current_tabpage() test_util.oil_open() - a.wrap(oil.select, 2)({ tab = true, close = true }) + test_util.await(oil.select, 2, { tab = true, close = true }) assert.equals(1, #vim.api.nvim_tabpage_list_wins(0)) assert.equals(2, #vim.api.nvim_list_tabpages()) vim.api.nvim_set_current_tabpage(tabpage) diff --git a/tests/test_util.lua b/spec/test_util.lua similarity index 51% rename from tests/test_util.lua rename to spec/test_util.lua index 46f63df..57f81b1 100644 --- a/tests/test_util.lua +++ b/spec/test_util.lua @@ -1,24 +1,19 @@ -require("plenary.async").tests.add_to_env() -local cache = require("oil.cache") -local test_adapter = require("oil.adapters.test") -local util = require("oil.util") +local cache = require('oil.cache') +local test_adapter = require('oil.adapters.test') +local util = require('oil.util') local M = {} M.reset_editor = function() - require("oil").setup({ + require('oil').setup({ columms = {}, adapters = { - ["oil-test://"] = "test", + ['oil-test://'] = 'test', }, prompt_save_on_select_new_entry = false, }) vim.cmd.tabonly({ mods = { silent = true } }) - for i, winid in ipairs(vim.api.nvim_tabpage_list_wins(0)) do - 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)) + vim.cmd.new() + vim.cmd.only() for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do vim.api.nvim_buf_delete(bufnr, { force = true }) end @@ -34,49 +29,87 @@ local function throwiferr(err, ...) end end -M.oil_open = function(...) - a.wrap(require("oil").open, 3)(...) -end - M.await = function(fn, nargs, ...) - return throwiferr(a.wrap(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.wait_for_autocmd = a.wrap(function(autocmd, cb) +M.await_throwiferr = function(fn, nargs, ...) + return throwiferr(M.await(fn, nargs, ...)) +end + +M.oil_open = function(...) + M.await(require('oil').open, 3, ...) +end + +M.wait_for_autocmd = function(autocmd) + local triggered = false local opts = { - pattern = "*", + pattern = '*', nested = true, once = true, } - if type(autocmd) == "table" then - opts = vim.tbl_extend("force", opts, autocmd) + if type(autocmd) == 'table' then + opts = vim.tbl_extend('force', opts, autocmd) autocmd = autocmd[1] opts[1] = nil end - opts.callback = vim.schedule_wrap(cb) - + opts.callback = vim.schedule_wrap(function() + triggered = true + end) vim.api.nvim_create_autocmd(autocmd, opts) -end, 2) + 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 = a.wrap(function(cb) - util.run_after_load(0, vim.schedule_wrap(cb)) -end, 1) +M.wait_oil_ready = function() + local ready = false + util.run_after_load( + 0, + vim.schedule_wrap(function() + ready = true + 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 timestep integer M.feedkeys = function(actions, timestep) timestep = timestep or 10 - a.util.sleep(timestep) + vim.wait(timestep) for _, action in ipairs(actions) do - a.util.sleep(timestep) + vim.wait(timestep) 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 - a.util.sleep(timestep) - -- process pending keys until the queue is empty. - -- Note that this will exit insert mode. - vim.api.nvim_feedkeys("", "x", true) - a.util.sleep(timestep) + vim.wait(timestep) + vim.api.nvim_feedkeys('', 'x', true) + vim.wait(timestep) end M.actions = { @@ -85,41 +118,40 @@ M.actions = { open = function(args) vim.schedule(function() 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 - vim.api.nvim_exec_autocmds("User", { - pattern = "OilEnter", + vim.api.nvim_exec_autocmds('User', { + pattern = 'OilEnter', modeline = false, data = { buf = vim.api.nvim_get_current_buf() }, }) end end) - M.wait_for_autocmd({ "User", pattern = "OilEnter" }) + M.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) end, ---Save all changes and wait for operation to complete save = function() - vim.schedule_wrap(require("oil").save)({ confirm = false }) - M.wait_for_autocmd({ "User", pattern = "OilMutationComplete" }) + vim.schedule_wrap(require('oil').save)({ confirm = false }) + M.wait_for_autocmd({ 'User', pattern = 'OilMutationComplete' }) end, ---@param bufnr? integer 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, ---Move cursor to a file or directory in an oil buffer ---@param filename string focus = function(filename) 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 if line:match(search) then vim.api.nvim_win_set_cursor(0, { i, 0 }) return end end - error("Could not find file " .. filename) + error('Could not find file ' .. filename) end, } @@ -133,7 +165,7 @@ M.parse_entries = function(bufnr) end local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true) return vim.tbl_map(function(line) - return line:match("^/%d+ +(.+)$") + return line:match('^/%d+ +(.+)$') end, lines) end diff --git a/tests/tmpdir.lua b/spec/tmpdir.lua similarity index 61% rename from tests/tmpdir.lua rename to spec/tmpdir.lua index bb8a9c0..00a04fd 100644 --- a/tests/tmpdir.lua +++ b/spec/tmpdir.lua @@ -1,25 +1,18 @@ -local fs = require("oil.fs") -local test_util = require("tests.test_util") - -local await = test_util.await +local fs = require('oil.fs') +local test_util = require('spec.test_util') ---@param path string ----@param cb fun(err: nil|string) -local function touch(path, cb) - vim.loop.fs_open(path, "w", 420, function(err, fd) -- 0644 - if err then - cb(err) - else - local shortpath = path:gsub("^[^" .. fs.sep .. "]*" .. fs.sep, "") - vim.loop.fs_write(fd, shortpath, nil, function(err2) - if err2 then - cb(err2) - else - vim.loop.fs_close(fd, cb) - end - end) - end - end) +local function touch(path) + local fd, open_err = vim.loop.fs_open(path, 'w', 420) -- 0644 + if not fd then + error(open_err) + end + local shortpath = path:gsub('^[^' .. fs.sep .. ']*' .. fs.sep, '') + local _, write_err = vim.loop.fs_write(fd, shortpath) + if write_err then + error(write_err) + end + vim.loop.fs_close(fd) end ---@param filepath string @@ -28,11 +21,14 @@ local function exists(filepath) local stat = vim.loop.fs_stat(filepath) return stat ~= nil and stat.type ~= nil end + local TmpDir = {} TmpDir.new = function() - local path = await(vim.loop.fs_mkdtemp, 2, "oil_test_XXXXXXXXX") - a.util.scheduler() + local path, err = vim.loop.fs_mkdtemp('oil_test_XXXXXXXXX') + if not path then + error(err) + end return setmetatable({ path = path }, { __index = TmpDir, }) @@ -46,31 +42,28 @@ function TmpDir:create(paths) for i, piece in ipairs(pieces) do partial_path = fs.join(partial_path, piece) if i == #pieces and not vim.endswith(partial_path, fs.sep) then - await(touch, 2, partial_path) + touch(partial_path) elseif not exists(partial_path) then vim.loop.fs_mkdir(partial_path, 493) end end end - a.util.scheduler() end ---@param filepath string ---@return string? 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 return nil end local stat = vim.loop.fs_fstat(fd) local content = vim.loop.fs_read(fd, stat.size) vim.loop.fs_close(fd) - a.util.scheduler() return content end ---@param dir string ----@param cb fun(err: nil|string, entry: {type: oil.EntryType, name: string, root: string} local function walk(dir) local ret = {} for name, type in vim.fs.dir(dir) do @@ -79,7 +72,7 @@ local function walk(dir) type = type, root = dir, }) - if type == "directory" then + if type == 'directory' then vim.list_extend(ret, walk(fs.join(dir, name))) end end @@ -90,10 +83,10 @@ end local assert_fs = function(root, paths) local unlisted_dirs = {} for k in pairs(paths) do - local pieces = vim.split(k, "/") - local partial_path = "" + local pieces = vim.split(k, '/') + local partial_path = '' for i, piece in ipairs(pieces) do - partial_path = partial_path .. piece .. "/" + partial_path = partial_path .. piece .. '/' if i ~= #pieces then unlisted_dirs[partial_path] = true end @@ -107,17 +100,16 @@ local assert_fs = function(root, paths) for _, entry in ipairs(entries) do local fullpath = fs.join(entry.root, entry.name) local shortpath = fullpath:sub(root:len() + 2) - if entry.type == "directory" then - shortpath = shortpath .. "/" + if entry.type == 'directory' then + shortpath = shortpath .. '/' end local expected_content = paths[shortpath] paths[shortpath] = nil - assert.truthy(expected_content, string.format("Unexpected entry '%s'", shortpath)) - if entry.type == "file" then + assert(expected_content, string.format("Unexpected entry '%s'", shortpath)) + if entry.type == 'file' then local data = read_file(fullpath) - assert.equals( - expected_content, - data, + assert( + expected_content == data, string.format( "File '%s' expected content '%s' received '%s'", shortpath, @@ -129,11 +121,11 @@ local assert_fs = function(root, paths) end for k, v in pairs(paths) do - assert.falsy( - k, + assert( + not k, string.format( "Expected %s '%s', but it was not found", - v == true and "directory" or "file", + v == true and 'directory' or 'file', k ) ) @@ -142,27 +134,23 @@ end ---@param paths table function TmpDir:assert_fs(paths) - a.util.scheduler() assert_fs(self.path, paths) end function TmpDir:assert_exists(path) - a.util.scheduler() path = fs.join(self.path, path) local stat = vim.loop.fs_stat(path) - assert.truthy(stat, string.format("Expected path '%s' to exist", path)) + assert(stat, string.format("Expected path '%s' to exist", path)) end function TmpDir:assert_not_exists(path) - a.util.scheduler() path = fs.join(self.path, path) local stat = vim.loop.fs_stat(path) - assert.falsy(stat, string.format("Expected path '%s' to not exist", path)) + assert(not stat, string.format("Expected path '%s' to not exist", path)) end function TmpDir:dispose() - await(fs.recursive_delete, 3, "directory", self.path) - a.util.scheduler() + test_util.await_throwiferr(fs.recursive_delete, 3, 'directory', self.path) end return TmpDir diff --git a/spec/trash_spec.lua b/spec/trash_spec.lua new file mode 100644 index 0000000..b34b40b --- /dev/null +++ b/spec/trash_spec.lua @@ -0,0 +1,149 @@ +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) diff --git a/spec/url_spec.lua b/spec/url_spec.lua new file mode 100644 index 0000000..46b7af7 --- /dev/null +++ b/spec/url_spec.lua @@ -0,0 +1,25 @@ +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) diff --git a/tests/util_spec.lua b/spec/util_spec.lua similarity index 53% rename from tests/util_spec.lua rename to spec/util_spec.lua index 3193842..5796398 100644 --- a/tests/util_spec.lua +++ b/spec/util_spec.lua @@ -1,10 +1,10 @@ -local util = require("oil.util") -describe("util", function() - it("url_escape", function() +local util = require('oil.util') +describe('util', function() + it('url_escape', function() local cases = { - { "foobar", "foobar" }, - { "foo bar", "foo%20bar" }, - { "/foo/bar", "%2Ffoo%2Fbar" }, + { 'foobar', 'foobar' }, + { 'foo bar', 'foo%20bar' }, + { '/foo/bar', '%2Ffoo%2Fbar' }, } for _, case in ipairs(cases) do local input, expected = unpack(case) @@ -13,12 +13,12 @@ describe("util", function() end end) - it("url_unescape", function() + it('url_unescape', function() local cases = { - { "foobar", "foobar" }, - { "foo%20bar", "foo bar" }, - { "%2Ffoo%2Fbar", "/foo/bar" }, - { "foo%%bar", "foo%%bar" }, + { 'foobar', 'foobar' }, + { 'foo%20bar', 'foo bar' }, + { '%2Ffoo%2Fbar', '/foo/bar' }, + { 'foo%%bar', 'foo%%bar' }, } for _, case in ipairs(cases) do local input, expected = unpack(case) diff --git a/spec/win_options_spec.lua b/spec/win_options_spec.lua new file mode 100644 index 0000000..f62aa83 --- /dev/null +++ b/spec/win_options_spec.lua @@ -0,0 +1,65 @@ +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 ', 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 ', 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) diff --git a/tests/altbuf_spec.lua b/tests/altbuf_spec.lua deleted file mode 100644 index beece15..0000000 --- a/tests/altbuf_spec.lua +++ /dev/null @@ -1,157 +0,0 @@ -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" }, 10) - oil.select() - test_util.wait_for_autocmd("BufEnter") - assert.equals("LICENSE", vim.fn.expand("%:.")) - assert.equals("foo", vim.fn.expand("#")) - end) - end) -end) diff --git a/tests/close_spec.lua b/tests/close_spec.lua deleted file mode 100644 index ff14738..0000000 --- a/tests/close_spec.lua +++ /dev/null @@ -1,45 +0,0 @@ -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({ "" }, 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("", 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) diff --git a/tests/files_spec.lua b/tests/files_spec.lua deleted file mode 100644 index 4333d80..0000000 --- a/tests/files_spec.lua +++ /dev/null @@ -1,173 +0,0 @@ -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) diff --git a/tests/minimal_init.lua b/tests/minimal_init.lua deleted file mode 100644 index 70a66b1..0000000 --- a/tests/minimal_init.lua +++ /dev/null @@ -1,5 +0,0 @@ -vim.opt.runtimepath:append(".") - -vim.o.swapfile = false -vim.bo.swapfile = false -require("tests.test_util").reset_editor() diff --git a/tests/move_rename_spec.lua b/tests/move_rename_spec.lua deleted file mode 100644 index 660ab64..0000000 --- a/tests/move_rename_spec.lua +++ /dev/null @@ -1,59 +0,0 @@ -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) diff --git a/tests/mutator_spec.lua b/tests/mutator_spec.lua deleted file mode 100644 index 17548d3..0000000 --- a/tests/mutator_spec.lua +++ /dev/null @@ -1,426 +0,0 @@ -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) diff --git a/tests/parser_spec.lua b/tests/parser_spec.lua deleted file mode 100644 index 9884ca1..0000000 --- a/tests/parser_spec.lua +++ /dev/null @@ -1,250 +0,0 @@ -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) diff --git a/tests/regression_spec.lua b/tests/regression_spec.lua deleted file mode 100644 index 9d8f944..0000000 --- a/tests/regression_spec.lua +++ /dev/null @@ -1,148 +0,0 @@ -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/", "" }, 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) diff --git a/tests/trash_spec.lua b/tests/trash_spec.lua deleted file mode 100644 index d09a57f..0000000 --- a/tests/trash_spec.lua +++ /dev/null @@ -1,150 +0,0 @@ -require("plenary.async").tests.add_to_env() -local TmpDir = require("tests.tmpdir") -local test_util = require("tests.test_util") - -a.describe("freedesktop", function() - local tmpdir - local tmphome - local home = vim.env.XDG_DATA_HOME - a.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) - a.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) - - a.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) - - a.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) - - a.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) - - a.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) - - a.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) - - a.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) - - a.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) - - a.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) - - a.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) diff --git a/tests/url_spec.lua b/tests/url_spec.lua deleted file mode 100644 index 32d801e..0000000 --- a/tests/url_spec.lua +++ /dev/null @@ -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) diff --git a/tests/win_options_spec.lua b/tests/win_options_spec.lua deleted file mode 100644 index cb638a7..0000000 --- a/tests/win_options_spec.lua +++ /dev/null @@ -1,66 +0,0 @@ -require("plenary.async").tests.add_to_env() -local oil = require("oil") -local test_util = require("tests.test_util") - -a.describe("window options", function() - after_each(function() - test_util.reset_editor() - end) - - a.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) - - a.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) - - a.it("Restores window options on split ", function() - test_util.oil_open() - assert.equals("no", vim.o.signcolumn) - vim.cmd.split({ args = { "README.md" } }) - assert.equals("auto", vim.o.signcolumn) - end) - - a.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) - - a.it("Restores window options on tabnew ", function() - test_util.oil_open() - assert.equals("no", vim.o.signcolumn) - vim.cmd.tabnew({ args = { "README.md" } }) - assert.equals("auto", vim.o.signcolumn) - end) - - a.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) - - a.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) diff --git a/vim.toml b/vim.toml new file mode 100644 index 0000000..0fbe00a --- /dev/null +++ b/vim.toml @@ -0,0 +1,36 @@ +[selene] +base = "lua51" +name = "vim" + +[vim] +any = true + +[jit] +any = true + +[bit] +any = true + +[assert] +any = true + +[a] +any = true + +[describe] +any = true + +[it] +any = true + +[before_each] +any = true + +[after_each] +any = true + +[spy] +any = true + +[stub] +any = true