Skip to content
115 changes: 115 additions & 0 deletions lua/mini/files.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not filter any create actions. The logic of fs_actions is like this:

  • "Create" action only has to.
  • "Delete" action has meaningful from (the possible to when moving to trash is not relevant).
  • "Rename" action has both from and to (as it is copy plus delete).

So I don't think there should be any checks for from and to when there is a check for action. Otherwise, it should be fixed in diff computation.

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),
})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these three and the didXxx notifications have the exact same logic, just different field names. I am fairly certain that there is no need for any H.get_lsp_file_xxx_form_action helpers. All their logic can be done at the top of lsp_fs_hook(method, diffs) with something like this:

H.lsp_fs_hook = function(method, diffs)
  -- Compute LSP params
  local files, to_uri = {}, vim.uri_from_fname
  for _, d in ipairs(diffs) do
    local file = {}
    file[d.to ~= nil and 'oldUri' or 'uri'] = d.from ~= nil and to_uri(d.from) or nil
    file[d.from ~= nil and 'newUri' or 'uri'] = d.to ~= nil and to_uri(d.to) or nil
    table.insert(files, file)
  end

  local params = { files = files }

  -- ...

local successfull_create = {}
local successfull_delete = {}
local successfull_rename = {}
Comment on lines +2757 to +2759
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With shorter names, fitting them on the same line will be very readable.

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 }
Expand All @@ -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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sorry I don't have a more actionable direction for now, but could you please make it less nested (it is currently way too nested) and easier to read?

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 = {},
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't create multiline tables if they can fit in a single line. StyLua won't reformat these into a single line if there are trailing commas, so these have to be done manually.

}
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 = {}
Expand Down
2 changes: 1 addition & 1 deletion tests/test_files.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading