Files
nvim.config/lua/scripts/go-extract-interface.lua

138 lines
4.0 KiB
Lua

local M = {}
-- Parse method signature from a line
local function parse_method(line)
-- Match: func (receiver) MethodName(params) (returns)
local pattern = "^func%s+%([^)]+%)%s+([%w_]+)%s*(%b())%s*(.*)"
local name, params, returns = line:match(pattern)
if name then
-- Clean up returns (remove leading spaces and parentheses if single return)
returns = returns:gsub("^%s+", "")
if returns:match("^%b()") then
returns = " " .. returns
elseif returns ~= "" then
returns = " " .. returns
end
return name .. params .. returns
end
return nil
end
-- Find all methods for a given struct type
local function find_struct_methods(struct_name, bufnr)
local methods = {}
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
-- Pattern to match methods with the struct as receiver
-- Matches both pointer and value receivers
local patterns = {
"^func%s+%(%s*%w+%s+%*?" .. struct_name .. "%s*%)", -- Standard pattern
}
for i, line in ipairs(lines) do
for _, pattern in ipairs(patterns) do
if line:match(pattern) then
local method_sig = parse_method(line)
if method_sig then
table.insert(methods, method_sig)
end
end
end
end
return methods
end
-- Get struct name under cursor
local function get_struct_name()
local line = vim.api.nvim_get_current_line()
-- Try to match: type StructName struct
local struct_name = line:match("^type%s+([%w_]+)%s+struct")
if not struct_name then
-- Try to match if cursor is on struct keyword
struct_name = line:match("type%s+([%w_]+)%s+struct")
end
return struct_name
end
-- Generate interface from methods
local function generate_interface(struct_name, methods)
if #methods == 0 then
return nil, "No methods found for struct " .. struct_name
end
-- Create interface name (conventionally, I + StructName or StructName + er)
local interface_name = "I" .. struct_name
local lines = {
"type " .. interface_name .. " interface {",
}
for _, method in ipairs(methods) do
local method_without_bracket = method:gsub("{$", "")
local cleaned_method = method_without_bracket:gsub("%s+$", "")
table.insert(lines, "\t" .. cleaned_method)
end
table.insert(lines, "}")
return lines, nil
end
-- Main function to extract interface
function M.extract_interface()
local bufnr = vim.api.nvim_get_current_buf()
-- Check if we're in a Go file
local filetype = vim.bo[bufnr].filetype
if filetype ~= "go" then
vim.notify("This command only works in Go files", vim.log.levels.ERROR)
return
end
-- Get struct name under cursor
local struct_name = get_struct_name()
if not struct_name then
vim.notify("Cursor is not on a struct definition", vim.log.levels.ERROR)
return
end
-- Find all methods for this struct
local methods = find_struct_methods(struct_name, bufnr)
-- Generate interface
local interface_lines, err = generate_interface(struct_name, methods)
if err then
vim.notify(err, vim.log.levels.WARN)
return
end
-- Insert interface above the struct definition
local cursor_line = vim.api.nvim_win_get_cursor(0)[1]
vim.api.nvim_buf_set_lines(bufnr, cursor_line - 1, cursor_line - 1, false, interface_lines)
vim.api.nvim_buf_set_lines(bufnr, cursor_line, cursor_line, false, { "" }) -- Empty line
vim.notify("Interface extracted: " .. #methods .. " methods found", vim.log.levels.INFO)
end
-- Setup function for lazy.nvim
function M.setup()
-- Create command
vim.api.nvim_create_user_command("GoExtractInterface", M.extract_interface, {
desc = "Extract interface from Go struct under cursor",
})
-- Optional: Add keybinding
vim.keymap.set("n", "<leader>cei", M.extract_interface, {
desc = "Extract interface from struct",
silent = true,
})
end
return M