From ec91d9915f76b24d564c798ced961c5f50eecc5c Mon Sep 17 00:00:00 2001 From: ray-x Date: Sat, 13 Nov 2021 14:29:42 +1100 Subject: [PATCH] Add command autocomplete --- lua/go.lua | 22 +++++--- lua/go/asyncmake.lua | 3 +- lua/go/godoc.lua | 122 +++++++++++++++++++++++++++++++++++++++++-- lua/go/gopls.lua | 13 +++++ lua/go/list.lua | 23 ++++++++ lua/go/package.lua | 31 +++++++++++ lua/go/runner.lua | 9 +++- lua/go/utils.lua | 32 +++++++++++- 8 files changed, 241 insertions(+), 14 deletions(-) create mode 100644 lua/go/list.lua create mode 100644 lua/go/package.lua diff --git a/lua/go.lua b/lua/go.lua index b43f2c5..fec9db4 100644 --- a/lua/go.lua +++ b/lua/go.lua @@ -65,19 +65,21 @@ function go.setup(cfg) vim.cmd([[command! -nargs=* GoImport lua require("go.format").goimport()]]) vim.cmd([[command! GoGenerate :setl makeprg=go\ generate | :GoMake]]) - vim.cmd([[command! -nargs=* GoBuild :setl makeprg=go\ build | lua require'go.asyncmake'.make()]]) - vim.cmd([[command! -nargs=* GoRun :setl makeprg=go\ run | lua require'go.asyncmake'.make()]]) + vim.cmd( + [[command! -nargs=* -complete=custom,v:lua.package.loaded.go.package_complete GoBuild :setl makeprg=go\ build | lua require'go.asyncmake'.make()]]) + vim.cmd( + [[command! -nargs=* -complete=custom,v:lua.package.loaded.go.package_complete GoRun :setl makeprg=go\ run | lua require'go.asyncmake'.make()]]) -- if you want to output to quickfix -- vim.cmd( -- [[command! -nargs=* GoTest :setl makeprg=go\ test\ -v\ ./...| lua require'go.asyncmake'.make()]]) local sep = require('go.utils').sep() - local cmd = [[command! -nargs=* GoTest :setl makeprg=go\ test\ -v\ .]] .. sep - .. [[...| lua require'go.runner'.make()]] -- example to running test in split buffer - vim.cmd(cmd) + vim.cmd( + [[command! -nargs=* -complete=custom,v:lua.package.loaded.go.package_complete GoTest :setl makeprg=go\ test\ -v\ | lua require'go.runner'.make()]]) - vim.cmd([[command! -nargs=* GoCoverage lua require'go.coverage'.run()]]) + vim.cmd( + [[command! -nargs=* -complete=custom,v:lua.package.loaded.go.package_complete GoCoverage lua require'go.coverage'.run()]]) -- vim.cmd([[command! GoTestCompile :setl makeprg=go\ build | :GoMake]]) vim.cmd([[command! GoLint :setl makeprg=golangci-lint\ run\ --out-format\ tab | :GoMake]]) @@ -96,6 +98,9 @@ function go.setup(cfg) vim.cmd([[command! -nargs=* GoRmTag lua require("go.tags").rm()]]) vim.cmd([[command! -nargs=* GoImpl lua require("go.impl").run()]]) vim.cmd([[command! -nargs=* GoDoc lua require("go.godoc").run()]]) + + vim.cmd( + [[command! -nargs=+ -complete=custom,v:lua.package.loaded.go.doc_complete GoDoc lua require'go.godoc'.run('doc', {})]]) vim.cmd([[command! GoClearTag lua require("go.tags").clear()]]) vim.cmd([[command! GoCmt lua require("go.comment").gen()]]) vim.cmd([[command! GoRename lua require("go.rename").run()]]) @@ -110,6 +115,8 @@ function go.setup(cfg) vim.cmd("au FileType go au QuickFixCmdPost [^l]* nested cwindow") vim.cmd("au FileType go au QuickFixCmdPost l* nested lwindow") + vim.cmd([[command! -bang GoModTidy lua require"go.gopls".tidy()]]) + if _GO_NVIM_CFG.dap_debug then dap_config() vim.cmd([[command! -nargs=* GoDebug lua require"go.dap".run()]]) @@ -140,4 +147,7 @@ function go.setup(cfg) vim.cmd([[command! Gofmt echo 'use GoFmt']]) vim.cmd([[command! -nargs=* Goimport echo 'use GoImport']]) end + +go.doc_complete = require'go.godoc'.doc_complete +go.package_complete = require'go.package'.complete return go diff --git a/lua/go/asyncmake.lua b/lua/go/asyncmake.lua index a8a90eb..b4194b4 100644 --- a/lua/go/asyncmake.lua +++ b/lua/go/asyncmake.lua @@ -36,6 +36,7 @@ function M.make(...) arg = arg .. ' ' .. v end + log(makeprg, args) if #arg then makeprg = makeprg .. arg @@ -61,7 +62,7 @@ function M.make(...) }) vim.api.nvim_command("doautocmd QuickFixCmdPost") end - if #lines > 1 then + if lines and #lines > 1 then vim.cmd("copen") end end diff --git a/lua/go/godoc.lua b/lua/go/godoc.lua index da49671..e36cb26 100644 --- a/lua/go/godoc.lua +++ b/lua/go/godoc.lua @@ -1,10 +1,124 @@ local utils = require("go.utils") +local log = utils.log +local gopls = require('go.gopls') +local help_items = {} +local m = {} +function m.help_complete(arglead, cmdline, cursorpos) + if #help_items < 1 then + local doc = vim.fn.systemlist('go help') + if vim.v.shell_error ~= 0 then + print('failed to run go help ', vim.v.shell_error) + return + end + + for _, line in ipairs(doc) do + local m = string.match(line, '^%s+([%w-]+)') + if m ~= nil and m ~= 'go' then + table.insert(help_items, m) + end + end + table.sort(help_items) + end + return table.concat(help_items, '\n') +end + +local function match_doc_flag(lead) + local doc_flags = {'-all', '-c', '-cmd', '-short', '-src', '-u'} + + local items = {} + local p = string.format('^%s', lead) + for _, f in ipairs(doc_flags) do + local k = string.match(f, p) + log(k, f, p) + if k then + table.insert(items, f) + end + end + table.sort(items) + log(items) + + return table.concat(items or {}, '\n') +end + +local function match_partial_item_name(pkg, pattern) + local cmd = string.format('go doc %s', pkg) + local doc = vim.fn.systemlist(cmd) + if vim.v.shell_error ~= 0 then + return + end + + local items = {} + for _, _type in ipairs {'var', 'const', 'func', 'type'} do + local patterns = { + string.format('^%%s*%s (%s%%w+)', _type, pattern), + string.format('^%%s*%s %%(.-%%) (%s%%w+)', _type, pattern) + } + log(patterns) + for _, line in ipairs(doc) do + local k + for _, pat in ipairs(patterns) do + k = string.match(line, pat) + if k then + log(k) + table.insert(items, k) + break + end + end + end + end + table.sort(items) + log(items) + return items +end + +function m.doc_complete(arglead, cmdline, cursorpos) + local words = vim.split(cmdline, '%s+') + if string.match(words[#words], '^-') then + log(words) + return match_doc_flag(words[#words]) + end + + if #words > 2 and string.match(words[#words - 1], '^-') == nil then + local pkg = words[#words - 1] + local item = words[#words] + return table.concat(match_partial_item_name(pkg, item), '\n') + elseif #words > 1 and string.match(words[#words], '^[^-].+%..*') ~= nil then + local pkg, item, method = unpack(vim.split(words[#words], '%.')) + if method then + pkg = string.format('%s.%s', pkg, item) + item = method + end + local comps = match_partial_item_name(pkg, item) + for i, comp in ipairs(comps or {}) do + comps[i] = string.format('%s.%s', pkg, comp) + end + return table.concat(comps or {}, '\n') + elseif #words >= 1 and not string.match(words[#words], '^-') then + local pkgs = gopls.list_pkgs() + if pkgs then + local match = {} + log(pkgs) + if #words > 1 and #words[#words] > 0 then + for _, value in ipairs(pkgs) do + if string.match(value, words[#words]) then + table.insert(match, value) + end + end + else + match = pkgs + end + return table.concat(match or {}, '\n') + end + end + return '' +end -local run = function(func, ...) +m.run = function(kind, func, ...) + log(func) - vim.validate({func = {func, 'string'}}) + vim.validate({func = {func, 'table'}}) - local setup = {'go', 'doc', vim.trim(func)} + local setup = {'go', kind, unpack(func)} -- local j = vim.fn.jobstart(setup, { on_stdout = function(jobid, data, event) @@ -19,4 +133,4 @@ local run = function(func, ...) end }) end -return {run = run} +return m diff --git a/lua/go/gopls.lua b/lua/go/gopls.lua index e05b99d..bc07f24 100644 --- a/lua/go/gopls.lua +++ b/lua/go/gopls.lua @@ -43,6 +43,19 @@ for _, value in ipairs(gopls_cmds) do end end +M.list_pkgs = function() + local resp = M.list_known_packages() + + local pkgs = {} + for _, response in pairs(resp) do + if response.result ~= nil then + pkgs = response.result.Packages + break + end + end + return pkgs +end + -- check_for_upgrades({Modules = {'package'}}) return M diff --git a/lua/go/list.lua b/lua/go/list.lua new file mode 100644 index 0000000..cee5946 --- /dev/null +++ b/lua/go/list.lua @@ -0,0 +1,23 @@ +local M = {} +function M.list(mod, args) + local cmd = {'go', 'list', '-json'} + + local out + if mod == false then + table.insert(cmd, 1, 'GO111MODULE=off') + end + + vim.list_extend(cmd, args or {'.'}) + out = vim.fn.systemlist(table.concat(cmd, ' ')) + if vim.v.shell_error ~= 0 then + return false + end + for i, e in ipairs(out) do + if e == '}' and out[i + 1] == '{' then + out[i] = '},' + end + end + return true, vim.json.decode('[' .. table.concat(out, '') .. ']') +end + +return M diff --git a/lua/go/package.lua b/lua/go/package.lua new file mode 100644 index 0000000..05d1c37 --- /dev/null +++ b/lua/go/package.lua @@ -0,0 +1,31 @@ +local golist = require'go.list'.list +local util = require 'go.utils' +local log = util.log +return { + 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 = vim.fn.fnamemodify(vim.fn.expand('%'), ':h:.') + local pkgs = {} + for _, p in ipairs(l) do + local d = vim.fn.fnamemodify(p.Dir, ':.') + if curpkg ~= d then + if d ~= vim.fn.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 +} diff --git a/lua/go/runner.lua b/lua/go/runner.lua index 92c8eed..ff73b3e 100644 --- a/lua/go/runner.lua +++ b/lua/go/runner.lua @@ -1,4 +1,5 @@ local uv, api = vim.loop, vim.api +local util = require('go.utils') local log = require('go.utils').log local check_same = function(tbl1, tbl2) if #tbl1 ~= #tbl2 then @@ -24,6 +25,11 @@ local run = function(cmd, opts) local job_options = vim.deepcopy(opts or {}) job_options.args = job_options.args or {} local cmdargs = vim.list_slice(cmd, 2, #cmd) or {} + + if cmdargs and cmdargs[1] == 'test' and #cmdargs == 3 then + table.insert(cmdargs, '.' .. util.sep() .. '...') + log(cmdargs) + end vim.list_extend(cmdargs, job_options.args) job_options.args = cmdargs @@ -95,9 +101,8 @@ end local function make(...) local makeprg = vim.api.nvim_buf_get_option(0, "makeprg") local args = {...} - local setup + local setup = {} if #args > 0 then - setup = {'-tags'} for i, v in ipairs(args) do table.insert(setup, v) end diff --git a/lua/go/utils.lua b/lua/go/utils.lua index 5f6d44a..f8c2e12 100644 --- a/lua/go/utils.lua +++ b/lua/go/utils.lua @@ -17,7 +17,6 @@ function util.sep() return '/' end - local function smartrun() if has_main() then -- Found main function in current buffer @@ -48,6 +47,24 @@ util.check_same = function(tbl1, tbl2) return true end +util.map = function(modes, key, result, options) + options = M.merge({noremap = true, silent = false, expr = false, nowait = false}, options or {}) + local buffer = options.buffer + options.buffer = nil + + if type(modes) ~= "table" then + modes = {modes} + end + + for i = 1, #modes do + if buffer then + vim.api.nvim_buf_set_keymap(0, modes[i], key, result, options) + else + vim.api.nvim_set_keymap(modes[i], key, result, options) + end + end +end + util.copy_array = function(from, to) for i = 1, #from do to[i] = from[i] @@ -253,4 +270,17 @@ function util.check_capabilities(feature, client_id) end end +function util.relative_to_cwd(name) + local rel = vim.fn.isdirectory(name) == 0 and vim.fn.fnamemodify(name, ':h:.') or vim.fn.fnamemodify(name, ':.') + if rel == '.' then + return '.' + else + return '.' .. util.sep() .. rel + end +end + +function util.all_pkgs() + return '.' .. util.sep() .. '...' +end + return util