diff --git a/lua/mini/files.lua b/lua/mini/files.lua index 98aaab3f..efbcb9c9 100644 --- a/lua/mini/files.lua +++ b/lua/mini/files.lua @@ -2734,10 +2734,37 @@ H.fs_actions_to_lines = function(fs_actions) end H.fs_actions_apply = function(fs_actions) + local filtered_create = vim.tbl_filter( + function(diff) return diff.from and diff.action == 'create' and not H.fs_is_present_path(diff.from) end, + fs_actions + ) + local filtered_delete = vim.tbl_filter(function(diff) return diff.action == 'delete' end, fs_actions) + local filtered_rename = vim.tbl_filter( + function(diff) return diff.from and diff.action == 'rename' and not H.fs_is_present_path(diff.to) end, + fs_actions + ) + + H.lsp_fs_hook('willCreate', { + files = vim.tbl_map(H.get_lsp_file_create_from_action, filtered_create), + }) + H.lsp_fs_hook('willDelete', { + files = vim.tbl_map(H.get_lsp_file_delete_from_action, filtered_delete), + }) + H.lsp_fs_hook('willRename', { + files = vim.tbl_map(H.get_lsp_file_rename_from_action, filtered_rename), + }) + + local successfull_create = {} + local successfull_delete = {} + local successfull_rename = {} for i = 1, #fs_actions do local diff, action = fs_actions[i], fs_actions[i].action local ok, success = pcall(H.fs_do[action], diff.from, diff.to) if ok and success then + if diff.from and diff.action == 'create' then table.insert(successfull_create, diff) end + if diff.from and diff.action == 'delete' then table.insert(successfull_delete, diff) end + if diff.from and diff.action == 'rename' then table.insert(successfull_rename, diff) end + -- Trigger event local to = action == 'create' and diff.to:gsub('/$', '') or diff.to local data = { action = action, from = diff.from, to = to } @@ -2749,6 +2776,94 @@ H.fs_actions_apply = function(fs_actions) if has_moved then H.adjust_after_move(diff.from, to, fs_actions, i + 1) end end end + + H.lsp_fs_hook('didCreate', { + files = vim.tbl_map(H.get_lsp_file_create_from_action, successfull_create), + }) + H.lsp_fs_hook('didDelete', { + files = vim.tbl_map(H.get_lsp_file_delete_from_action, successfull_delete), + }) + H.lsp_fs_hook('didRename', { + files = vim.tbl_map(H.get_lsp_file_rename_from_action, successfull_rename), + }) +end + +H.lsp_fs_hook = function(method, params) + local full_method = 'workspace/' .. method .. 'Files' + local clients = vim.lsp.get_clients({ method = full_method }) + + -- TODO(TheLeoP): configurable timeout + local timeout = 1000 + for _, client in ipairs(clients) do + local filters = client.server_capabilities.workspace.fileOperations[method].filters --[=[@as lsp.FileOperationFilter[]]=] + local grouped_matching_functions = {} + for _, filter in ipairs(filters) do + local ignore_case = filter.pattern.options and filter.pattern.options.ignoreCase + local glob = filter.pattern.glob + if ignore_case then glob = vim.fn.tolower(glob) end + + local matching_functions = {} + table.insert(matching_functions, function(uri) + local path = vim.uri_to_fname(uri) + return vim.glob.to_lpeg(glob):match(path) ~= nil + end) + if filter.scheme then + table.insert(matching_functions, function(uri) return uri:find('^' .. filter.scheme .. ':') ~= nil end) + end + if filter.pattern.matches then + table.insert(matching_functions, function(uri) + local path = vim.uri_to_fname(uri) + local type = H.fs_get_type(path) + if filter.pattern.matches == 'folder' then return type == 'directory' end + if filter.pattern.matches == 'file' then return type == 'file' end + end) + end + table.insert(grouped_matching_functions, matching_functions) + end + + local filtered_params = { + files = {}, + } + for _, file in ipairs(params.files) do + local uri = file.uri or file.oldUri + local matches_any_filter = false + for _, matching_functions in ipairs(grouped_matching_functions) do + if not matches_any_filter then + local matches_current_filter = true + for _, matching_function in ipairs(matching_functions) do + matches_current_filter = matches_current_filter and matching_function(uri) + end + matches_any_filter = matches_any_filter or matches_current_filter + end + end + if matches_any_filter then table.insert(filtered_params.files, file) end + end + + if method:sub(1, 3) == 'did' then + client:notify(full_method, filtered_params) + else + local response, err = client:request_sync(full_method, filtered_params, timeout) + -- TODO(TheLeoP): show error to user? + if response and response.result then + vim.lsp.util.apply_workspace_edit(response.result, client.offset_encoding) + end + end + end +end + +H.get_lsp_file_create_from_action = function(action) + return { + uri = vim.uri_from_fname(action.from), + } +end + +H.get_lsp_file_delete_from_action = H.get_lsp_file_create_from_action + +H.get_lsp_file_rename_from_action = function(action) + return { + oldUri = vim.uri_from_fname(action.from), + newUri = vim.uri_from_fname(action.to), + } end H.fs_do = {} diff --git a/tests/test_files.lua b/tests/test_files.lua index d1f44d65..3b90d63f 100644 --- a/tests/test_files.lua +++ b/tests/test_files.lua @@ -154,7 +154,7 @@ local mock_stdpath_data = function() local data_dir = make_test_path('data') local lua_cmd = string.format( [[ - _G.stdpath_orig = vim.fn.stpath + _G.stdpath_orig = vim.fn.stdpath vim.fn.stdpath = function(what) if what == 'data' then return %s end return _G.stdpath_orig(what)