@@ -47,7 +47,12 @@ ~/go/pkg/mod
key: ${{ runner.os }}-tests-${{ hashFiles('${{ github.workspace }}/.tests') }} - name: Install Go bins - run: nvim --headless -u "./scripts/minimal_init.lua" -c "GoInstallDeps" -c "qa!" + run: | + # TODO: install with :GoInstallDeps + go install github.com/fatih/gomodifytags@latest + go install github.com/josharian/impl@latest + go install github.com/cweill/gotests/...@latest + go install github.com/koron/iferr@latest - name: Run Tests run: |
@@ -13,17 +13,15 @@
Requirements: - **Neovim 0.10** or later -- `go` treesitter parser, install by `:TSInstall go` +- Treesitter `go` parser(`:TSInstall go`) - [Go](https://github.com/golang/go) installed (tested on 1.23) ```lua { "olexsmir/gopher.nvim", ft = "go", - -- branch = "develop", -- if you want develop branch - -- keep in mind, it might break everything + -- branch = "develop" dependencies = { - "nvim-lua/plenary.nvim", "nvim-treesitter/nvim-treesitter", }, -- (optional) will update plugin's deps on every update
@@ -3,8 +3,9 @@ local log = require "gopher._utils.log"
local utils = {} ---@param msg string ----@param lvl number +---@param lvl? number function utils.deferred_notify(msg, lvl) + lvl = lvl or vim.log.levels.INFO vim.defer_fn(function() vim.notify(msg, lvl, { title = c.___plugin_name,@@ -21,6 +22,24 @@ vim.notify(msg, lvl, {
title = c.___plugin_name, }) log.debug(msg) +end + +---@param path string +---@return string +function utils.readfile_joined(path) + return table.concat(vim.fn.readfile(path), "\n") +end + +---@param t string[] +---@return string[] +function utils.remove_empty_lines(t) + local res = {} + for _, line in ipairs(t) do + if line ~= "" then + table.insert(res, line) + end + end + return res end return utils
@@ -1,33 +1,39 @@
-local Job = require "plenary.job" +local c = require "gopher.config" local runner = {} ---@class gopher.RunnerOpts ----@field args? string[] ----@field cwd? string? ----@field on_exit? fun(data:string, status:number) +---@field cwd? string +---@field timeout? number +---@field stdin? boolean|string|string[] +---@field text? boolean ----@param cmd string ----@param opts gopher.RunnerOpts ----@return string[]|nil +---@param cmd (string|number)[] +---@param on_exit fun(out:vim.SystemCompleted) +---@param opts? gopher.RunnerOpts +---@return vim.SystemObj +function runner.async(cmd, on_exit, opts) + opts = opts or {} + return vim.system(cmd, { + cwd = opts.cwd or nil, + timeout = opts.timeout or c.timeout, + stdin = opts.stdin or nil, + text = opts.text or true, + }, on_exit) +end + +---@param cmd (string|number)[] +---@param opts? gopher.RunnerOpts +---@return vim.SystemCompleted function runner.sync(cmd, opts) - local output - Job:new({ - command = cmd, - args = opts.args, - cwd = opts.cwd, - on_stderr = function(_, data) - vim.print(data) - end, - on_exit = function(data, status) - output = data:result() - vim.schedule(function() - if opts.on_exit then - opts.on_exit(output, status) - end - end) - end, - }):sync(60000 --[[1 min]]) - return output + opts = opts or {} + return vim + .system(cmd, { + cwd = opts.cwd or nil, + timeout = opts.timeout or c.timeout, + stdin = opts.stdin or nil, + text = opts.text or true, + }) + :wait() end return runner
@@ -33,6 +33,10 @@ -- log level, you might consider using DEBUG or TRACE for debugging the plugin
---@type number log_level = vim.log.levels.INFO, + -- timeout for running commands + ---@type number + timeout = 2000, + -- user specified paths to binaries ---@class gopher.ConfigCommand commands = {
@@ -67,16 +67,12 @@ table.insert(args, vim.fn.expand "%")
log.debug("generating tests with args: ", args) - return r.sync(c.commands.gotests, { - args = args, - on_exit = function(data, status) - if not status == 0 then - error("gotests failed: " .. data) - end + local rs = r.sync { c.commands.gotests, unpack(args) } + if rs.code ~= 0 then + error("gotests failed: " .. rs.stderr) + end - u.notify "unit test(s) generated" - end, - }) + u.notify "unit test(s) generated" end -- generate unit test for one function
@@ -4,9 +4,7 @@ local u = require "gopher._utils.health_util"
local deps = { plugin = { - { lib = "dap", msg = "required for `gopher.dap`", optional = true }, - { lib = "plenary", msg = "required for everything in gopher.nvim", optional = false }, - { lib = "nvim-treesitter", msg = "required for everything in gopher.nvim", optional = false }, + { lib = "nvim-treesitter", msg = "required for everything in gopher.nvim" }, }, bin = { {@@ -14,17 +12,17 @@ bin = cmd.go,
msg = "required for `:GoGet`, `:GoMod`, `:GoGenerate`, `:GoWork`, `:GoInstallDeps`", optional = false, }, - { bin = cmd.gomodifytags, msg = "required for `:GoTagAdd`, `:GoTagRm`", optional = false }, - { bin = cmd.impl, msg = "required for `:GoImpl`", optional = false }, - { bin = cmd.iferr, msg = "required for `:GoIfErr`", optional = false }, + { bin = cmd.gomodifytags, msg = "required for `:GoTagAdd`, `:GoTagRm`", optional = true }, + { bin = cmd.impl, msg = "required for `:GoImpl`", optional = true }, + { bin = cmd.iferr, msg = "required for `:GoIfErr`", optional = true }, { bin = cmd.gotests, msg = "required for `:GoTestAdd`, `:GoTestsAll`, `:GoTestsExp`", - optional = false, + optional = true, }, }, treesitter = { - { parser = "go", msg = "required for `gopher.nvim`", optional = false }, + { parser = "go", msg = "required for `gopher.nvim`" }, }, }@@ -34,11 +32,7 @@ for _, plugin in ipairs(deps.plugin) do
if u.is_lualib_found(plugin.lib) then u.ok(plugin.lib .. " installed") else - if plugin.optional then - u.warn(plugin.lib .. " not found, " .. plugin.msg) - else - u.error(plugin.lib .. " not found, " .. plugin.msg) - end + u.error(plugin.lib .. " not found, " .. plugin.msg) end end
@@ -4,27 +4,33 @@ ---@text If you're using `iferr` tool, this module provides a way to automatically insert `if err != nil` check.
---@usage Execute `:GoIfErr` near any `err` variable to insert the check local c = require "gopher.config" +local u = require "gopher._utils" +local r = require "gopher._utils.runner" local log = require "gopher._utils.log" local iferr = {} --- That's Lua implementation: github.com/koron/iferr +-- That's Lua implementation: https://github.com/koron/iferr function iferr.iferr() - local boff = vim.fn.wordcount().cursor_bytes + local curb = vim.fn.wordcount().cursor_bytes local pos = vim.fn.getcurpos()[2] + local fpath = vim.fn.expand "%" - local data = vim.fn.systemlist((c.commands.iferr .. " -pos " .. boff), vim.fn.bufnr "%") - if vim.v.shell_error ~= 0 then - if string.find(data[1], "no functions at") then - vim.print "no function found" - log.warn("iferr: no function at " .. boff) + local rs = r.sync({ c.commands.iferr, "-pos", curb }, { + stdin = u.readfile_joined(fpath), + }) + + if rs.code ~= 0 then + if string.find(rs.stderr, "no functions at") then + u.notify("iferr: no function at " .. curb, vim.log.levels.ERROR) + log.warn("iferr: no function at " .. curb) return end - log.error("failed. output: " .. vim.inspect(data)) - error("iferr failed: " .. vim.inspect(data)) + log.error("ferr: failed. output: " .. rs.stderr) + error("iferr failed: " .. rs.stderr) end - vim.fn.append(pos, data) + vim.fn.append(pos, u.remove_empty_lines(vim.split(rs.stdout, "\n"))) vim.cmd [[silent normal! j=2j]] vim.fn.setpos(".", pos) end
@@ -43,7 +43,7 @@ ---@private
local function get_struct() local ns = ts_utils.get_struct_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0))) if ns == nil then - u.deferred_notify("put cursor on a struct or specify a receiver", vim.log.levels.INFO) + u.notify "put cursor on a struct or specify a receiver" return "" end@@ -82,21 +82,14 @@ recv_name = select(#args - 2, ...)
recv = string.format("%s %s", recv_name, recv) end - local output = r.sync(c.impl, { - args = { - "-dir", - vim.fn.fnameescape(vim.fn.expand "%:p:h" --[[@as string]]), - recv, - iface, - }, - on_exit = function(data, status) - if not status == 0 then - error("impl failed: " .. data) - end - end, - }) + local rs = r.sync { c.impl, "-dir", vim.fn.fnameescape(vim.fn.expand "%:p:h"), recv, iface } + if rs.code ~= 0 then + error("failed to implement interface: " .. rs.stderr) + end local pos = vim.fn.getcurpos()[2] + local output = u.remove_empty_lines(vim.split(rs.stdout, "\n")) + table.insert(output, 1, "") vim.fn.append(pos, output) end
@@ -1,6 +1,7 @@
local c = require("gopher.config").commands local r = require "gopher._utils.runner" local u = require "gopher._utils" +local log = require "gopher._utils.log" local installer = {} local urls = {@@ -10,25 +11,42 @@ gotests = "github.com/cweill/gotests/...",
iferr = "github.com/koron/iferr", } ----@param pkg string -local function install(pkg) - local url = urls[pkg] .. "@latest" - r.sync(c.go, { - args = { "install", url }, - on_exit = function(data, status) - if not status == 0 then - error("go install failed: " .. data) - return - end - u.notify("installed: " .. url) - end, - }) +---@param opt vim.SystemCompleted +---@param url string +local function handle_intall_exit(opt, url) + if opt.code ~= 0 then + u.deferred_notify("go install failed: " .. url) + log.error("go install failed:", "url", url, "opt", vim.inspect(opt)) + return + end + + u.deferred_notify("go install-ed: " .. url) +end + +---@param url string +local function install(url) + r.async({ c.go, "install", url }, function(opt) + handle_intall_exit(opt, url) + end) +end + +---@param url string +local function install_sync(url) + local rs = r.sync { c.go, "install", url } + handle_intall_exit(rs, url) end ---Install required go deps -function installer.install_deps() +---@param opts? {sync:boolean} +function installer.install_deps(opts) + opts = opts or {} for pkg, _ in pairs(urls) do - install(pkg) + local url = urls[pkg] .. "@latest" + if opts.sync then + install_sync(url) + else + install(url) + end end end
@@ -11,4 +11,5 @@ command! -nargs=* GoGenerate :lua require"gopher".generate(<f-args>)
command! GoCmt :lua require"gopher".comment() command! GoIfErr :lua require"gopher".iferr() command! GoInstallDeps :lua require"gopher".install_deps() +command! GoInstallDepsSync :lua require"gopher".install_deps({ sync = true }) command! GopherLog :lua vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile())
@@ -8,17 +8,23 @@ local name = plugin:match ".*/(.*)"
local package_root = root ".tests/site/pack/deps/start/" if not vim.uv.fs_stat(package_root .. name) then print("Installing " .. plugin) - vim.fn.mkdir(package_root, "p") - vim.fn.system { - "git", - "clone", - "--depth=1", - "https://github.com/" .. plugin .. ".git", - package_root .. "/" .. name, - } + vim + .system({ + "git", + "clone", + "--depth=1", + "https://github.com/" .. plugin .. ".git", + package_root .. "/" .. name, + }) + :wait() end end +install_plug "nvim-lua/plenary.nvim" +install_plug "nvim-treesitter/nvim-treesitter" +install_plug "echasnovski/mini.doc" -- used for docs generation +install_plug "echasnovski/mini.test" + vim.env.XDG_CONFIG_HOME = root ".tests/config" vim.env.XDG_DATA_HOME = root ".tests/data" vim.env.XDG_STATE_HOME = root ".tests/state"@@ -27,15 +33,15 @@
vim.cmd [[set runtimepath=$VIMRUNTIME]] vim.opt.runtimepath:append(root()) vim.opt.packpath = { root ".tests/site" } -vim.notify = print - -install_plug "nvim-lua/plenary.nvim" -install_plug "nvim-treesitter/nvim-treesitter" -install_plug "echasnovski/mini.doc" -- used for docs generation -install_plug "echasnovski/mini.test" +vim.notify = vim.print -- install go treesitter parse require("nvim-treesitter.install").ensure_installed_sync "go" + +require("gopher").setup { + log_level = vim.log.levels.OFF, + timeout = 4000, +} -- setup mini.test only when running headless nvim if #vim.api.nvim_list_uis() == 0 then
@@ -5,4 +5,3 @@
func (closertest *CloserTest2) Close() error { panic("not implemented") // TODO: Implement } -
@@ -4,5 +4,4 @@ func (r Read2) Read(p []byte) (n int, err error) {
panic("not implemented") // TODO: Implement } - type Read2 struct{}
@@ -5,4 +5,3 @@
func (w *WriterTest2) Write(p []byte) (n int, err error) { panic("not implemented") // TODO: Implement } -