diff --git a/README.md b/README.md index bbf4437b..1d5fc4bc 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,19 @@ require("oil").setup({ { "name", "asc" }, }, }, + -- EXPERIMENTAL support for performing file operations with git + git = { + -- Return true to automatically git add/mv/rm files + add = function(path) + return false + end, + mv = function(src_path, dest_path) + return false + end, + rm = function(path) + return false + end, + }, -- Configuration for the floating window in oil.open_float float = { -- Padding around the floating window diff --git a/doc/oil.txt b/doc/oil.txt index 68a75317..9e713ece 100644 --- a/doc/oil.txt +++ b/doc/oil.txt @@ -117,6 +117,19 @@ CONFIG *oil-confi { "name", "asc" }, }, }, + -- EXPERIMENTAL support for performing file operations with git + git = { + -- Return true to automatically git add/mv/rm files + add = function(path) + return false + end, + mv = function(src_path, dest_path) + return false + end, + rm = function(path) + return false + end, + }, -- Configuration for the floating window in oil.open_float float = { -- Padding around the floating window diff --git a/lua/oil/adapters/files.lua b/lua/oil/adapters/files.lua index 72131539..89eb8103 100644 --- a/lua/oil/adapters/files.lua +++ b/lua/oil/adapters/files.lua @@ -3,6 +3,7 @@ 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 permissions = require("oil.adapters.files.permissions") local trash = require("oil.adapters.files.trash") local util = require("oil.util") @@ -476,6 +477,18 @@ M.perform_action = function(action, cb) local _, path = util.parse_url(action.url) assert(path) path = fs.posix_to_os_path(path) + + if config.git.add(path) then + local old_cb = cb + cb = vim.schedule_wrap(function(err) + if not err then + git.add(path, old_cb) + else + old_cb(err) + end + end) + end + if action.entry_type == "directory" then uv.fs_mkdir(path, 493, function(err) -- Ignore if the directory already exists @@ -503,6 +516,18 @@ M.perform_action = function(action, cb) local _, path = util.parse_url(action.url) assert(path) path = fs.posix_to_os_path(path) + + if config.git.rm(path) then + local old_cb = cb + cb = function(err) + if not err then + git.rm(path, old_cb) + else + old_cb(err) + end + end + end + if config.delete_to_trash then if config.trash_command then vim.notify_once( @@ -525,7 +550,11 @@ M.perform_action = function(action, cb) assert(dest_path) src_path = fs.posix_to_os_path(src_path) dest_path = fs.posix_to_os_path(dest_path) - fs.recursive_move(action.entry_type, src_path, dest_path, cb) + if config.git.mv(src_path, dest_path) then + git.mv(action.entry_type, src_path, dest_path, cb) + else + fs.recursive_move(action.entry_type, src_path, dest_path, cb) + end else -- We should never hit this because we don't implement supported_cross_adapter_actions cb("files adapter doesn't support cross-adapter move") diff --git a/lua/oil/config.lua b/lua/oil/config.lua index b3683873..cc0ac3b6 100644 --- a/lua/oil/config.lua +++ b/lua/oil/config.lua @@ -100,6 +100,19 @@ local default_config = { { "name", "asc" }, }, }, + -- EXPERIMENTAL support for performing file operations with git + git = { + -- Return true to automatically git add/mv/rm files + add = function(path) + return false + end, + mv = function(src_path, dest_path) + return false + end, + rm = function(path) + return false + end, + }, -- Configuration for the floating window in oil.open_float float = { -- Padding around the floating window diff --git a/lua/oil/git.lua b/lua/oil/git.lua new file mode 100644 index 00000000..b01c305f --- /dev/null +++ b/lua/oil/git.lua @@ -0,0 +1,115 @@ +-- integration with git operations +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] + if git_dir then + return vim.fs.dirname(git_dir) + else + return nil + end +end + +---@param path string +---@param cb fun(err: nil|string) +M.add = function(path, cb) + local root = M.get_root(path) + if not root then + return cb() + end + + local stderr = "" + local jid = vim.fn.jobstart({ "git", "add", path }, { + cwd = root, + stderr_buffered = true, + on_stderr = function(_, data) + stderr = table.concat(data, "\n") + end, + on_exit = function(_, code) + if code ~= 0 then + cb("Error in git add: " .. stderr) + else + cb() + end + end, + }) + if jid <= 0 then + cb() + end +end + +---@param path string +---@param cb fun(err: nil|string) +M.rm = function(path, cb) + local root = M.get_root(path) + if not root then + return cb() + end + + 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") + end, + on_exit = function(_, code) + if code ~= 0 then + stderr = vim.trim(stderr) + if stderr:match("^fatal: pathspec '.*' did not match any files$") then + cb() + else + cb("Error in git rm: " .. stderr) + end + else + cb() + end + end, + }) + if jid <= 0 then + cb() + end +end + +---@param entry_type oil.EntryType +---@param src_path string +---@param dest_path string +---@param cb fun(err: nil|string) +M.mv = function(entry_type, src_path, dest_path, cb) + local src_git = M.get_root(src_path) + if not src_git or src_git ~= M.get_root(dest_path) then + fs.recursive_move(entry_type, src_path, dest_path, cb) + return + end + + 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") + end, + on_exit = function(_, code) + if code ~= 0 then + stderr = vim.trim(stderr) + if stderr:match("^fatal: not under version control") then + fs.recursive_move(entry_type, src_path, dest_path, cb) + else + cb("Error in git mv: " .. stderr) + end + else + cb() + end + end, + }) + if jid <= 0 then + -- Failed to run git, fall back to normal filesystem operations + fs.recursive_move(entry_type, src_path, dest_path, cb) + end +end + +return M