local golist = require("go.list").list local util = require("go.utils") local log = util.log local vfn = vim.fn local api = vim.api local path_to_pkg = {} local complete = function() local ok, l = golist(false, { util.all_pkgs() }) if not ok then log("Failed to find all packages for current module/project.") end local curpkgmatch = false local curpkg = vfn.fnamemodify(vfn.expand("%"), ":h:.") local pkgs = {} for _, p in ipairs(l) do local d = vfn.fnamemodify(p.Dir, ":.") if curpkg ~= d then if d ~= vfn.getcwd() then table.insert(pkgs, util.relative_to_cwd(d)) end else curpkgmatch = true end end table.sort(pkgs) table.insert(pkgs, util.all_pkgs()) table.insert(pkgs, ".") if curpkgmatch then table.insert(pkgs, util.relative_to_cwd(curpkg)) end return table.concat(pkgs, "\n") end local all_pkgs = function() local ok, l = golist(false, { util.all_pkgs() }) if not ok then log("Failed to find all packages for current module/project.") end return l end local pkg_from_path = function(pkg, bufnr) local cmd = { "go", "list" } if pkg ~= nil then table.insert(cmd, pkg) end log(cmd) return util.exec_in_path(cmd, bufnr) end local show_float = function(result) local textview = util.load_plugin("guihua.lua", "guihua.textview") if not textview then util.log("Failed to load guihua.textview") vim.fn.setloclist(0, {}, " ", { title = "go package outline", lines = result, }) vim.cmd("lopen") return end local win = textview:new({ relative = "cursor", syntax = "lua", rect = { height = math.min(40, #result), pos_x = 0, pos_y = 10 }, data = result, }) log("draw data", result) vim.api.nvim_buf_set_option(win.buf, "filetype", "go") return win:on_draw(result) end local defs local render_outline = function(result) if not result then log("result nil", debug.traceback()) return end local fname = vim.fn.tempname() .. "._go" -- avoid lsp activation log("tmp:" .. fname) local uri = vim.uri_from_fname(fname) local bufnr = vim.uri_to_bufnr(uri) vim.fn.writefile(result, fname) vfn.bufload(bufnr) defs = require("go.ts.utils").list_definitions_toc(bufnr) return bufnr, fname end local outline local render local show_panel = function(result, pkg, rerender) local bufnr, fname = render_outline(result) if rerender then return true -- just re-gen the outline end log("defs 1", defs[1]) local panel = util.load_plugin("guihua.lua", "guihua.panel") local pkg_name = pkg or "pkg" pkg_name = vfn.split(pkg_name, "/") pkg_name = pkg_name[#pkg_name] or "pkg" log("create panel") if panel then local p = panel:new({ header = "   " .. pkg_name .. "  ", render = function(b) log("render for ", bufnr, b) log(debug.traceback()) -- outline("-r") render() return defs end, on_confirm = function(n) log("on_confirm symbol ", n) if not n or not n.symbol then log("info missing: symbol ", n) return end -- need to change to main window first to enable gopls local wins = api.nvim_list_wins() local panel_win = api.nvim_get_current_win() log(wins, panel_win) local cur_win for _, w in ipairs(wins) do if w ~= panel_win then api.nvim_set_current_win(w) local cur = api.nvim_win_get_cursor(w) api.nvim_win_set_cursor(w, cur) cur_win = w break end end vim.lsp.buf_request(0, "workspace/symbol", { query = "'" .. n.symbol }, function(e, result, ctx) local filtered = {} for _, r in pairs(result) do local container = r.containerName if pkg == container and r.name == n.symbol then table.insert(filtered, r) end end log("filtered", filtered) if #filtered == 0 then log("nothing found fallback to result", pkg, n.symbol) filtered = result end if vfn.empty(filtered) == 1 then log(e, result, ctx) vim.notify("no symbol found for " .. vim.inspect(pkg)) return false end if #filtered == 1 then -- jump to pos local loc = filtered[1].location local buf = vim.uri_to_bufnr(loc.uri) vfn.bufload(buf) api.nvim_set_current_win(cur_win) api.nvim_set_current_buf(buf) api.nvim_win_set_buf(cur_win, buf) api.nvim_win_set_cursor(cur_win, { loc.range.start.line + 1, loc.range.start.character }) else -- lets just call workspace/symbol handler vim.lsp.handlers["workspace/symbol"](e, filtered, ctx) end end) -- vim.lsp.buf.workspace_symbol("'" .. n.symbol) return n.symbol end, }) p:open(true) else vim.fn.setloclist(0, {}, " ", { title = "go package outline", lines = defs, }) vim.cmd("lopen") end log("cleanup") vim.api.nvim_buf_delete(bufnr, { unload = true }) os.remove(fname) end local pkg_info = {} -- get package info local function handle_data_out(_, data, ev) data = util.handle_job_data(data) if not data then return end local types = { "CONSTANTS", "FUNCTIONS", "TYPES", "VARIABLES" } for i, val in ipairs(data) do -- first strip the filename if vim.tbl_contains(types, val) then val = "//" .. val end local sp = string.match(val, "^(%s*)") if sp and #sp == 4 then val = "//" .. val end local f = string.match(val, "^func ") if f then -- incase the func def is mulilines local next_line = data[i + 1] if next_line then local next_sp = string.match(next_line, "^(%s*)") -- one tab in front if next_sp and #next_sp == 1 then -- tab size 1 next_line = next_line .. "{}" data[i + 1] = next_line else val = val .. "{}" end else val = val .. "{}" end end -- log(val) table.insert(pkg_info, val) end end local gen_pkg_info = function(cmd, pkg, arg, rerender) log("gen_pkg_info", cmd, pkg, rerender) vfn.jobstart(cmd, { on_stdout = handle_data_out, on_exit = function(e, data, _) if data ~= 0 then log("no packege info data " .. e .. tostring(data)) return end if arg == "-f" then return show_float(pkg_info) end show_panel(pkg_info, pkg[1], rerender) end, }) end outline = function(...) log(debug.traceback()) local arg = select(1, ...) local path = vim.fn.expand("%:p:h") path = vfn.fnamemodify(path, ":p") if arg == "-p" then path = select(2, ...) else path = "." .. util.sep() .. "..." -- how about window? end local re_render = false if arg == "-r" then re_render = true end local pkg = path_to_pkg[path] if not pkg then pkg = pkg_from_path(path) -- return list of all packages only check first one path_to_pkg[path] = pkg end if vfn.empty(pkg) == 1 then util.log("No package found in current directory.") return nil end local setup = { "go", "doc", "-all", "-u", "-cmd", pkg[1] } gen_pkg_info(setup, pkg, arg, re_render) end render = function(bufnr) log(debug.traceback()) local fpath = vfn.fnamemodify(vfn.bufname(bufnr or 0), ":p") local pkg = path_to_pkg[fpath] if not pkg then pkg = pkg_from_path("." .. util.sep() .. "...", bufnr) -- return list of all packages only check first one path_to_pkg[fpath] = pkg end if vfn.empty(pkg) == 1 then util.log("No package found in current directory.") return nil end local cmd = { "go", "doc", "-all", "-u", "-cmd", pkg[1] } log("gen_pkg_info", cmd, pkg) vfn.jobstart(cmd, { on_stdout = handle_data_out, on_exit = function(e, data, _) if data ~= 0 then log("no packege info data " .. e .. tostring(data)) return end bufnr, fname = render_outline(result) log(bufnr, fname) end, }) return defs end return { complete = complete, all_pkgs = all_pkgs, pkg_from_path = pkg_from_path, outline = outline, } --[[ result of workspacesymbol { { containerName = "github.com/vendor/packagename/internal/aws", kind = 12, location = { range = { end = { character = 23, line = 39 }, start = { character = 5, line = 39 } }, uri = "file:///go_home/src/vendor/packagename/internal/aws/aws.go" }, name = "S3EndpointResolver" } } ]]