From da960189c137411557749bd2ef075fe57c7b1e64 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Mon, 24 Feb 2025 14:21:01 +0200 Subject: [PATCH 01/25] tests: improve testing (#80) * chore: setup mini.test * chore(ci): setup new test runner, install plugin deps * chore(ci): test only on stable and nightly releases * test: iferr * test: struct_tags * test: impl * test: gotests --- .envrc | 3 ++ .github/workflows/linters.yml | 8 +++- .github/workflows/tests.yml | 56 +++++++++++++++---------- .gitignore | 1 + README.md | 5 ++- Taskfile.yml | 12 ++---- doc/gopher.nvim.txt | 3 -- lua/gopher/_utils/init.lua | 8 ---- lua/gopher/config.lua | 2 +- nvim.toml | 3 ++ scripts/minimal_init.lua | 25 ++++++++--- spec/fixtures/comment/package_input.go | 1 + spec/fixtures/comment/package_output.go | 2 + spec/fixtures/iferr/iferr_input.go | 9 ++++ spec/fixtures/iferr/iferr_output.go | 12 ++++++ spec/fixtures/impl/closer_input.go | 3 ++ spec/fixtures/impl/closer_output.go | 8 ++++ spec/fixtures/impl/reader_input.go | 3 ++ spec/fixtures/impl/reader_output.go | 8 ++++ spec/fixtures/impl/writer_input.go | 3 ++ spec/fixtures/impl/writer_output.go | 8 ++++ spec/fixtures/tags/remove_output.go | 12 +++--- spec/fixtures/tests/function_input.go | 5 +++ spec/fixtures/tests/function_output.go | 24 +++++++++++ spec/fixtures/tests/method_input.go | 7 ++++ spec/fixtures/tests/method_output.go | 26 ++++++++++++ spec/integration/comment_test.lua | 27 ++++++++++++ spec/integration/gotests_test.lua | 47 +++++++++++++++++++++ spec/integration/iferr_test.lua | 26 ++++++++++++ spec/integration/impl_test.lua | 55 ++++++++++++++++++++++++ spec/integration/struct_tags_test.lua | 37 ++++++++++++++++ spec/testutils.lua | 43 +++++++++++++++++++ spec/units/config_spec.lua | 29 ------------- spec/units/utils_spec.lua | 15 ------- 34 files changed, 435 insertions(+), 101 deletions(-) create mode 100644 .envrc create mode 100644 spec/fixtures/comment/package_input.go create mode 100644 spec/fixtures/comment/package_output.go create mode 100644 spec/fixtures/iferr/iferr_input.go create mode 100644 spec/fixtures/iferr/iferr_output.go create mode 100644 spec/fixtures/impl/closer_input.go create mode 100644 spec/fixtures/impl/closer_output.go create mode 100644 spec/fixtures/impl/reader_input.go create mode 100644 spec/fixtures/impl/reader_output.go create mode 100644 spec/fixtures/impl/writer_input.go create mode 100644 spec/fixtures/impl/writer_output.go create mode 100644 spec/fixtures/tests/function_input.go create mode 100644 spec/fixtures/tests/function_output.go create mode 100644 spec/fixtures/tests/method_input.go create mode 100644 spec/fixtures/tests/method_output.go create mode 100644 spec/integration/comment_test.lua create mode 100644 spec/integration/gotests_test.lua create mode 100644 spec/integration/iferr_test.lua create mode 100644 spec/integration/impl_test.lua create mode 100644 spec/integration/struct_tags_test.lua create mode 100644 spec/testutils.lua delete mode 100644 spec/units/config_spec.lua delete mode 100644 spec/units/utils_spec.lua diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..dd6cca9 --- /dev/null +++ b/.envrc @@ -0,0 +1,3 @@ +dotenv + +env_vars_required GOPHER_DIR diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index a78c3c3..389b72f 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -1,5 +1,11 @@ name: linters -on: [push, pull_request] + +on: + push: + branches: + - main + - develop + pull_request: jobs: linters: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9b16b5d..8564381 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,25 +1,20 @@ name: tests -on: [push, pull_request] + +on: + push: + branches: + - main + - develop + pull_request: jobs: tests: strategy: matrix: os: [ubuntu-latest] - nvim_version: + version: + - v0.10.4 - nightly - - v0.7.0 - - v0.7.2 - - v0.8.0 - - v0.8.1 - - v0.8.2 - - v0.8.3 - - v0.9.0 - - v0.9.1 - - v0.9.2 - - v0.9.4 - - v0.9.5 - - v0.10.0 runs-on: ${{ matrix.os }} steps: - name: Install Task @@ -28,18 +23,33 @@ jobs: version: 3.x repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: "1.24.0" + check-latest: false + + - name: Install NeoVim + uses: rhysd/action-setup-vim@v1 + with: + neovim: true + version: ${{ matrix.version }} + - uses: actions/checkout@v3 - - name: Install Neovim - run: | - mkdir -p /tmp/nvim - wget -q https://github.com/neovim/neovim/releases/download/${{ matrix.nvim_version }}/nvim.appimage -O /tmp/nvim/nvim.appimage - cd /tmp/nvim - chmod a+x ./nvim.appimage - ./nvim.appimage --appimage-extract - echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH + - name: Cache .tests + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/.tests + ~/.cache/go-build + ~/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!" - name: Run Tests run: | nvim --version - task test + task tests diff --git a/.gitignore b/.gitignore index df7d859..3a9d44e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /playground/ /.tests/ +/.env diff --git a/README.md b/README.md index d256e5f..cd66f74 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,11 @@ It's **NOT** an LSP tool, the main goal of this plugin is to add go tooling supp ## Install (using [lazy.nvim](https://github.com/folke/lazy.nvim)) -Pre-dependency: +Requirements: -- [Go](https://github.com/golang/go) +- **Neovim 0.10** or later - `go` treesitter parser, install by `:TSInstall go` +- [Go](https://github.com/golang/go) installed (tested on 1.23) ```lua { diff --git a/Taskfile.yml b/Taskfile.yml index 581f11c..c6f5339 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -26,17 +26,13 @@ tasks: cmds: - stylua . - test: - desc: runs all tests - aliases: [tests, spec] + tests: + desc: run all tests cmds: - | nvim --headless \ - -u ./scripts/minimal_init.lua \ - -c "PlenaryBustedDirectory spec \ - {minimal_init='./scripts/minimal_init.lua' \ - ,sequential=true}" \ - -c ":qa!" + -u ./scripts/minimal_init.lua \ + -c "lua MiniTest.run()" docgen: desc: generate vimhelp diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index d4a82d0..78b316d 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -116,7 +116,6 @@ simple example: } < - ============================================================================== ------------------------------------------------------------------------------ *gopher.nvim-impl* @@ -147,7 +146,6 @@ simple example: } < - ============================================================================== ------------------------------------------------------------------------------ *gopher.nvim-gotests* @@ -167,7 +165,6 @@ Usage ~ you can also specify the template to use for generating the tests. see |gopher.nvim-config| more details about templates can be found at: https://github.com/cweill/gotests - ------------------------------------------------------------------------------ *gopher.nvim-gotests-named* diff --git a/lua/gopher/_utils/init.lua b/lua/gopher/_utils/init.lua index a3b567c..96a9134 100644 --- a/lua/gopher/_utils/init.lua +++ b/lua/gopher/_utils/init.lua @@ -23,12 +23,4 @@ function utils.notify(msg, lvl) log.debug(msg) end --- safe require ----@param module string module name -function utils.sreq(module) - local ok, m = pcall(require, module) - assert(ok, string.format("gopher.nvim dependency error: %s not installed", module)) - return m -end - return utils diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index 9c20843..8265838 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -23,7 +23,7 @@ local config = {} --minidoc_replace_start { ---@tag gopher.nvim-config-defaults ----@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section):gsub(">", ">lua") +---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section) --- ---@class gopher.Config local default_config = { diff --git a/nvim.toml b/nvim.toml index fa09a88..50dc11f 100644 --- a/nvim.toml +++ b/nvim.toml @@ -1,6 +1,9 @@ [vim] any = true +[MiniTest] +any = true + [describe] any = true [[describe.args]] diff --git a/scripts/minimal_init.lua b/scripts/minimal_init.lua index 49a606e..e59f06a 100644 --- a/scripts/minimal_init.lua +++ b/scripts/minimal_init.lua @@ -6,7 +6,7 @@ end local function install_plug(plugin) local name = plugin:match ".*/(.*)" local package_root = root ".tests/site/pack/deps/start/" - if not vim.loop.fs_stat(package_root .. name) then + if not vim.uv.fs_stat(package_root .. name) then print("Installing " .. plugin) vim.fn.mkdir(package_root, "p") vim.fn.system { @@ -19,6 +19,11 @@ local function install_plug(plugin) end end +vim.env.XDG_CONFIG_HOME = root ".tests/config" +vim.env.XDG_DATA_HOME = root ".tests/data" +vim.env.XDG_STATE_HOME = root ".tests/state" +vim.env.XDG_CACHE_HOME = root ".tests/cache" + vim.cmd [[set runtimepath=$VIMRUNTIME]] vim.opt.runtimepath:append(root()) vim.opt.packpath = { root ".tests/site" } @@ -27,8 +32,18 @@ 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.env.XDG_CONFIG_HOME = root ".tests/config" -vim.env.XDG_DATA_HOME = root ".tests/data" -vim.env.XDG_STATE_HOME = root ".tests/state" -vim.env.XDG_CACHE_HOME = root ".tests/cache" +-- install go treesitter parse +require("nvim-treesitter.install").ensure_installed_sync "go" + +-- setup mini.test only when running headless nvim +if #vim.api.nvim_list_uis() == 0 then + require("mini.test").setup { + collect = { + find_files = function() + return vim.fn.globpath("spec", "**/*_test.lua", true, true) + end, + }, + } +end diff --git a/spec/fixtures/comment/package_input.go b/spec/fixtures/comment/package_input.go new file mode 100644 index 0000000..06ab7d0 --- /dev/null +++ b/spec/fixtures/comment/package_input.go @@ -0,0 +1 @@ +package main diff --git a/spec/fixtures/comment/package_output.go b/spec/fixtures/comment/package_output.go new file mode 100644 index 0000000..66d106a --- /dev/null +++ b/spec/fixtures/comment/package_output.go @@ -0,0 +1,2 @@ +// Package main provides main +package main diff --git a/spec/fixtures/iferr/iferr_input.go b/spec/fixtures/iferr/iferr_input.go new file mode 100644 index 0000000..b94f158 --- /dev/null +++ b/spec/fixtures/iferr/iferr_input.go @@ -0,0 +1,9 @@ +package main + +func test() error { + return nil +} + +func main() { + err := test() +} diff --git a/spec/fixtures/iferr/iferr_output.go b/spec/fixtures/iferr/iferr_output.go new file mode 100644 index 0000000..8ab181a --- /dev/null +++ b/spec/fixtures/iferr/iferr_output.go @@ -0,0 +1,12 @@ +package main + +func test() error { + return nil +} + +func main() { + err := test() + if err != nil { + return + } +} diff --git a/spec/fixtures/impl/closer_input.go b/spec/fixtures/impl/closer_input.go new file mode 100644 index 0000000..e4d5f52 --- /dev/null +++ b/spec/fixtures/impl/closer_input.go @@ -0,0 +1,3 @@ +package main + +type CloserTest struct{} diff --git a/spec/fixtures/impl/closer_output.go b/spec/fixtures/impl/closer_output.go new file mode 100644 index 0000000..5e976f3 --- /dev/null +++ b/spec/fixtures/impl/closer_output.go @@ -0,0 +1,8 @@ +package main + +type CloserTest2 struct{} + +func (closertest *CloserTest2) Close() error { + panic("not implemented") // TODO: Implement +} + diff --git a/spec/fixtures/impl/reader_input.go b/spec/fixtures/impl/reader_input.go new file mode 100644 index 0000000..ebc8eff --- /dev/null +++ b/spec/fixtures/impl/reader_input.go @@ -0,0 +1,3 @@ +package main + +type Read struct{} diff --git a/spec/fixtures/impl/reader_output.go b/spec/fixtures/impl/reader_output.go new file mode 100644 index 0000000..26df873 --- /dev/null +++ b/spec/fixtures/impl/reader_output.go @@ -0,0 +1,8 @@ +package main + +func (r Read2) Read(p []byte) (n int, err error) { + panic("not implemented") // TODO: Implement +} + + +type Read2 struct{} diff --git a/spec/fixtures/impl/writer_input.go b/spec/fixtures/impl/writer_input.go new file mode 100644 index 0000000..ef034cc --- /dev/null +++ b/spec/fixtures/impl/writer_input.go @@ -0,0 +1,3 @@ +package main + +type WriterTest struct{} diff --git a/spec/fixtures/impl/writer_output.go b/spec/fixtures/impl/writer_output.go new file mode 100644 index 0000000..19e8f6e --- /dev/null +++ b/spec/fixtures/impl/writer_output.go @@ -0,0 +1,8 @@ +package main + +type WriterTest2 struct{} + +func (w *WriterTest2) Write(p []byte) (n int, err error) { + panic("not implemented") // TODO: Implement +} + diff --git a/spec/fixtures/tags/remove_output.go b/spec/fixtures/tags/remove_output.go index 7e27a27..6d9fe70 100644 --- a/spec/fixtures/tags/remove_output.go +++ b/spec/fixtures/tags/remove_output.go @@ -1,11 +1,11 @@ package main type Test struct { - ID int - Name string - Num int64 + ID int + Name string + Num int64 Another struct { - First int - Second string - } + First int + Second string + } } diff --git a/spec/fixtures/tests/function_input.go b/spec/fixtures/tests/function_input.go new file mode 100644 index 0000000..bde08df --- /dev/null +++ b/spec/fixtures/tests/function_input.go @@ -0,0 +1,5 @@ +package fortest + +func Add(x, y int) int { + return 2 + x + y +} diff --git a/spec/fixtures/tests/function_output.go b/spec/fixtures/tests/function_output.go new file mode 100644 index 0000000..42ebd4c --- /dev/null +++ b/spec/fixtures/tests/function_output.go @@ -0,0 +1,24 @@ +package fortest + +import "testing" + +func TestAdd(t *testing.T) { + type args struct { + x int + y int + } + tests := []struct { + name string + args args + want int + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Add(tt.args.x, tt.args.y); got != tt.want { + t.Errorf("Add() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/spec/fixtures/tests/method_input.go b/spec/fixtures/tests/method_input.go new file mode 100644 index 0000000..fe04124 --- /dev/null +++ b/spec/fixtures/tests/method_input.go @@ -0,0 +1,7 @@ +package fortest + +type ForTest struct{} + +func (t *ForTest) Add(x, y int) int { + return 2 + x + y +} diff --git a/spec/fixtures/tests/method_output.go b/spec/fixtures/tests/method_output.go new file mode 100644 index 0000000..7f927b5 --- /dev/null +++ b/spec/fixtures/tests/method_output.go @@ -0,0 +1,26 @@ +package fortest + +import "testing" + +func TestForTest_Add(t *testing.T) { + type args struct { + x int + y int + } + tests := []struct { + name string + tr *ForTest + args args + want int + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := &ForTest{} + if got := tr.Add(tt.args.x, tt.args.y); got != tt.want { + t.Errorf("ForTest.Add() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/spec/integration/comment_test.lua b/spec/integration/comment_test.lua new file mode 100644 index 0000000..8276688 --- /dev/null +++ b/spec/integration/comment_test.lua @@ -0,0 +1,27 @@ +local t = require "spec.testutils" + +local child = MiniTest.new_child_neovim() +local T = MiniTest.new_set { + hooks = { + post_once = child.stop, + pre_case = function() + MiniTest.skip "This module should be fixed first" + child.restart { "-u", t.mininit_path } + end, + }, +} +T["comment"] = MiniTest.new_set {} + +T["comment"]["should add comment to package"] = function() end + +T["comment"]["should add comment to struct"] = function() end + +T["comment"]["should add comment to function"] = function() end + +T["comment"]["should add comment to method"] = function() end + +T["comment"]["should add comment to interface"] = function() end + +T["comment"]["otherwise should add // above cursor"] = function() end + +return T diff --git a/spec/integration/gotests_test.lua b/spec/integration/gotests_test.lua new file mode 100644 index 0000000..57f3142 --- /dev/null +++ b/spec/integration/gotests_test.lua @@ -0,0 +1,47 @@ +local t = require "spec.testutils" + +local child = MiniTest.new_child_neovim() +local T = MiniTest.new_set { + hooks = { + post_once = child.stop, + pre_case = function() + child.restart { "-u", t.mininit_path } + end, + }, +} +T["gotests"] = MiniTest.new_set {} + +--- NOTE: :GoTestAdd is the only place that has actual logic +--- All other parts are handled `gotests` itself. + +---@param fpath string +---@return string +local function read_testfile(fpath) + return t.readfile(fpath:gsub(".go", "_test.go")) +end + +T["gotests"]["should add test for function under cursor"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tests/function" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr "%", 3, 6 }) + child.cmd "GoTestAdd" + + t.eq(fixtures.output, read_testfile(tmp)) +end + +T["gotests"]["should add test for method under cursor"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tests/method" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr "%", 5, 19 }) + child.cmd "GoTestAdd" + + t.eq(fixtures.output, read_testfile(tmp)) +end + +return T diff --git a/spec/integration/iferr_test.lua b/spec/integration/iferr_test.lua new file mode 100644 index 0000000..14ee08e --- /dev/null +++ b/spec/integration/iferr_test.lua @@ -0,0 +1,26 @@ +local t = require "spec.testutils" + +local child = MiniTest.new_child_neovim() +local T = MiniTest.new_set { + hooks = { + post_once = child.stop, + pre_case = function() + child.restart { "-u", t.mininit_path } + end, + }, +} +T["iferr"] = MiniTest.new_set {} +T["iferr"]["works"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "iferr/iferr" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr "%", 8, 2, 0 }) + child.cmd "GoIfErr" + child.cmd "write" + + t.eq(t.readfile(tmp), fixtures.output) +end + +return T diff --git a/spec/integration/impl_test.lua b/spec/integration/impl_test.lua new file mode 100644 index 0000000..5376fa5 --- /dev/null +++ b/spec/integration/impl_test.lua @@ -0,0 +1,55 @@ +local t = require "spec.testutils" + +local child = MiniTest.new_child_neovim() +local T = MiniTest.new_set { + hooks = { + post_once = child.stop, + pre_case = function() + child.restart { "-u", t.mininit_path } + end, + }, +} +T["impl"] = MiniTest.new_set {} +T["impl"]["works w io.Writer"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "impl/writer" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 6 }) + child.cmd "GoImpl w io.Writer" + child.cmd "write" + + -- NOTE: since "impl" won't implement interface if it's already implemented i went with this hack + local rhs = fixtures.output:gsub("Test2", "Test") + t.eq(t.readfile(tmp), rhs) +end + +T["impl"]["works r Read io.Reader"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "impl/reader" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.cmd "GoImpl r Read io.Reader" + child.cmd "write" + + local rhs = fixtures.output:gsub("Read2", "Read") + t.eq(t.readfile(tmp), rhs) +end + +T["impl"]["works io.Closer"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "impl/closer" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 6 }) + child.cmd "GoImpl io.Closer" + child.cmd "write" + + local rhs = fixtures.output:gsub("Test2", "Test") + t.eq(t.readfile(tmp), rhs) +end + +return T diff --git a/spec/integration/struct_tags_test.lua b/spec/integration/struct_tags_test.lua new file mode 100644 index 0000000..8fb4aa3 --- /dev/null +++ b/spec/integration/struct_tags_test.lua @@ -0,0 +1,37 @@ +local t = require "spec.testutils" + +local child = MiniTest.new_child_neovim() +local T = MiniTest.new_set { + hooks = { + post_once = child.stop, + pre_case = function() + child.restart { "-u", t.mininit_path } + end, + }, +} +T["struct_tags"] = MiniTest.new_set {} +T["struct_tags"]["works add"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tags/add" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr "%", 3, 6, 0 }) + child.cmd "GoTagAdd json" + + t.eq(t.readfile(tmp), fixtures.output) +end + +T["struct_tags"]["works remove"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tags/remove" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr "%", 4, 6, 0 }) + child.cmd "GoTagRm json" + + t.eq(t.readfile(tmp), fixtures.output) +end + +return T diff --git a/spec/testutils.lua b/spec/testutils.lua new file mode 100644 index 0000000..e5ab9a2 --- /dev/null +++ b/spec/testutils.lua @@ -0,0 +1,43 @@ +local base_dir = vim.env.GOPHER_DIR or vim.fn.expand "%:p:h" + +---@class gopher.TestUtils +local testutils = {} + +testutils.mininit_path = vim.fs.joinpath(base_dir, "scripts", "minimal_init.lua") +testutils.fixtures_dir = vim.fs.joinpath(base_dir, "spec/fixtures") + +---@generic T +---@param a T +---@param b T +---@return boolean +function testutils.eq(a, b) + return MiniTest.expect.equality(a, b) +end + +---@return string +function testutils.tmpfile() + return vim.fn.tempname() .. ".go" +end + +---@param path string +---@return string +function testutils.readfile(path) + return vim.fn.join(vim.fn.readfile(path), "\n") +end + +---@param fpath string +---@param contents string +function testutils.writefile(fpath, contents) + vim.fn.writefile(vim.split(contents, "\n"), fpath) +end + +---@param fixture string +---@return {input: string, output: string} +function testutils.get_fixtures(fixture) + return { + input = testutils.readfile(vim.fs.joinpath(testutils.fixtures_dir, fixture) .. "_input.go"), + output = testutils.readfile(vim.fs.joinpath(testutils.fixtures_dir, fixture) .. "_output.go"), + } +end + +return testutils diff --git a/spec/units/config_spec.lua b/spec/units/config_spec.lua deleted file mode 100644 index 1fd91dd..0000000 --- a/spec/units/config_spec.lua +++ /dev/null @@ -1,29 +0,0 @@ -describe("gopher.config", function() - it(".setup() should provide default when .setup() is not called", function() - local c = require "gopher.config" - - assert.are.same(c.commands.go, "go") - assert.are.same(c.commands.gomodifytags, "gomodifytags") - assert.are.same(c.commands.gotests, "gotests") - assert.are.same(c.commands.impl, "impl") - assert.are.same(c.commands.iferr, "iferr") - assert.are.same(c.commands.dlv, "dlv") - end) - - it(".setup() should change options on users config", function() - local c = require "gopher.config" - c.setup { - commands = { - go = "go1.420", - gomodifytags = "iDontUseRustBtw", - }, - } - - assert.are.same(c.commands.go, "go1.420") - assert.are.same(c.commands.gomodifytags, "iDontUseRustBtw") - assert.are.same(c.commands.gotests, "gotests") - assert.are.same(c.commands.impl, "impl") - assert.are.same(c.commands.iferr, "iferr") - assert.are.same(c.commands.dlv, "dlv") - end) -end) diff --git a/spec/units/utils_spec.lua b/spec/units/utils_spec.lua deleted file mode 100644 index ea2f30c..0000000 --- a/spec/units/utils_spec.lua +++ /dev/null @@ -1,15 +0,0 @@ -describe("gopher._utils", function() - local u = require "gopher._utils" - - describe(".sreq()", function() - it("can require existing module", function() - assert.are.same(require "gopher", u.sreq "gopher") - end) - - it("cannot require non-existing module", function() - assert.has.errors(function() - u.sreq "iDontExistBtw" - end) - end) - end) -end) From cd8a5efc88360f6ece2527a89523045812fc6e12 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Tue, 25 Feb 2025 13:16:24 +0200 Subject: [PATCH 02/25] refactor!: remove dap adapter (#81) --- README.md | 16 ----- doc/gopher.nvim.txt | 11 +--- lua/gopher/dap.lua | 129 --------------------------------------- lua/gopher/health.lua | 1 - lua/gopher/installer.lua | 1 - scripts/docgen.lua | 1 - 6 files changed, 1 insertion(+), 158 deletions(-) delete mode 100644 lua/gopher/dap.lua diff --git a/README.md b/README.md index cd66f74..126475c 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,6 @@ Requirements: dependencies = { "nvim-lua/plenary.nvim", "nvim-treesitter/nvim-treesitter", - "mfussenegger/nvim-dap", -- (optional) only if you use `gopher.dap` }, -- (optional) will update plugin's deps on every update build = function() @@ -216,20 +215,6 @@ require("gopher").setup { ``` -
- - Setup nvim-dap for go in one line - - - THIS FEATURE WILL BE REMOVED IN `0.1.6` - - note [nvim-dap](https://github.com/mfussenegger/nvim-dap) has to be installed - - ```lua - require("gopher.dap").setup() - ``` -
- ## Contributing PRs are always welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) @@ -237,5 +222,4 @@ PRs are always welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) ## Thanks - [go.nvim](https://github.com/ray-x/go.nvim) -- [nvim-dap-go](https://github.com/leoluz/nvim-dap-go) - [iferr](https://github.com/koron/iferr) diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index 78b316d..45f0617 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -16,7 +16,6 @@ Table of Contents Generating unit tests boilerplate......................|gopher.nvim-gotests| Iferr....................................................|gopher.nvim-iferr| Generate comments.....................................|gopher.nvim-comments| - Setup `nvim-dap` for Go......................................|gopher.nvim-dap| ------------------------------------------------------------------------------ *gopher.nvim-setup* @@ -28,7 +27,7 @@ Calling this function is optional, if you ok with default settings. Look |gopher Usage ~ `require("gopher").setup {}` (replace `{}` with your `config` table) Parameters ~ -{user_config} gopher.Config +{user_config} `(gopher.Config)` ------------------------------------------------------------------------------ *gopher.nvim-install-deps* @@ -206,12 +205,4 @@ Execute `:GoCmt` to generate a comment for the current function/method/struct/et This module provides a way to generate comments for Go code. -============================================================================== ------------------------------------------------------------------------------- - *gopher.nvim-dap* -This module sets up `nvim-dap` for Go. -Usage ~ -just call `require("gopher.dap").setup()`, and you're good to go. - - vim:tw=78:ts=8:noet:ft=help:norl: \ No newline at end of file diff --git a/lua/gopher/dap.lua b/lua/gopher/dap.lua deleted file mode 100644 index 9930318..0000000 --- a/lua/gopher/dap.lua +++ /dev/null @@ -1,129 +0,0 @@ ----@toc_entry Setup `nvim-dap` for Go ----@tag gopher.nvim-dap ----@text This module sets up `nvim-dap` for Go. ----@usage just call `require("gopher.dap").setup()`, and you're good to go. - -local c = require "gopher.config" -local dap = {} - -dap.adapter = function(callback, config) - local host = config.host or "127.0.0.1" - local port = config.port or "38697" - local addr = string.format("%s:%s", host, port) - - local handle, pid_or_err - local stdout = assert(vim.loop.new_pipe(false)) - local opts = { - stdio = { nil, stdout }, - args = { "dap", "-l", addr }, - detached = true, - } - - handle, pid_or_err = vim.loop.spawn(c.commands.dlv, opts, function(status) - if not stdout or not handle then - return - end - - stdout:close() - handle:close() - if status ~= 0 then - print("dlv exited with code", status) - end - end) - - assert(handle, "Error running dlv: " .. tostring(pid_or_err)) - if stdout then - stdout:read_start(function(err, chunk) - assert(not err, err) - if chunk then - vim.schedule(function() - require("dap.repl").append(chunk) - end) - end - end) - end - - -- wait for delve to start - vim.defer_fn(function() - callback { type = "server", host = "127.0.0.1", port = port } - end, 100) -end - -local function args_input() - vim.ui.input({ prompt = "Args: " }, function(input) - return vim.split(input or "", " ") - end) -end - -local function get_arguments() - local co = coroutine.running() - if co then - return coroutine.create(function() - local args = args_input() - coroutine.resume(co, args) - end) - else - return args_input() - end -end - -dap.configuration = { - { - type = "go", - name = "Debug", - request = "launch", - program = "${file}", - }, - { - type = "go", - name = "Debug (Arguments)", - request = "launch", - program = "${file}", - args = get_arguments, - }, - { - type = "go", - name = "Debug Package", - request = "launch", - program = "${fileDirname}", - }, - { - type = "go", - name = "Attach", - mode = "local", - request = "attach", - processId = require("dap.utils").pick_process, - }, - { - type = "go", - name = "Debug test", - request = "launch", - mode = "test", - program = "${file}", - }, - { - type = "go", - name = "Debug test (go.mod)", - request = "launch", - mode = "test", - program = "./${relativeFileDirname}", - }, -} - --- sets ups nvim-dap for Go in one function call. -function dap.setup() - vim.deprecate( - "gopher.dap", - "you might consider setting up `nvim-dap` manually, or using another plugin(https://github.com/leoluz/nvim-dap-go)", - "v0.1.6", - "gopher" - ) - - local ok, d = pcall(require, "dap") - assert(ok, "gopher.nvim dependency error: dap not installed") - - d.adapters.go = dap.adapter - d.configurations.go = dap.configuration -end - -return dap diff --git a/lua/gopher/health.lua b/lua/gopher/health.lua index 633a184..e9351fe 100644 --- a/lua/gopher/health.lua +++ b/lua/gopher/health.lua @@ -22,7 +22,6 @@ local deps = { msg = "required for `:GoTestAdd`, `:GoTestsAll`, `:GoTestsExp`", optional = false, }, - { bin = cmd.dlv, msg = "required for debugging, (`nvim-dap`, `gopher.dap`)", optional = true }, }, treesitter = { { parser = "go", msg = "required for `gopher.nvim`", optional = false }, diff --git a/lua/gopher/installer.lua b/lua/gopher/installer.lua index 2994b8a..ac60c03 100644 --- a/lua/gopher/installer.lua +++ b/lua/gopher/installer.lua @@ -8,7 +8,6 @@ local urls = { impl = "github.com/josharian/impl", gotests = "github.com/cweill/gotests/...", iferr = "github.com/koron/iferr", - dlv = "github.com/go-delve/delve/cmd/dlv", } ---@param pkg string diff --git a/scripts/docgen.lua b/scripts/docgen.lua index f2deb8b..87cbd3a 100644 --- a/scripts/docgen.lua +++ b/scripts/docgen.lua @@ -15,7 +15,6 @@ local files = { "lua/gopher/gotests.lua", "lua/gopher/iferr.lua", "lua/gopher/comment.lua", - "lua/gopher/dap.lua", } minidoc.setup() From 837897a79d9c68e6338fefd0949ec26f9377451e Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Tue, 25 Feb 2025 14:08:15 +0200 Subject: [PATCH 03/25] fix: typos (#82) * docs(gotests): update and fix * fix(health): typos * docs(impl): update and fix typos * docs(config): typos * docs(iferr): update * typos * docs(struct_tags): update * docs: fix typos * docs: Capitalization * docgen --- CONTRIBUTING.md | 5 +-- README.md | 2 +- doc/gopher.nvim.txt | 83 +++++++++++++++++++++----------------- lua/gopher/config.lua | 7 ++-- lua/gopher/gotests.lua | 23 +++++------ lua/gopher/health.lua | 4 +- lua/gopher/iferr.lua | 4 +- lua/gopher/impl.lua | 32 +++++++++------ lua/gopher/init.lua | 6 +-- lua/gopher/struct_tags.lua | 16 ++++---- 10 files changed, 96 insertions(+), 86 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 39092b1..1a825db 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,6 @@ You can install these with: ```bash sudo pacman -S selene stylua # or whatever is your package manager -# or way of installing pkgs ``` For formatting use this following commands, or setup your editor to integrate with selene/stylua: @@ -39,6 +38,7 @@ task docgen ``` ### Commit messages + We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), please follow it. ### Testing @@ -48,8 +48,5 @@ All tests live in [/spec](https://github.com/olexsmir/gopher.nvim/tree/main/spec You can run tests with: ```bash -task test -# also there are some aliases for that task tests -task spec ``` diff --git a/README.md b/README.md index 126475c..49cb8ed 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Requirements: } ``` -## Configuratoin +## Configuration > [!IMPORTANT] > diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index 45f0617..29a3786 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -11,7 +11,7 @@ Table of Contents Setup....................................................|gopher.nvim-setup| Install dependencies..............................|gopher.nvim-install-deps| Configuration...........................................|gopher.nvim-config| - Modifty struct tags................................|gopher.nvim-struct-tags| + Modify struct tags.................................|gopher.nvim-struct-tags| Auto implementation of interface methods..................|gopher.nvim-impl| Generating unit tests boilerplate......................|gopher.nvim-gotests| Iferr....................................................|gopher.nvim-iferr| @@ -20,7 +20,7 @@ Table of Contents ------------------------------------------------------------------------------ *gopher.nvim-setup* `gopher.setup`({user_config}) -Setup function. This method simply merges default configs with opts table. +Setup function. This method simply merges default config with opts table. You can read more about configuration at |gopher.nvim-config| Calling this function is optional, if you ok with default settings. Look |gopher.nvim.config-defaults| @@ -33,8 +33,8 @@ Parameters ~ *gopher.nvim-install-deps* `gopher.install_deps` Gopher.nvim implements most of its features using third-party tools. -To install these tools, you can run `:GoInstallDeps` command -or call `require("gopher").install_deps()` if you want ues lua api. +To install these tools, you can run `:GoInstallDeps` command +or call `require("gopher").install_deps()` if you want to use lua api. ============================================================================== @@ -51,7 +51,7 @@ You can look at default options |gopher.nvim-config-defaults| local default_config = { --minidoc_replace_end - -- log level, you might consider using DEBUG or TRACE for degugging the plugin + -- log level, you might consider using DEBUG or TRACE for debugging the plugin ---@type number log_level = vim.log.levels.INFO, @@ -92,13 +92,16 @@ Class ~ *gopher.nvim-struct-tags* struct-tags is utilizing the `gomodifytags` tool to add or remove tags to struct fields. Usage ~ -- put your coursor on the struct -- run `:GoTagAdd json` to add json tags to struct fields -- run `:GoTagRm json` to remove json tags to struct fields -note: if you dont spesify the tag it will use `json` as default +How to add/remove tags to struct fields: -simple example: +------------------------------------------------------------------------------ +2. Run `:GoTagAdd json` to add json tags to struct fields +3. Run `:GoTagRm json` to remove json tags to struct fields + +NOTE: if you dont specify the tag it will use `json` as default + +Example: >go // before type User struct { @@ -121,19 +124,25 @@ simple example: impl is utilizing the `impl` tool to generate method stubs for interfaces. Usage ~ -1. put your coursor on the struct on which you want implement the interface - and run `:GoImpl io.Reader` - which will automatically choose the reciver for the methods and - implement the `io.Reader` interface -2. same as previous but with custom receiver, so put your coursor on the struct - run `:GoImpl w io.Writer` - where `w` is the receiver and `io.Writer` is the interface -3. specift receiver, struct, and interface - there's no need to put your coursor on the struct if you specify all arguments - `:GoImpl r RequestReader io.Reader` - where `r` is the receiver, `RequestReader` is the struct and `io.Reader` is the interface +1. Automatically implement an interface for a struct: + - Place your cursor on the struct where you want to implement the interface. + - Run `:GoImpl io.Reader` + - This will automatically determine the receiver and implement the `io.Reader` interface. -simple example: +2. Specify a custom receiver: + - Place your cursor on the struct + - Run `:GoImpl w io.Writer`, where: + - `w` is the receiver. + - `io.Writer` is the interface to implement. + +3. Explicitly specify the receiver, struct, and interface: + - No need to place the cursor on the struct if all arguments are provided. + - Run `:GoImpl r RequestReader io.Reader`, where: + - `r` is the receiver. + - `RequestReader` is the struct. + - `io.Reader` is the interface to implement. + +Example: >go type BytesReader struct{} // ^ put your cursor here @@ -141,7 +150,7 @@ simple example: // this is what you will get func (b *BytesReader) Read(p []byte) (n int, err error) { - panic("not implemented") // TODO: Implement + panic("not implemented") // TODO: Implement } < @@ -151,30 +160,31 @@ simple example: gotests is utilizing the `gotests` tool to generate unit tests boilerplate. Usage ~ -- generate unit test for spesisfic function/method - - to specift the function/method put your cursor on it - - run `:GoTestAdd` +- Generate unit test for specific function/method: + 1. Place your cursor on the desired function/method. + 2. Run `:GoTestAdd` -- generate unit tests for all functions/methods in current file +- Generate unit tests for *all* functions/methods in current file: - run `:GoTestsAll` -- generate unit tests only for exported(public) functions/methods +- Generate unit tests *only* for *exported(public)* functions/methods: - run `:GoTestsExp` -you can also specify the template to use for generating the tests. see |gopher.nvim-config| -more details about templates can be found at: https://github.com/cweill/gotests +You can also specify the template to use for generating the tests. See |gopher.nvim-config| +More details about templates can be found at: https://github.com/cweill/gotests ------------------------------------------------------------------------------ *gopher.nvim-gotests-named* -if you prefare using named tests, you can enable it in the config. -but you would need to install `gotests@develop` because stable version doesn't support this feature. -you can do it with: +You can enable named tests in the config if you prefer using named tests. +But you must install `gotests@develop` because the stable version doesn't support this feature. + >lua -- simply run go get in your shell: go install github.com/cweill/gotests/...@develop -- if you want to install it within neovim, you can use one of this: + -- if you choose to install gotests this way i reocmmend adding it to your `build` section in your |lazy.nvim| vim.fn.jobstart("go install github.com/cweill/gotests/...@develop") @@ -186,15 +196,12 @@ you can do it with: } < -if you choose to install `gotests` within neovim, i recommend adding it to your `build` section in your |lazy.nvim| - - ============================================================================== ------------------------------------------------------------------------------ *gopher.nvim-iferr* -if you're using `iferr` tool, this module provides a way to automatically insert `if err != nil` check. +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 +Execute `:GoIfErr` near any `err` variable to insert the check ============================================================================== diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index 8265838..18c08c3 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -29,7 +29,7 @@ local config = {} local default_config = { --minidoc_replace_end - -- log level, you might consider using DEBUG or TRACE for degugging the plugin + -- log level, you might consider using DEBUG or TRACE for debugging the plugin ---@type number log_level = vim.log.levels.INFO, @@ -66,10 +66,9 @@ local default_config = { ---@private local _config = default_config --- I am kinda secret so don't tell anyone about me --- even dont use me +-- I am kinda secret so don't tell anyone about me even dont use me -- --- if you don't belive me that i am secret see +-- if you don't believe me that i am secret see -- the line below it says @private ---@private _config.___plugin_name = "gopher.nvim" ---@diagnostic disable-line: inject-field diff --git a/lua/gopher/gotests.lua b/lua/gopher/gotests.lua index da4753d..1c177b6 100644 --- a/lua/gopher/gotests.lua +++ b/lua/gopher/gotests.lua @@ -2,30 +2,31 @@ ---@tag gopher.nvim-gotests ---@text gotests is utilizing the `gotests` tool to generate unit tests boilerplate. ---@usage ---- - generate unit test for spesisfic function/method ---- - to specift the function/method put your cursor on it ---- - run `:GoTestAdd` +--- - Generate unit test for specific function/method: +--- 1. Place your cursor on the desired function/method. +--- 2. Run `:GoTestAdd` --- ---- - generate unit tests for all functions/methods in current file +--- - Generate unit tests for *all* functions/methods in current file: --- - run `:GoTestsAll` --- ---- - generate unit tests only for exported(public) functions/methods +--- - Generate unit tests *only* for *exported(public)* functions/methods: --- - run `:GoTestsExp` --- ---- you can also specify the template to use for generating the tests. see |gopher.nvim-config| ---- more details about templates can be found at: https://github.com/cweill/gotests +--- You can also specify the template to use for generating the tests. See |gopher.nvim-config| +--- More details about templates can be found at: https://github.com/cweill/gotests --- ---@tag gopher.nvim-gotests-named ---@text ---- if you prefare using named tests, you can enable it in the config. ---- but you would need to install `gotests@develop` because stable version doesn't support this feature. ---- you can do it with: +--- You can enable named tests in the config if you prefer using named tests. +--- But you must install `gotests@develop` because the stable version doesn't support this feature. +--- --- >lua --- -- simply run go get in your shell: --- go install github.com/cweill/gotests/...@develop --- --- -- if you want to install it within neovim, you can use one of this: +--- -- if you choose to install gotests this way i reocmmend adding it to your `build` section in your |lazy.nvim| --- --- vim.fn.jobstart("go install github.com/cweill/gotests/...@develop") --- @@ -36,8 +37,6 @@ --- } --- } --- < ---- ---- if you choose to install `gotests` within neovim, i recommend adding it to your `build` section in your |lazy.nvim| local c = require "gopher.config" local ts_utils = require "gopher._utils.ts" diff --git a/lua/gopher/health.lua b/lua/gopher/health.lua index e9351fe..9fa0bed 100644 --- a/lua/gopher/health.lua +++ b/lua/gopher/health.lua @@ -5,8 +5,8 @@ local u = require "gopher._utils.health_util" local deps = { plugin = { { lib = "dap", msg = "required for `gopher.dap`", optional = true }, - { lib = "plenary", msg = "required for everyting in gopher.nvim", optional = false }, - { lib = "nvim-treesitter", msg = "required for everyting in gopher.nvim", optional = false }, + { lib = "plenary", msg = "required for everything in gopher.nvim", optional = false }, + { lib = "nvim-treesitter", msg = "required for everything in gopher.nvim", optional = false }, }, bin = { { diff --git a/lua/gopher/iferr.lua b/lua/gopher/iferr.lua index bcd4b24..a3a193e 100644 --- a/lua/gopher/iferr.lua +++ b/lua/gopher/iferr.lua @@ -1,7 +1,7 @@ ---@toc_entry Iferr ---@tag gopher.nvim-iferr ----@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 +---@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 log = require "gopher._utils.log" diff --git a/lua/gopher/impl.lua b/lua/gopher/impl.lua index f461376..4e1ec9a 100644 --- a/lua/gopher/impl.lua +++ b/lua/gopher/impl.lua @@ -2,19 +2,25 @@ ---@tag gopher.nvim-impl ---@text impl is utilizing the `impl` tool to generate method stubs for interfaces. ---@usage ---- 1. put your coursor on the struct on which you want implement the interface ---- and run `:GoImpl io.Reader` ---- which will automatically choose the reciver for the methods and ---- implement the `io.Reader` interface ---- 2. same as previous but with custom receiver, so put your coursor on the struct ---- run `:GoImpl w io.Writer` ---- where `w` is the receiver and `io.Writer` is the interface ---- 3. specift receiver, struct, and interface ---- there's no need to put your coursor on the struct if you specify all arguments ---- `:GoImpl r RequestReader io.Reader` ---- where `r` is the receiver, `RequestReader` is the struct and `io.Reader` is the interface +--- 1. Automatically implement an interface for a struct: +--- - Place your cursor on the struct where you want to implement the interface. +--- - Run `:GoImpl io.Reader` +--- - This will automatically determine the receiver and implement the `io.Reader` interface. --- ---- simple example: +--- 2. Specify a custom receiver: +--- - Place your cursor on the struct +--- - Run `:GoImpl w io.Writer`, where: +--- - `w` is the receiver. +--- - `io.Writer` is the interface to implement. +--- +--- 3. Explicitly specify the receiver, struct, and interface: +--- - No need to place the cursor on the struct if all arguments are provided. +--- - Run `:GoImpl r RequestReader io.Reader`, where: +--- - `r` is the receiver. +--- - `RequestReader` is the struct. +--- - `io.Reader` is the interface to implement. +--- +--- Example: --- >go --- type BytesReader struct{} --- // ^ put your cursor here @@ -22,7 +28,7 @@ --- --- // this is what you will get --- func (b *BytesReader) Read(p []byte) (n int, err error) { ---- panic("not implemented") // TODO: Implement +--- panic("not implemented") // TODO: Implement --- } --- < diff --git a/lua/gopher/init.lua b/lua/gopher/init.lua index 2aa138e..e18a8bd 100644 --- a/lua/gopher/init.lua +++ b/lua/gopher/init.lua @@ -17,7 +17,7 @@ local gopher = {} ---@toc_entry Setup ---@tag gopher.nvim-setup ----@text Setup function. This method simply merges default configs with opts table. +---@text Setup function. This method simply merges default config with opts table. --- You can read more about configuration at |gopher.nvim-config| --- Calling this function is optional, if you ok with default settings. Look |gopher.nvim.config-defaults| --- @@ -32,8 +32,8 @@ end ---@toc_entry Install dependencies ---@tag gopher.nvim-install-deps ---@text Gopher.nvim implements most of its features using third-party tools. ---- To install these tools, you can run `:GoInstallDeps` command ---- or call `require("gopher").install_deps()` if you want ues lua api. +--- To install these tools, you can run `:GoInstallDeps` command +--- or call `require("gopher").install_deps()` if you want to use lua api. gopher.install_deps = require("gopher.installer").install_deps gopher.impl = require("gopher.impl").impl diff --git a/lua/gopher/struct_tags.lua b/lua/gopher/struct_tags.lua index 4389b62..b3c9dc7 100644 --- a/lua/gopher/struct_tags.lua +++ b/lua/gopher/struct_tags.lua @@ -1,13 +1,15 @@ ----@toc_entry Modifty struct tags +---@toc_entry Modify struct tags ---@tag gopher.nvim-struct-tags ---@text struct-tags is utilizing the `gomodifytags` tool to add or remove tags to struct fields. ----@usage - put your coursor on the struct ---- - run `:GoTagAdd json` to add json tags to struct fields ---- - run `:GoTagRm json` to remove json tags to struct fields +---@usage +--- How to add/remove tags to struct fields: +-- 1. Place cursor on the struct +--- 2. Run `:GoTagAdd json` to add json tags to struct fields +--- 3. Run `:GoTagRm json` to remove json tags to struct fields --- ---- note: if you dont spesify the tag it will use `json` as default +--- NOTE: if you dont specify the tag it will use `json` as default --- ---- simple example: +--- Example: --- >go --- // before --- type User struct { @@ -74,7 +76,7 @@ local function modify(...) end, }) - -- decode goted value + -- decode value local tagged = vim.json.decode(table.concat(output)) if tagged.errors ~= nil From 6016ca57d4e21ccc1c401a0e3eedb34bb2b164e1 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Sun, 2 Mar 2025 16:31:50 +0200 Subject: [PATCH 04/25] refactor: use vim.system instead of pleanry (#85) * refactor!: migrate to vim.system * refactor(gotests): use vim.system * refactor(iferr): use vim.system * refactor(impl): use vim.system * refactor(installer): use vim.system and add sync mode * test: fix gotests' tests * refactor(struct_tags): use vim.system * chore(ci): install all deps explicitly * refactor(installer)!: add sync as an option * docs: update readme --- .github/workflows/tests.yml | 7 +++- README.md | 6 ++-- lua/gopher/_utils/init.lua | 21 ++++++++++- lua/gopher/_utils/runner/init.lua | 56 ++++++++++++++++------------- lua/gopher/config.lua | 4 +++ lua/gopher/gotests.lua | 14 +++----- lua/gopher/health.lua | 20 ++++------- lua/gopher/iferr.lua | 26 ++++++++------ lua/gopher/impl.lua | 21 ++++------- lua/gopher/installer.lua | 48 +++++++++++++++++-------- lua/gopher/struct_tags.lua | 34 +++++++++--------- plugin/gopher.vim | 1 + scripts/minimal_init.lua | 34 ++++++++++-------- spec/fixtures/impl/closer_output.go | 1 - spec/fixtures/impl/reader_output.go | 1 - spec/fixtures/impl/writer_output.go | 1 - 16 files changed, 169 insertions(+), 126 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8564381..c82730b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -47,7 +47,12 @@ jobs: 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: | diff --git a/README.md b/README.md index 49cb8ed..a6f59db 100644 --- a/README.md +++ b/README.md @@ -13,17 +13,15 @@ It's **NOT** an LSP tool, the main goal of this plugin is to add go tooling supp 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 diff --git a/lua/gopher/_utils/init.lua b/lua/gopher/_utils/init.lua index 96a9134..1ff0f28 100644 --- a/lua/gopher/_utils/init.lua +++ b/lua/gopher/_utils/init.lua @@ -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, @@ -23,4 +24,22 @@ function utils.notify(msg, lvl) 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 diff --git a/lua/gopher/_utils/runner/init.lua b/lua/gopher/_utils/runner/init.lua index 3c08f7f..15eaa42 100644 --- a/lua/gopher/_utils/runner/init.lua +++ b/lua/gopher/_utils/runner/init.lua @@ -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 diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index 18c08c3..94dbab9 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -33,6 +33,10 @@ local default_config = { ---@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 = { diff --git a/lua/gopher/gotests.lua b/lua/gopher/gotests.lua index 1c177b6..e97f26c 100644 --- a/lua/gopher/gotests.lua +++ b/lua/gopher/gotests.lua @@ -67,16 +67,12 @@ local function add_test(args) 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 diff --git a/lua/gopher/health.lua b/lua/gopher/health.lua index 9fa0bed..4438c8a 100644 --- a/lua/gopher/health.lua +++ b/lua/gopher/health.lua @@ -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 @@ local deps = { 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 @@ function health.check() 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 diff --git a/lua/gopher/iferr.lua b/lua/gopher/iferr.lua index a3a193e..3258bd8 100644 --- a/lua/gopher/iferr.lua +++ b/lua/gopher/iferr.lua @@ -4,27 +4,33 @@ ---@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 diff --git a/lua/gopher/impl.lua b/lua/gopher/impl.lua index 4e1ec9a..0ed3e76 100644 --- a/lua/gopher/impl.lua +++ b/lua/gopher/impl.lua @@ -43,7 +43,7 @@ local impl = {} 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 @@ function impl.impl(...) 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 diff --git a/lua/gopher/installer.lua b/lua/gopher/installer.lua index ac60c03..526e688 100644 --- a/lua/gopher/installer.lua +++ b/lua/gopher/installer.lua @@ -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 @@ local urls = { 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 diff --git a/lua/gopher/struct_tags.lua b/lua/gopher/struct_tags.lua index b3c9dc7..17e0d78 100644 --- a/lua/gopher/struct_tags.lua +++ b/lua/gopher/struct_tags.lua @@ -38,15 +38,8 @@ local function modify(...) return end - -- stylua: ignore - local cmd_args = { - "-transform", c.gotag.transform, - "-format", "json", - "-file", fpath, - "-w" - } - -- by struct name of line pos + local cmd_args = {} if ns.name == nil then local _, csrow, _, _ = unpack(vim.fn.getpos ".") table.insert(cmd_args, "-line") @@ -67,17 +60,24 @@ local function modify(...) table.insert(cmd_args, "json") end - local output = r.sync(c.commands.gomodifytags, { - args = cmd_args, - on_exit = function(data, status) - if not status == 0 then - error("gotag failed: " .. data) - end - end, - }) + local rs = r.sync { + c.commands.gomodifytags, + "-transform", + c.gotag.transform, + "-format", + "json", + "-w", + "-file", + fpath, + unpack(cmd_args), + } + + if rs.code ~= 0 then + error("failed to set tags " .. rs.stderr) + end -- decode value - local tagged = vim.json.decode(table.concat(output)) + local tagged = vim.json.decode(rs.stdout) if tagged.errors ~= nil or tagged.lines == nil diff --git a/plugin/gopher.vim b/plugin/gopher.vim index a219a1c..b61ed6e 100644 --- a/plugin/gopher.vim +++ b/plugin/gopher.vim @@ -11,4 +11,5 @@ command! -nargs=* GoGenerate :lua require"gopher".generate() 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()) diff --git a/scripts/minimal_init.lua b/scripts/minimal_init.lua index e59f06a..503bf49 100644 --- a/scripts/minimal_init.lua +++ b/scripts/minimal_init.lua @@ -8,17 +8,23 @@ local function install_plug(plugin) 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,16 +33,16 @@ vim.env.XDG_CACHE_HOME = root ".tests/cache" 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 require("mini.test").setup { diff --git a/spec/fixtures/impl/closer_output.go b/spec/fixtures/impl/closer_output.go index 5e976f3..4f077f4 100644 --- a/spec/fixtures/impl/closer_output.go +++ b/spec/fixtures/impl/closer_output.go @@ -5,4 +5,3 @@ type CloserTest2 struct{} func (closertest *CloserTest2) Close() error { panic("not implemented") // TODO: Implement } - diff --git a/spec/fixtures/impl/reader_output.go b/spec/fixtures/impl/reader_output.go index 26df873..c384b23 100644 --- a/spec/fixtures/impl/reader_output.go +++ b/spec/fixtures/impl/reader_output.go @@ -4,5 +4,4 @@ func (r Read2) Read(p []byte) (n int, err error) { panic("not implemented") // TODO: Implement } - type Read2 struct{} diff --git a/spec/fixtures/impl/writer_output.go b/spec/fixtures/impl/writer_output.go index 19e8f6e..b69a70c 100644 --- a/spec/fixtures/impl/writer_output.go +++ b/spec/fixtures/impl/writer_output.go @@ -5,4 +5,3 @@ type WriterTest2 struct{} func (w *WriterTest2) Write(p []byte) (n int, err error) { panic("not implemented") // TODO: Implement } - From c2f64db4a86feaf993e6899887729414d5b0ddb3 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Sun, 2 Mar 2025 16:43:30 +0200 Subject: [PATCH 05/25] refactor(health): remove deprecations (#86) --- lua/gopher/_utils/health_util.lua | 33 --------------------- lua/gopher/health.lua | 49 +++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 48 deletions(-) delete mode 100644 lua/gopher/_utils/health_util.lua diff --git a/lua/gopher/_utils/health_util.lua b/lua/gopher/_utils/health_util.lua deleted file mode 100644 index e1d44ee..0000000 --- a/lua/gopher/_utils/health_util.lua +++ /dev/null @@ -1,33 +0,0 @@ -local h = vim.health or require "health" -local health = {} - -health.start = h.start or h.report_start -health.ok = h.ok or h.report_ok -health.warn = h.warn or h.report_warn -health.error = h.error or h.report_error -health.info = h.info or h.report_info - ----@param module string ----@return boolean -function health.is_lualib_found(module) - local is_found, _ = pcall(require, module) - return is_found -end - ----@param bin string ----@return boolean -function health.is_binary_found(bin) - if vim.fn.executable(bin) == 1 then - return true - end - return false -end - ----@param ft string ----@return boolean -function health.is_treesitter_parser_available(ft) - local ok, parser = pcall(vim.treesitter.get_parser, 0, ft) - return ok and parser ~= nil -end - -return health diff --git a/lua/gopher/health.lua b/lua/gopher/health.lua index 4438c8a..f45c499 100644 --- a/lua/gopher/health.lua +++ b/lua/gopher/health.lua @@ -1,6 +1,5 @@ local health = {} local cmd = require("gopher.config").commands -local u = require "gopher._utils.health_util" local deps = { plugin = { @@ -26,36 +25,56 @@ local deps = { }, } +---@param module string +---@return boolean +local function is_lualib_found(module) + local is_found, _ = pcall(require, module) + return is_found +end + +---@param bin string +---@return boolean +local function is_binary_found(bin) + return vim.fn.executable(bin) == 1 +end + +---@param ft string +---@return boolean +local function is_treesitter_parser_available(ft) + local ok, parser = pcall(vim.treesitter.get_parser, 0, ft) + return ok and parser ~= nil +end + function health.check() - u.start "required plugins" + vim.health.start "required plugins" for _, plugin in ipairs(deps.plugin) do - if u.is_lualib_found(plugin.lib) then - u.ok(plugin.lib .. " installed") + if is_lualib_found(plugin.lib) then + vim.health.ok(plugin.lib .. " installed") else - u.error(plugin.lib .. " not found, " .. plugin.msg) + vim.health.error(plugin.lib .. " not found, " .. plugin.msg) end end - u.start "required binaries" - u.info "all those binaries can be installed by `:GoInstallDeps`" + vim.health.start "required binaries" + vim.health.info "all those binaries can be installed by `:GoInstallDeps`" for _, bin in ipairs(deps.bin) do - if u.is_binary_found(bin.bin) then - u.ok(bin.bin .. " installed") + if is_binary_found(bin.bin) then + vim.health.ok(bin.bin .. " installed") else if bin.optional then - u.warn(bin.bin .. " not found, " .. bin.msg) + vim.health.warn(bin.bin .. " not found, " .. bin.msg) else - u.error(bin.bin .. " not found, " .. bin.msg) + vim.health.error(bin.bin .. " not found, " .. bin.msg) end end end - u.start "required treesitter parsers" + vim.health.start "required treesitter parsers" for _, parser in ipairs(deps.treesitter) do - if u.is_treesitter_parser_available(parser.parser) then - u.ok(parser.parser .. " parser installed") + if is_treesitter_parser_available(parser.parser) then + vim.health.ok(parser.parser .. " parser installed") else - u.error(parser.parser .. " parser not found, " .. parser.msg) + vim.health.error(parser.parser .. " parser not found, " .. parser.msg) end end end From 57b5dbf62e7d01d4eb9597f6df4ce11b463c97be Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Sun, 2 Mar 2025 17:19:00 +0200 Subject: [PATCH 06/25] feat(struct_tags): set default tag (#87) * feat(struct_tags): add config option for default tag * docs: docgen * fix(struct_tags): as it turns out it didnt work as i supposed to before, but now it does --- doc/gopher.nvim.txt | 7 +++++++ lua/gopher/config.lua | 3 +++ lua/gopher/struct_tags.lua | 22 +++++++++------------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index 29a3786..c455a7e 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -55,6 +55,10 @@ You can look at default options |gopher.nvim-config-defaults| ---@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 = { @@ -80,6 +84,9 @@ You can look at default options |gopher.nvim-config-defaults| gotag = { ---@type gopher.ConfigGoTagTransform transform = "snakecase", + + -- default tags to add to struct fields + default_tag = "json", }, } < diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index 94dbab9..f86d49c 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -62,6 +62,9 @@ local default_config = { gotag = { ---@type gopher.ConfigGoTagTransform transform = "snakecase", + + -- default tags to add to struct fields + default_tag = "json", }, } --minidoc_afterlines_end diff --git a/lua/gopher/struct_tags.lua b/lua/gopher/struct_tags.lua index 17e0d78..703b296 100644 --- a/lua/gopher/struct_tags.lua +++ b/lua/gopher/struct_tags.lua @@ -55,11 +55,6 @@ local function modify(...) table.insert(cmd_args, v) end - -- set default tag for "clear tags" - if #arg == 1 and arg[1] ~= "-clear-tags" then - table.insert(cmd_args, "json") - end - local rs = r.sync { c.commands.gomodifytags, "-transform", @@ -99,13 +94,14 @@ end -- add tags to struct under cursor function struct_tags.add(...) - local arg = { ... } - if #arg == nil or arg == "" then - arg = { "json" } + local user_tags = { ... } + if #user_tags == 0 then + vim.print("c.gotag.default_tag", c.gotag.default_tag) + user_tags = { c.gotag.default_tag } end local cmd_args = { "-add-tags" } - for _, v in ipairs(arg) do + for _, v in ipairs(user_tags) do table.insert(cmd_args, v) end @@ -114,13 +110,13 @@ end -- remove tags to struct under cursor function struct_tags.remove(...) - local arg = { ... } - if #arg == nil or arg == "" then - arg = { "json" } + local user_tags = { ... } + if #user_tags == 0 then + user_tags = { c.gotag.default_tag } end local cmd_args = { "-remove-tags" } - for _, v in ipairs(arg) do + for _, v in ipairs(user_tags) do table.insert(cmd_args, v) end From bb31271311f3bb226b6c21af59a30bb30d1ad54a Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Sun, 2 Mar 2025 17:26:23 +0200 Subject: [PATCH 07/25] chore: add docs checking thing (#88) * chore: add doc checking thing * testing * Revert "testing" This reverts commit ae0618d89f4cff036d307ffeb5f242ff37f58c01. --- .github/workflows/docs.yml | 40 +++++++++++++++++++++++++++++++++++ .github/workflows/linters.yml | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..bcd0514 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,40 @@ +name: docs + +on: + push: + branches: + - main + - develop + pull_request: + +jobs: + docs: + name: linters + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Task + uses: arduino/setup-task@v1 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install NeoVim + uses: rhysd/action-setup-vim@v1 + with: + neovim: true + version: ${{ matrix.version }} + + - name: Cache .tests + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/.tests + key: ${{ runner.os }}-tests-${{ hashFiles('${{ github.workspace }}/.tests') }} + + - name: Generate docs + run: task docgen + + - name: Check docs diff + run: exit $(git status --porcelain doc | wc -l | tr -d " ") diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 389b72f..ae5be14 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -12,7 +12,7 @@ jobs: name: linters runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: JohnnyMorganz/stylua-action@v3 with: token: ${{ secrets.GITHUB_TOKEN }} From d1a21bffabd7e74b241bfea1016cf78befb3b122 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Mon, 3 Mar 2025 14:22:28 +0200 Subject: [PATCH 08/25] feat(iferr): add `-message` support (#89) * feat(iferr): add *-message* support * generate docs --- doc/gopher.nvim.txt | 5 +++++ lua/gopher/config.lua | 5 +++++ lua/gopher/iferr.lua | 8 +++++++- spec/fixtures/iferr/message_input.go | 7 +++++++ spec/fixtures/iferr/message_output.go | 10 ++++++++++ spec/integration/iferr_test.lua | 14 ++++++++++++++ 6 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 spec/fixtures/iferr/message_input.go create mode 100644 spec/fixtures/iferr/message_output.go diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index c455a7e..4c3fd65 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -88,6 +88,11 @@ You can look at default options |gopher.nvim-config-defaults| -- default tags to add to struct fields default_tag = "json", }, + iferr = { + -- choose a custom error message + ---@type string|nil + message = nil, + }, } < Class ~ diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index f86d49c..7c67773 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -66,6 +66,11 @@ local default_config = { -- default tags to add to struct fields default_tag = "json", }, + iferr = { + -- choose a custom error message + ---@type string|nil + message = nil, + }, } --minidoc_afterlines_end diff --git a/lua/gopher/iferr.lua b/lua/gopher/iferr.lua index 3258bd8..c7c7450 100644 --- a/lua/gopher/iferr.lua +++ b/lua/gopher/iferr.lua @@ -15,7 +15,13 @@ function iferr.iferr() local pos = vim.fn.getcurpos()[2] local fpath = vim.fn.expand "%" - local rs = r.sync({ c.commands.iferr, "-pos", curb }, { + local cmd = { c.commands.iferr, "-pos", curb } + if c.iferr.message ~= nil and type(c.iferr.message) == "string" then + table.insert(cmd, "-message") + table.insert(cmd, c.iferr.message) + end + + local rs = r.sync(cmd, { stdin = u.readfile_joined(fpath), }) diff --git a/spec/fixtures/iferr/message_input.go b/spec/fixtures/iferr/message_input.go new file mode 100644 index 0000000..5998cba --- /dev/null +++ b/spec/fixtures/iferr/message_input.go @@ -0,0 +1,7 @@ +package main + +func getErr() error { return nil } + +func test() error { + err := getErr() +} diff --git a/spec/fixtures/iferr/message_output.go b/spec/fixtures/iferr/message_output.go new file mode 100644 index 0000000..7d10241 --- /dev/null +++ b/spec/fixtures/iferr/message_output.go @@ -0,0 +1,10 @@ +package main + +func getErr() error { return nil } + +func test() error { + err := getErr() + if err != nil { + return fmt.Errorf("failed to %w", err) + } +} diff --git a/spec/integration/iferr_test.lua b/spec/integration/iferr_test.lua index 14ee08e..9c5ffdb 100644 --- a/spec/integration/iferr_test.lua +++ b/spec/integration/iferr_test.lua @@ -23,4 +23,18 @@ T["iferr"]["works"] = function() t.eq(t.readfile(tmp), fixtures.output) end +T["iferr"]["works with custom message"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "iferr/message" + t.writefile(tmp, fixtures.input) + + child.lua [[ require("gopher").setup { iferr = { message = 'fmt.Errorf("failed to %w", err)' } } ]] + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr "%", 6, 2, 0 }) + child.cmd "GoIfErr" + child.cmd "write" + + t.eq(t.readfile(tmp), fixtures.output) +end + return T From f171953e4366af7328abd4d9054b6c22466a8768 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Mon, 3 Mar 2025 14:27:02 +0200 Subject: [PATCH 09/25] chore(ci)!: use rolling stable version of nvim (#90) * chore(ci)!: use rolling stable version of nvim * fix(ci): use correct version on doc check --- .github/workflows/docs.yml | 2 +- .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index bcd0514..8a0335e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -24,7 +24,7 @@ jobs: uses: rhysd/action-setup-vim@v1 with: neovim: true - version: ${{ matrix.version }} + version: stable - name: Cache .tests uses: actions/cache@v4 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c82730b..f5a767e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: matrix: os: [ubuntu-latest] version: - - v0.10.4 + - stable - nightly runs-on: ${{ matrix.os }} steps: From e9f2eef5e7e76dfc33c962822d5e65d4e05a3c24 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Wed, 19 Mar 2025 15:09:57 +0200 Subject: [PATCH 10/25] refactor: treesitter utils (#91) * refactor(ts_utils): i dont know why event it was here * fix: typos * fix(struct_tags)!: remove statement that i used for debug * refactor(ts_util): start from scratch * refactor(struct_tags): use new ts_util * fixup! refactor(struct_tags): use new ts_util * test(struct_tags): add support for multiple structs * fix(gotests): use new api * fix(impl): refactor some logic, use new api * docs(ts): add an explanation * refactor(_utils.ts): all public methods are just adapters * fix(comment): now it works * fixup! refactor(_utils.ts): all public methods are just adapters * fixup! fixup! refactor(_utils.ts): all public methods are just adapters * test(comment): e2e * tests(comment): fix * refactor(utils.ts): fix, docs * test(comment): fix tests again * fix(tests/comments): well, now i fell stupid * refactor(ts): add assert just to be sure that all elements are in the result * fix(ts): type annotations * fix(ts): pass bufnr to vim.treesitter.get_node * chore(ci): disable nightly * chore(ci): reorganize --- .github/workflows/docs.yml | 40 ------ .github/workflows/linters.yml | 33 ++++- .github/workflows/tests.yml | 2 +- lua/gopher/_utils/log.lua | 2 +- lua/gopher/_utils/runner/gocmd.lua | 4 +- lua/gopher/_utils/ts.lua | 115 +++++++++++++++++ lua/gopher/_utils/ts/init.lua | 104 ---------------- lua/gopher/_utils/ts/nodes.lua | 143 ---------------------- lua/gopher/comment.lua | 86 +++++++------ lua/gopher/gotests.lua | 9 +- lua/gopher/impl.lua | 50 ++------ lua/gopher/init.lua | 2 +- lua/gopher/struct_tags.lua | 21 +--- spec/fixtures/comment/empty_input.go | 0 spec/fixtures/comment/empty_output.go | 2 + spec/fixtures/comment/func_input.go | 5 + spec/fixtures/comment/func_output.go | 6 + spec/fixtures/comment/interface_input.go | 3 + spec/fixtures/comment/interface_output.go | 4 + spec/fixtures/comment/method_input.go | 7 ++ spec/fixtures/comment/method_output.go | 8 ++ spec/fixtures/comment/package_output.go | 2 +- spec/fixtures/comment/struct_input.go | 3 + spec/fixtures/comment/struct_output.go | 4 + spec/fixtures/impl/reader_output.go | 2 +- spec/fixtures/tags/many_input.go | 18 +++ spec/fixtures/tags/many_output.go | 18 +++ spec/integration/comment_test.lua | 43 +++++-- spec/integration/impl_test.lua | 2 +- spec/integration/struct_tags_test.lua | 12 ++ spec/testutils.lua | 5 + 31 files changed, 346 insertions(+), 409 deletions(-) delete mode 100644 .github/workflows/docs.yml create mode 100644 lua/gopher/_utils/ts.lua delete mode 100644 lua/gopher/_utils/ts/init.lua delete mode 100644 lua/gopher/_utils/ts/nodes.lua create mode 100644 spec/fixtures/comment/empty_input.go create mode 100644 spec/fixtures/comment/empty_output.go create mode 100644 spec/fixtures/comment/func_input.go create mode 100644 spec/fixtures/comment/func_output.go create mode 100644 spec/fixtures/comment/interface_input.go create mode 100644 spec/fixtures/comment/interface_output.go create mode 100644 spec/fixtures/comment/method_input.go create mode 100644 spec/fixtures/comment/method_output.go create mode 100644 spec/fixtures/comment/struct_input.go create mode 100644 spec/fixtures/comment/struct_output.go create mode 100644 spec/fixtures/tags/many_input.go create mode 100644 spec/fixtures/tags/many_output.go diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 8a0335e..0000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: docs - -on: - push: - branches: - - main - - develop - pull_request: - -jobs: - docs: - name: linters - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Install Task - uses: arduino/setup-task@v1 - with: - version: 3.x - repo-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Install NeoVim - uses: rhysd/action-setup-vim@v1 - with: - neovim: true - version: stable - - - name: Cache .tests - uses: actions/cache@v4 - with: - path: | - ${{ github.workspace }}/.tests - key: ${{ runner.os }}-tests-${{ hashFiles('${{ github.workspace }}/.tests') }} - - - name: Generate docs - run: task docgen - - - name: Check docs diff - run: exit $(git status --porcelain doc | wc -l | tr -d " ") diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index ae5be14..175465e 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -9,7 +9,7 @@ on: jobs: linters: - name: linters + name: Lua runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -23,3 +23,34 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} args: . + + docs: + name: Docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Task + uses: arduino/setup-task@v1 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install NeoVim + uses: rhysd/action-setup-vim@v1 + with: + neovim: true + version: stable + + - name: Cache .tests + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/.tests + key: ${{ runner.os }}-tests-${{ hashFiles('${{ github.workspace }}/.tests') }} + + - name: Generate docs + run: task docgen + + - name: Diff + run: exit $(git status --porcelain doc | wc -l | tr -d " ") diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f5a767e..b575f94 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: os: [ubuntu-latest] version: - stable - - nightly + # - nightly # TODO: enable when stable runs-on: ${{ matrix.os }} steps: - name: Install Task diff --git a/lua/gopher/_utils/log.lua b/lua/gopher/_utils/log.lua index c7dccd7..9c22a4e 100644 --- a/lua/gopher/_utils/log.lua +++ b/lua/gopher/_utils/log.lua @@ -91,7 +91,7 @@ do local log_at_level = function(level_config, message_maker, ...) -- Return early if we're below the current_log_level -- - -- the log level source get from config directly because otherwise it doesnt work + -- the log level source get from config directly because otherwise it doesn't work if level_config.level < c.log_level then return end diff --git a/lua/gopher/_utils/runner/gocmd.lua b/lua/gopher/_utils/runner/gocmd.lua index 97323f9..7e14456 100644 --- a/lua/gopher/_utils/runner/gocmd.lua +++ b/lua/gopher/_utils/runner/gocmd.lua @@ -28,7 +28,7 @@ end ---@return string[]|nil function gocmd.run(subcmd, args) if #args == 0 then - error "please provice any arguments" + error "please provide any arguments" end if subcmd == "get" then @@ -45,7 +45,7 @@ function gocmd.run(subcmd, args) if status ~= 0 then error("gocmd failed: " .. data) end - u.notify(c.go .. " " .. subcmd .. " successful runned") + u.notify(c.go .. " " .. subcmd .. " ran successful") end, }) end diff --git a/lua/gopher/_utils/ts.lua b/lua/gopher/_utils/ts.lua new file mode 100644 index 0000000..9c74f47 --- /dev/null +++ b/lua/gopher/_utils/ts.lua @@ -0,0 +1,115 @@ +local ts = {} +local queries = { + struct = [[ + (type_spec name: (type_identifier) @_name + type: (struct_type)) + ]], + func = [[ + [(function_declaration name: (identifier) @_name) + (method_declaration name: (field_identifier) @_name)] + ]], + package = [[ + (package_identifier) @_name + ]], + interface = [[ + (type_spec + name: (type_identifier) @_name + type: (interface_type)) + ]], +} + +---@param parent_type string[] +---@param node TSNode +---@return TSNode? +local function get_parrent_node(parent_type, node) + ---@type TSNode? + local current = node + while current do + if vim.tbl_contains(parent_type, current:type()) then + break + end + + current = current:parent() + if current == nil then + return nil + end + end + return current +end + +---@param query vim.treesitter.Query +---@param node TSNode +---@param bufnr integer +---@return {name:string} +local function get_captures(query, node, bufnr) + local res = {} + for _, match, _ in query:iter_matches(node, bufnr) do + for capture_id, captured_node in pairs(match) do + local capture_name = query.captures[capture_id] + if capture_name == "_name" then + res["name"] = vim.treesitter.get_node_text(captured_node, bufnr) + end + end + end + + return res +end + +---@class gopher.TsResult +---@field name string +---@field start_line integer +---@field end_line integer + +---@param bufnr integer +---@param parent_type string[] +---@param query string +---@return gopher.TsResult +local function do_stuff(bufnr, parent_type, query) + local node = vim.treesitter.get_node { + bufnr = bufnr, + } + if not node then + error "No nodes found under cursor" + end + + local parent_node = get_parrent_node(parent_type, node) + if not parent_node then + error "No parent node found under cursor" + end + + local q = vim.treesitter.query.parse("go", query) + local res = get_captures(q, parent_node, bufnr) + assert(res.name ~= nil, "No capture name found") + + local start_row, _, end_row, _ = parent_node:range() + res["start_line"] = start_row + 1 + res["end_line"] = end_row + 1 + + return res +end + +---@param bufnr integer +function ts.get_struct_under_cursor(bufnr) + --- should be both type_spec and type_declaration + --- because in cases like `type ( T struct{}, U strict{} )` + --- i will be choosing always last struct in the list + return do_stuff(bufnr, { "type_spec", "type_declaration" }, queries.struct) +end + +---@param bufnr integer +function ts.get_func_under_cursor(bufnr) + --- since this handles both and funcs and methods we should check for both parent nodes + return do_stuff(bufnr, { "function_declaration", "method_declaration" }, queries.func) +end + +---@param bufnr integer +function ts.get_package_under_cursor(bufnr) + return do_stuff(bufnr, { "package_clause" }, queries.package) +end + +---@param bufnr integer +function ts.get_interface_under_cursor(bufnr) + return do_stuff(bufnr, { "type_declaration" }, queries.interface) +end + +return ts diff --git a/lua/gopher/_utils/ts/init.lua b/lua/gopher/_utils/ts/init.lua deleted file mode 100644 index aba3a94..0000000 --- a/lua/gopher/_utils/ts/init.lua +++ /dev/null @@ -1,104 +0,0 @@ ----@diagnostic disable: param-type-mismatch -local nodes = require "gopher._utils.ts.nodes" -local u = require "gopher._utils" -local ts = { - querys = { - struct_block = [[((type_declaration (type_spec name:(type_identifier) @struct.name type: (struct_type)))@struct.declaration)]], - em_struct_block = [[(field_declaration name:(field_identifier)@struct.name type: (struct_type)) @struct.declaration]], - package = [[(package_clause (package_identifier)@package.name)@package.clause]], - interface = [[((type_declaration (type_spec name:(type_identifier) @interface.name type:(interface_type)))@interface.declaration)]], - method_name = [[((method_declaration receiver: (parameter_list)@method.receiver name: (field_identifier)@method.name body:(block))@method.declaration)]], - func = [[((function_declaration name: (identifier)@function.name) @function.declaration)]], - }, -} - ----@return table -local function get_name_defaults() - return { - ["func"] = "function", - ["if"] = "if", - ["else"] = "else", - ["for"] = "for", - } -end - ----@param row string ----@param col string ----@param bufnr string|nil ----@param do_notify boolean|nil ----@return table|nil -function ts.get_struct_node_at_pos(row, col, bufnr, do_notify) - local notify = do_notify or true - local query = ts.querys.struct_block .. " " .. ts.querys.em_struct_block - local bufn = bufnr or vim.api.nvim_get_current_buf() - local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) - if ns == nil then - if notify then - u.deferred_notify("struct not found", vim.log.levels.WARN) - end - else - return ns[#ns] - end -end - ----@param row string ----@param col string ----@param bufnr string|nil ----@param do_notify boolean|nil ----@return table|nil -function ts.get_func_method_node_at_pos(row, col, bufnr, do_notify) - local notify = do_notify or true - local query = ts.querys.func .. " " .. ts.querys.method_name - local bufn = bufnr or vim.api.nvim_get_current_buf() - local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) - if ns == nil then - if notify then - u.deferred_notify("function not found", vim.log.levels.WARN) - end - else - return ns[#ns] - end -end - ----@param row string ----@param col string ----@param bufnr string|nil ----@param do_notify boolean|nil ----@return table|nil -function ts.get_package_node_at_pos(row, col, bufnr, do_notify) - local notify = do_notify or true - -- stylua: ignore - if row > 10 then return end - local query = ts.querys.package - local bufn = bufnr or vim.api.nvim_get_current_buf() - local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) - if ns == nil then - if notify then - u.deferred_notify("package not found", vim.log.levels.WARN) - return nil - end - else - return ns[#ns] - end -end - ----@param row string ----@param col string ----@param bufnr string|nil ----@param do_notify boolean|nil ----@return table|nil -function ts.get_interface_node_at_pos(row, col, bufnr, do_notify) - local notify = do_notify or true - local query = ts.querys.interface - local bufn = bufnr or vim.api.nvim_get_current_buf() - local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) - if ns == nil then - if notify then - u.deferred_notify("interface not found", vim.log.levels.WARN) - end - else - return ns[#ns] - end -end - -return ts diff --git a/lua/gopher/_utils/ts/nodes.lua b/lua/gopher/_utils/ts/nodes.lua deleted file mode 100644 index e9c730d..0000000 --- a/lua/gopher/_utils/ts/nodes.lua +++ /dev/null @@ -1,143 +0,0 @@ -local ts_query = require "nvim-treesitter.query" -local parsers = require "nvim-treesitter.parsers" -local locals = require "nvim-treesitter.locals" -local u = require "gopher._utils" -local M = {} - -local function intersects(row, col, sRow, sCol, eRow, eCol) - if sRow > row or eRow < row then - return false - end - - if sRow == row and sCol > col then - return false - end - - if eRow == row and eCol < col then - return false - end - - return true -end - ----@param nodes table ----@param row string ----@param col string ----@return table -function M.intersect_nodes(nodes, row, col) - local found = {} - for idx = 1, #nodes do - local node = nodes[idx] - local sRow = node.dim.s.r - local sCol = node.dim.s.c - local eRow = node.dim.e.r - local eCol = node.dim.e.c - - if intersects(row, col, sRow, sCol, eRow, eCol) then - table.insert(found, node) - end - end - - return found -end - ----@param nodes table ----@return table -function M.sort_nodes(nodes) - table.sort(nodes, function(a, b) - return M.count_parents(a) < M.count_parents(b) - end) - - return nodes -end - ----@param query string ----@param lang string ----@param bufnr integer ----@param pos_row string ----@return string -function M.get_all_nodes(query, lang, _, bufnr, pos_row, _) - bufnr = bufnr or 0 - pos_row = pos_row or 30000 - - local ok, parsed_query = pcall(function() - return vim.treesitter.query.parse(lang, query) - end) - if not ok then - return nil - end - - local parser = parsers.get_parser(bufnr, lang) - local root = parser:parse()[1]:root() - local start_row, _, end_row, _ = root:range() - local results = {} - - for match in ts_query.iter_prepared_matches(parsed_query, root, bufnr, start_row, end_row) do - local sRow, sCol, eRow, eCol, declaration_node - local type, name, op = "", "", "" - locals.recurse_local_nodes(match, function(_, node, path) - local idx = string.find(path, ".[^.]*$") - op = string.sub(path, idx + 1, #path) - type = string.sub(path, 1, idx - 1) - - if op == "name" then - name = vim.treesitter.get_node_text(node, bufnr) - elseif op == "declaration" or op == "clause" then - declaration_node = node - sRow, sCol, eRow, eCol = node:range() - sRow = sRow + 1 - eRow = eRow + 1 - sCol = sCol + 1 - eCol = eCol + 1 - end - end) - - if declaration_node ~= nil then - table.insert(results, { - declaring_node = declaration_node, - dim = { s = { r = sRow, c = sCol }, e = { r = eRow, c = eCol } }, - name = name, - operator = op, - type = type, - }) - end - end - - return results -end - ----@param query string ----@param default string ----@param bufnr string ----@param row string ----@param col string ----@return table -function M.nodes_at_cursor(query, default, bufnr, row, col) - bufnr = bufnr or vim.api.nvim_get_current_buf() - local ft = vim.api.nvim_buf_get_option(bufnr, "ft") - if row == nil or col == nil then - row, col = unpack(vim.api.nvim_win_get_cursor(0)) - end - - local nodes = M.get_all_nodes(query, ft, default, bufnr, row, col) - if nodes == nil then - u.deferred_notify( - "Unable to find any nodes. Place your cursor on a go symbol and try again", - vim.log.levels.DEBUG - ) - return nil - end - - nodes = M.sort_nodes(M.intersect_nodes(nodes, row, col)) - if nodes == nil or #nodes == 0 then - u.deferred_notify( - "Unable to find any nodes at pos. " .. tostring(row) .. ":" .. tostring(col), - vim.log.levels.DEBUG - ) - return nil - end - - return nodes -end - -return M diff --git a/lua/gopher/comment.lua b/lua/gopher/comment.lua index 8754405..6f68a3f 100644 --- a/lua/gopher/comment.lua +++ b/lua/gopher/comment.lua @@ -3,57 +3,53 @@ ---@usage Execute `:GoCmt` to generate a comment for the current function/method/struct/etc on this line. ---@text This module provides a way to generate comments for Go code. +local ts = require "gopher._utils.ts" local log = require "gopher._utils.log" +local comment = {} -local function generate(row, col) - local ts_utils = require "gopher._utils.ts" - local comment, ns = nil, nil - - ns = ts_utils.get_package_node_at_pos(row, col, nil, false) - if ns ~= nil then - comment = "// Package " .. ns.name .. " provides " .. ns.name - return comment, ns - end - - ns = ts_utils.get_struct_node_at_pos(row, col, nil, false) - if ns ~= nil then - comment = "// " .. ns.name .. " " .. ns.type .. " " - return comment, ns - end - - ns = ts_utils.get_func_method_node_at_pos(row, col, nil, false) - if ns ~= nil then - comment = "// " .. ns.name .. " " .. ns.type .. " " - return comment, ns - end - - ns = ts_utils.get_interface_node_at_pos(row, col, nil, false) - if ns ~= nil then - comment = "// " .. ns.name .. " " .. ns.type .. " " - return comment, ns - end - - return "// ", {} +---@param name string +---@return string +---@private +local function template(name) + return "// " .. name .. " " end -return function() - local row, col = unpack(vim.api.nvim_win_get_cursor(0)) - local comment, ns = generate(row + 1, col + 1) +---@param bufnr integer +---@return string +---@private +local function generate(bufnr) + local s_ok, s_res = pcall(ts.get_struct_under_cursor, bufnr) + if s_ok then + return template(s_res.name) + end - log.debug("generated comment: " .. comment) + local f_ok, f_res = pcall(ts.get_func_under_cursor, bufnr) + if f_ok then + return template(f_res.name) + end - vim.api.nvim_win_set_cursor(0, { - ns.dim.s.r, - ns.dim.s.c, - }) + local i_ok, i_res = pcall(ts.get_interface_under_cursor, bufnr) + if i_ok then + return template(i_res.name) + end - ---@diagnostic disable-next-line: param-type-mismatch - vim.fn.append(row - 1, comment) + local p_ok, p_res = pcall(ts.get_package_under_cursor, bufnr) + if p_ok then + return "// Package " .. p_res.name .. " provides " + end - vim.api.nvim_win_set_cursor(0, { - ns.dim.s.r, - #comment + 1, - }) - - vim.cmd [[startinsert!]] + return "// " end + +function comment.comment() + local bufnr = vim.api.nvim_get_current_buf() + local cmt = generate(bufnr) + log.debug("generated comment: " .. cmt) + + local pos = vim.fn.getcurpos()[2] + vim.fn.append(pos - 1, cmt) + vim.fn.setpos(".", { 0, pos, #cmt }) + vim.cmd "startinsert!" +end + +return comment diff --git a/lua/gopher/gotests.lua b/lua/gopher/gotests.lua index e97f26c..deab8b3 100644 --- a/lua/gopher/gotests.lua +++ b/lua/gopher/gotests.lua @@ -77,13 +77,10 @@ end -- generate unit test for one function function gotests.func_test() - local ns = ts_utils.get_func_method_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0))) - if ns == nil or ns.name == nil then - u.notify("cursor on func/method and execute the command again", vim.log.levels.WARN) - return - end + local bufnr = vim.api.nvim_get_current_buf() + local func = ts_utils.get_func_under_cursor(bufnr) - add_test { "-only", ns.name } + add_test { "-only", func.name } end -- generate unit tests for all functions in current file diff --git a/lua/gopher/impl.lua b/lua/gopher/impl.lua index 0ed3e76..3a731bf 100644 --- a/lua/gopher/impl.lua +++ b/lua/gopher/impl.lua @@ -38,48 +38,22 @@ local ts_utils = require "gopher._utils.ts" local u = require "gopher._utils" local impl = {} ----@return string ----@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.notify "put cursor on a struct or specify a receiver" - return "" - end - - vim.api.nvim_win_set_cursor(0, { - ns.dim.e.r, - ns.dim.e.c, - }) - - return ns.name -end - function impl.impl(...) local args = { ... } - local iface, recv_name = "", "" - local recv = get_struct() + local iface, recv = "", "" + local bufnr = vim.api.nvim_get_current_buf() - if #args == 0 then - iface = vim.fn.input "impl: generating method stubs for interface: " - vim.cmd "redraw!" - if iface == "" then - u.deferred_notify("usage: GoImpl f *File io.Reader", vim.log.levels.INFO) - return - end - elseif #args == 1 then -- :GoImpl io.Reader - recv = string.lower(recv) .. " *" .. recv - vim.cmd "redraw!" - iface = select(1, ...) + if #args == 1 then -- :GoImpl io.Reader + local st = ts_utils.get_struct_under_cursor(bufnr) + iface = args[1] + recv = string.lower(st.name) .. " *" .. st.name elseif #args == 2 then -- :GoImpl w io.Writer - recv_name = select(1, ...) - recv = string.format("%s *%s", recv_name, recv) - iface = select(#args, ...) - elseif #args > 2 then - iface = select(#args, ...) - recv = select(#args - 1, ...) - recv_name = select(#args - 2, ...) - recv = string.format("%s %s", recv_name, recv) + local st = ts_utils.get_struct_under_cursor(bufnr) + iface = args[2] + recv = args[1] .. " *" .. st.name + elseif #args == 3 then -- :GoImpl r Struct io.Reader + recv = args[1] .. " *" .. args[2] + iface = args[3] end local rs = r.sync { c.impl, "-dir", vim.fn.fnameescape(vim.fn.expand "%:p:h"), recv, iface } diff --git a/lua/gopher/init.lua b/lua/gopher/init.lua index e18a8bd..0de883b 100644 --- a/lua/gopher/init.lua +++ b/lua/gopher/init.lua @@ -38,7 +38,7 @@ gopher.install_deps = require("gopher.installer").install_deps gopher.impl = require("gopher.impl").impl gopher.iferr = require("gopher.iferr").iferr -gopher.comment = require "gopher.comment" +gopher.comment = require("gopher.comment").comment gopher.tags = { add = tags.add, diff --git a/lua/gopher/struct_tags.lua b/lua/gopher/struct_tags.lua index 703b296..73c7612 100644 --- a/lua/gopher/struct_tags.lua +++ b/lua/gopher/struct_tags.lua @@ -33,23 +33,11 @@ local struct_tags = {} local function modify(...) local fpath = vim.fn.expand "%" ---@diagnostic disable-line: missing-parameter - local ns = ts_utils.get_struct_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0))) - if ns == nil then - return - end - - -- by struct name of line pos - local cmd_args = {} - if ns.name == nil then - local _, csrow, _, _ = unpack(vim.fn.getpos ".") - table.insert(cmd_args, "-line") - table.insert(cmd_args, csrow) - else - table.insert(cmd_args, "-struct") - table.insert(cmd_args, ns.name) - end + local bufnr = vim.api.nvim_get_current_buf() + local struct = ts_utils.get_struct_under_cursor(bufnr) -- set user args for cmd + local cmd_args = {} local arg = { ... } for _, v in ipairs(arg) do table.insert(cmd_args, v) @@ -61,6 +49,8 @@ local function modify(...) c.gotag.transform, "-format", "json", + "-struct", + struct.name, "-w", "-file", fpath, @@ -96,7 +86,6 @@ end function struct_tags.add(...) local user_tags = { ... } if #user_tags == 0 then - vim.print("c.gotag.default_tag", c.gotag.default_tag) user_tags = { c.gotag.default_tag } end diff --git a/spec/fixtures/comment/empty_input.go b/spec/fixtures/comment/empty_input.go new file mode 100644 index 0000000..e69de29 diff --git a/spec/fixtures/comment/empty_output.go b/spec/fixtures/comment/empty_output.go new file mode 100644 index 0000000..aa56d2c --- /dev/null +++ b/spec/fixtures/comment/empty_output.go @@ -0,0 +1,2 @@ +// + diff --git a/spec/fixtures/comment/func_input.go b/spec/fixtures/comment/func_input.go new file mode 100644 index 0000000..aec80b0 --- /dev/null +++ b/spec/fixtures/comment/func_input.go @@ -0,0 +1,5 @@ +package main + +func Test(a int) bool { + return false +} diff --git a/spec/fixtures/comment/func_output.go b/spec/fixtures/comment/func_output.go new file mode 100644 index 0000000..7dc39e5 --- /dev/null +++ b/spec/fixtures/comment/func_output.go @@ -0,0 +1,6 @@ +package main + +// Test +func Test(a int) bool { + return false +} diff --git a/spec/fixtures/comment/interface_input.go b/spec/fixtures/comment/interface_input.go new file mode 100644 index 0000000..c065125 --- /dev/null +++ b/spec/fixtures/comment/interface_input.go @@ -0,0 +1,3 @@ +package main + +type Testinger interface{} diff --git a/spec/fixtures/comment/interface_output.go b/spec/fixtures/comment/interface_output.go new file mode 100644 index 0000000..689bc6d --- /dev/null +++ b/spec/fixtures/comment/interface_output.go @@ -0,0 +1,4 @@ +package main + +// Testinger +type Testinger interface{} diff --git a/spec/fixtures/comment/method_input.go b/spec/fixtures/comment/method_input.go new file mode 100644 index 0000000..39f715c --- /dev/null +++ b/spec/fixtures/comment/method_input.go @@ -0,0 +1,7 @@ +package main + +type Method struct{} + +func (Method) Run() error { + return nil +} diff --git a/spec/fixtures/comment/method_output.go b/spec/fixtures/comment/method_output.go new file mode 100644 index 0000000..2ffdf87 --- /dev/null +++ b/spec/fixtures/comment/method_output.go @@ -0,0 +1,8 @@ +package main + +type Method struct{} + +// Run +func (Method) Run() error { + return nil +} diff --git a/spec/fixtures/comment/package_output.go b/spec/fixtures/comment/package_output.go index 66d106a..3721c5c 100644 --- a/spec/fixtures/comment/package_output.go +++ b/spec/fixtures/comment/package_output.go @@ -1,2 +1,2 @@ -// Package main provides main +// Package main provides package main diff --git a/spec/fixtures/comment/struct_input.go b/spec/fixtures/comment/struct_input.go new file mode 100644 index 0000000..98e8561 --- /dev/null +++ b/spec/fixtures/comment/struct_input.go @@ -0,0 +1,3 @@ +package main + +type CommentStruct struct{} diff --git a/spec/fixtures/comment/struct_output.go b/spec/fixtures/comment/struct_output.go new file mode 100644 index 0000000..14e279d --- /dev/null +++ b/spec/fixtures/comment/struct_output.go @@ -0,0 +1,4 @@ +package main + +// CommentStruct +type CommentStruct struct{} diff --git a/spec/fixtures/impl/reader_output.go b/spec/fixtures/impl/reader_output.go index c384b23..2f948c4 100644 --- a/spec/fixtures/impl/reader_output.go +++ b/spec/fixtures/impl/reader_output.go @@ -1,6 +1,6 @@ package main -func (r Read2) Read(p []byte) (n int, err error) { +func (r *Read2) Read(p []byte) (n int, err error) { panic("not implemented") // TODO: Implement } diff --git a/spec/fixtures/tags/many_input.go b/spec/fixtures/tags/many_input.go new file mode 100644 index 0000000..f5c6367 --- /dev/null +++ b/spec/fixtures/tags/many_input.go @@ -0,0 +1,18 @@ +package main + +type ( + TestOne struct { + Asdf string + ID int + } + + TestTwo struct { + Fesa int + A bool + } + + TestThree struct { + Asufj int + Fs string + } +) diff --git a/spec/fixtures/tags/many_output.go b/spec/fixtures/tags/many_output.go new file mode 100644 index 0000000..36877b8 --- /dev/null +++ b/spec/fixtures/tags/many_output.go @@ -0,0 +1,18 @@ +package main + +type ( + TestOne struct { + Asdf string + ID int + } + + TestTwo struct { + Fesa int `testing:"fesa"` + A bool `testing:"a"` + } + + TestThree struct { + Asufj int + Fs string + } +) diff --git a/spec/integration/comment_test.lua b/spec/integration/comment_test.lua index 8276688..9cad5f7 100644 --- a/spec/integration/comment_test.lua +++ b/spec/integration/comment_test.lua @@ -5,23 +5,50 @@ local T = MiniTest.new_set { hooks = { post_once = child.stop, pre_case = function() - MiniTest.skip "This module should be fixed first" child.restart { "-u", t.mininit_path } end, }, } + +local function do_the_test(fixture, pos) + local tmp = t.tmpfile() + local fixtures = t.get_fixtures("comment/" .. fixture) + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr "%", unpack(pos) }) + child.cmd "GoCmt" + child.cmd "write" + + t.eq(t.readfile(tmp), fixtures.output) + + -- without it all other(not even from this module) tests are falling + t.deletefile(tmp) +end + T["comment"] = MiniTest.new_set {} +T["comment"]["should add comment to package"] = function() + do_the_test("package", { 1, 1 }) +end -T["comment"]["should add comment to package"] = function() end +T["comment"]["should add comment to struct"] = function() + do_the_test("struct", { 4, 1 }) +end -T["comment"]["should add comment to struct"] = function() end +T["comment"]["should add comment to function"] = function() + do_the_test("func", { 3, 1 }) +end -T["comment"]["should add comment to function"] = function() end +T["comment"]["should add comment to method"] = function() + do_the_test("method", { 5, 1 }) +end -T["comment"]["should add comment to method"] = function() end +T["comment"]["should add comment to interface"] = function() + do_the_test("interface", { 3, 6 }) +end -T["comment"]["should add comment to interface"] = function() end - -T["comment"]["otherwise should add // above cursor"] = function() end +T["comment"]["otherwise should add // above cursor"] = function() + do_the_test("empty", { 1, 1 }) +end return T diff --git a/spec/integration/impl_test.lua b/spec/integration/impl_test.lua index 5376fa5..bbbbf6f 100644 --- a/spec/integration/impl_test.lua +++ b/spec/integration/impl_test.lua @@ -16,7 +16,7 @@ T["impl"]["works w io.Writer"] = function() t.writefile(tmp, fixtures.input) child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 6 }) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 0 }) child.cmd "GoImpl w io.Writer" child.cmd "write" diff --git a/spec/integration/struct_tags_test.lua b/spec/integration/struct_tags_test.lua index 8fb4aa3..e8f8606 100644 --- a/spec/integration/struct_tags_test.lua +++ b/spec/integration/struct_tags_test.lua @@ -34,4 +34,16 @@ T["struct_tags"]["works remove"] = function() t.eq(t.readfile(tmp), fixtures.output) end +T["struct_tags"]["works many structs"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tags/many" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr "%", 10, 3, 0 }) + child.cmd "GoTagAdd testing" + + t.eq(t.readfile(tmp), fixtures.output) +end + return T diff --git a/spec/testutils.lua b/spec/testutils.lua index e5ab9a2..32c19ab 100644 --- a/spec/testutils.lua +++ b/spec/testutils.lua @@ -31,6 +31,11 @@ function testutils.writefile(fpath, contents) vim.fn.writefile(vim.split(contents, "\n"), fpath) end +---@param fpath string +function testutils.deletefile(fpath) + vim.fn.delete(fpath) +end + ---@param fixture string ---@return {input: string, output: string} function testutils.get_fixtures(fixture) From 55bc5787d183e5b00f07ac14fa2b78044e0d4fd8 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Wed, 19 Mar 2025 18:06:33 +0200 Subject: [PATCH 11/25] refactor: struct tags (#94) * refactor(struct_tags): finally my hands got to this * feat(struct_tags): trim output * feat(struct_tags): add :GoTagClear * docgen * refactor(struct_tags): error on out-of-bounds * feat(struct_tags): add support for working with multiple tags at the once * test(struct_tags): test both possible inputs * refactor(struct_tags): add type annotation, dont force write * refactor(struct_tags): optimization ig * docs: fix * fixup! refactor(struct_tags): add type annotation, dont force write * task docgen --------- Co-authored-by: Oliver <1571880470@qq.com> --- doc/gopher.nvim.txt | 19 ++++ lua/gopher/init.lua | 1 + lua/gopher/struct_tags.lua | 122 ++++++++++++++------------ plugin/gopher.vim | 1 + spec/fixtures/tags/add_many_input.go | 11 +++ spec/fixtures/tags/add_many_output.go | 11 +++ spec/fixtures/tags/clear_input.go | 11 +++ spec/fixtures/tags/clear_output.go | 11 +++ spec/fixtures/tags/remove_output.go | 12 +-- spec/integration/struct_tags_test.lua | 48 ++++++++-- 10 files changed, 177 insertions(+), 70 deletions(-) create mode 100644 spec/fixtures/tags/add_many_input.go create mode 100644 spec/fixtures/tags/add_many_output.go create mode 100644 spec/fixtures/tags/clear_input.go create mode 100644 spec/fixtures/tags/clear_output.go diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index 4c3fd65..377a4f4 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -129,6 +129,25 @@ Example: Name string `yaml:name` } < +------------------------------------------------------------------------------ + *struct_tags.add()* + `struct_tags.add`({...}) +tags to a struct under the cursor +Parameters ~ +{...} `(string)` Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] + +------------------------------------------------------------------------------ + *struct_tags.remove()* + `struct_tags.remove`({...}) +tags from a struct under the cursor +Parameters ~ +{...} `(string)` Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] + +------------------------------------------------------------------------------ + *struct_tags.clear()* + `struct_tags.clear`() +all tags from a struct under the cursor + ============================================================================== ------------------------------------------------------------------------------ diff --git a/lua/gopher/init.lua b/lua/gopher/init.lua index 0de883b..bb9888b 100644 --- a/lua/gopher/init.lua +++ b/lua/gopher/init.lua @@ -43,6 +43,7 @@ gopher.comment = require("gopher.comment").comment gopher.tags = { add = tags.add, rm = tags.remove, + clear = tags.clear, } gopher.test = { diff --git a/lua/gopher/struct_tags.lua b/lua/gopher/struct_tags.lua index 73c7612..4545640 100644 --- a/lua/gopher/struct_tags.lua +++ b/lua/gopher/struct_tags.lua @@ -26,90 +26,96 @@ --- } --- < -local ts_utils = require "gopher._utils.ts" +local ts = require "gopher._utils.ts" local r = require "gopher._utils.runner" local c = require "gopher.config" +local log = require "gopher._utils.log" local struct_tags = {} -local function modify(...) - local fpath = vim.fn.expand "%" ---@diagnostic disable-line: missing-parameter - local bufnr = vim.api.nvim_get_current_buf() - local struct = ts_utils.get_struct_under_cursor(bufnr) +---@param fpath string +---@param bufnr integer +---@param user_args string[] +---@private +local function handle_tags(fpath, bufnr, user_args) + local st = ts.get_struct_under_cursor(bufnr) - -- set user args for cmd - local cmd_args = {} - local arg = { ... } - for _, v in ipairs(arg) do - table.insert(cmd_args, v) - end - - local rs = r.sync { + -- stylua: ignore + local cmd = { c.commands.gomodifytags, - "-transform", - c.gotag.transform, - "-format", - "json", - "-struct", - struct.name, + "-transform", c.gotag.transform, + "-format", "json", + "-struct", st.name, + "-file", fpath, "-w", - "-file", - fpath, - unpack(cmd_args), } + for _, v in ipairs(user_args) do + table.insert(cmd, v) + end + + local rs = r.sync(cmd) if rs.code ~= 0 then + log.error("tags: failed to set tags " .. rs.stderr) error("failed to set tags " .. rs.stderr) end - -- decode value - local tagged = vim.json.decode(rs.stdout) - if - tagged.errors ~= nil - or tagged.lines == nil - or tagged["start"] == nil - or tagged["start"] == 0 - then - error("failed to set tags " .. vim.inspect(tagged)) + local res = vim.json.decode(rs.stdout) + + if res["errors"] then + log.error("tags: got an error " .. vim.inspect(res)) + error("failed to set tags " .. vim.inspect(res["errors"])) + end + + for i, v in ipairs(res["lines"]) do + res["lines"][i] = string.gsub(v, "%s+$", "") end vim.api.nvim_buf_set_lines( - 0, - tagged.start - 1, - tagged.start - 1 + #tagged.lines, - false, - tagged.lines + bufnr, + res["start"] - 1, + res["start"] - 1 + #res["lines"], + true, + res["lines"] ) - vim.cmd "write" end --- add tags to struct under cursor +---@param args string[] +---@return string +---@private +local function handler_user_args(args) + if #args == 0 then + return c.gotag.default_tag + end + return table.concat(args, ",") +end + +---Adds tags to a struct under the cursor +---@param ... string Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] function struct_tags.add(...) - local user_tags = { ... } - if #user_tags == 0 then - user_tags = { c.gotag.default_tag } - end + local args = { ... } + local fpath = vim.fn.expand "%" + local bufnr = vim.api.nvim_get_current_buf() - local cmd_args = { "-add-tags" } - for _, v in ipairs(user_tags) do - table.insert(cmd_args, v) - end - - modify(unpack(cmd_args)) + local user_tags = handler_user_args(args) + handle_tags(fpath, bufnr, { "-add-tags", user_tags }) end --- remove tags to struct under cursor +---Removes tags from a struct under the cursor +---@param ... string Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] function struct_tags.remove(...) - local user_tags = { ... } - if #user_tags == 0 then - user_tags = { c.gotag.default_tag } - end + local args = { ... } + local fpath = vim.fn.expand "%" + local bufnr = vim.api.nvim_get_current_buf() - local cmd_args = { "-remove-tags" } - for _, v in ipairs(user_tags) do - table.insert(cmd_args, v) - end + local user_tags = handler_user_args(args) + handle_tags(fpath, bufnr, { "-remove-tags", user_tags }) +end - modify(unpack(cmd_args)) +---Removes all tags from a struct under the cursor +function struct_tags.clear() + local fpath = vim.fn.expand "%" + local bufnr = vim.api.nvim_get_current_buf() + handle_tags(fpath, bufnr, { "-clear-tags" }) end return struct_tags diff --git a/plugin/gopher.vim b/plugin/gopher.vim index b61ed6e..dc924b9 100644 --- a/plugin/gopher.vim +++ b/plugin/gopher.vim @@ -1,5 +1,6 @@ command! -nargs=* GoTagAdd :lua require"gopher".tags.add() command! -nargs=* GoTagRm :lua require"gopher".tags.rm() +command! GoTagClear :lua require"gopher".tags.clear() command! GoTestAdd :lua require"gopher".test.add() command! GoTestsAll :lua require"gopher".test.all() command! GoTestsExp :lua require"gopher".test.exported() diff --git a/spec/fixtures/tags/add_many_input.go b/spec/fixtures/tags/add_many_input.go new file mode 100644 index 0000000..7e27a27 --- /dev/null +++ b/spec/fixtures/tags/add_many_input.go @@ -0,0 +1,11 @@ +package main + +type Test struct { + ID int + Name string + Num int64 + Another struct { + First int + Second string + } +} diff --git a/spec/fixtures/tags/add_many_output.go b/spec/fixtures/tags/add_many_output.go new file mode 100644 index 0000000..9a43e62 --- /dev/null +++ b/spec/fixtures/tags/add_many_output.go @@ -0,0 +1,11 @@ +package main + +type Test struct { + ID int `test4:"id" test5:"id" test1:"id" test2:"id"` + Name string `test4:"name" test5:"name" test1:"name" test2:"name"` + Num int64 `test4:"num" test5:"num" test1:"num" test2:"num"` + Another struct { + First int `test4:"first" test5:"first" test1:"first" test2:"first"` + Second string `test4:"second" test5:"second" test1:"second" test2:"second"` + } `test4:"another" test5:"another" test1:"another" test2:"another"` +} diff --git a/spec/fixtures/tags/clear_input.go b/spec/fixtures/tags/clear_input.go new file mode 100644 index 0000000..050b327 --- /dev/null +++ b/spec/fixtures/tags/clear_input.go @@ -0,0 +1,11 @@ +package main + +type Test struct { + ID int `json:"id" yaml:"id" xml:"id" db:"id"` + Name string `json:"name" yaml:"name" xml:"name" db:"name"` + Num int64 `json:"num" yaml:"num" xml:"num" db:"num"` + Another struct { + First int `json:"first" yaml:"first" xml:"first" db:"first"` + Second string `json:"second" yaml:"second" xml:"second" db:"second"` + } `json:"another" yaml:"another" xml:"another" db:"another"` +} diff --git a/spec/fixtures/tags/clear_output.go b/spec/fixtures/tags/clear_output.go new file mode 100644 index 0000000..7e27a27 --- /dev/null +++ b/spec/fixtures/tags/clear_output.go @@ -0,0 +1,11 @@ +package main + +type Test struct { + ID int + Name string + Num int64 + Another struct { + First int + Second string + } +} diff --git a/spec/fixtures/tags/remove_output.go b/spec/fixtures/tags/remove_output.go index 6d9fe70..7e27a27 100644 --- a/spec/fixtures/tags/remove_output.go +++ b/spec/fixtures/tags/remove_output.go @@ -1,11 +1,11 @@ package main type Test struct { - ID int - Name string - Num int64 + ID int + Name string + Num int64 Another struct { - First int - Second string - } + First int + Second string + } } diff --git a/spec/integration/struct_tags_test.lua b/spec/integration/struct_tags_test.lua index e8f8606..4ac245b 100644 --- a/spec/integration/struct_tags_test.lua +++ b/spec/integration/struct_tags_test.lua @@ -10,38 +10,74 @@ local T = MiniTest.new_set { }, } T["struct_tags"] = MiniTest.new_set {} -T["struct_tags"]["works add"] = function() +T["struct_tags"]["should add tag"] = function() local tmp = t.tmpfile() local fixtures = t.get_fixtures "tags/add" t.writefile(tmp, fixtures.input) child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr "%", 3, 6, 0 }) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 6, 0 }) child.cmd "GoTagAdd json" + child.cmd "write" t.eq(t.readfile(tmp), fixtures.output) end -T["struct_tags"]["works remove"] = function() +T["struct_tags"]["should remove tag"] = function() local tmp = t.tmpfile() local fixtures = t.get_fixtures "tags/remove" t.writefile(tmp, fixtures.input) child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr "%", 4, 6, 0 }) + child.fn.setpos(".", { child.fn.bufnr(tmp), 4, 6, 0 }) child.cmd "GoTagRm json" + child.cmd "write" t.eq(t.readfile(tmp), fixtures.output) end -T["struct_tags"]["works many structs"] = function() +T["struct_tags"]["should be able to handle many structs"] = function() local tmp = t.tmpfile() local fixtures = t.get_fixtures "tags/many" t.writefile(tmp, fixtures.input) child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr "%", 10, 3, 0 }) + child.fn.setpos(".", { child.fn.bufnr(tmp), 10, 3, 0 }) child.cmd "GoTagAdd testing" + child.cmd "write" + + t.eq(t.readfile(tmp), fixtures.output) +end + +T["struct_tags"]["should clear struct"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tags/clear" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 1, 0 }) + child.cmd "GoTagClear" + child.cmd "write" + + t.eq(t.readfile(tmp), fixtures.output) +end + +T["struct_tags"]["should add more than one tag"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tags/add_many" + t.writefile(tmp, fixtures.input) + + --- with comma, like gomodifytags + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 1 }) + child.cmd "GoTagAdd test4,test5" + child.cmd "write" + + -- without comma + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 1 }) + child.cmd "GoTagAdd test1 test2" + child.cmd "write" t.eq(t.readfile(tmp), fixtures.output) end From c5cc5080fa75e1876fcb4b34c4b00810a53b0d50 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Fri, 21 Mar 2025 00:54:04 +0200 Subject: [PATCH 12/25] refactor(installer): install gotests@develop by default (#95) * refactor(installer): automatically install gotests@develop * docs: update --- README.md | 1 - doc/gopher.nvim.txt | 23 ++++------------------- lua/gopher/config.lua | 1 - lua/gopher/gotests.lua | 19 +------------------ lua/gopher/init.lua | 4 +++- lua/gopher/installer.lua | 11 +++++------ 6 files changed, 13 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index a6f59db..1dfff49 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,6 @@ require("gopher").setup { -- path to a directory containing custom test code templates template_dir = nil, -- switch table tests from using slice to map (with test name for the key) - -- works only with gotests installed from develop branch named = false, }, gotag = { diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index 377a4f4..a266fc9 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -22,7 +22,8 @@ Table of Contents `gopher.setup`({user_config}) Setup function. This method simply merges default config with opts table. You can read more about configuration at |gopher.nvim-config| -Calling this function is optional, if you ok with default settings. Look |gopher.nvim.config-defaults| +Calling this function is optional, if you ok with default settings. +See |gopher.nvim.config-defaults| Usage ~ `require("gopher").setup {}` (replace `{}` with your `config` table) @@ -35,6 +36,7 @@ Parameters ~ Gopher.nvim implements most of its features using third-party tools. To install these tools, you can run `:GoInstallDeps` command or call `require("gopher").install_deps()` if you want to use lua api. +By default dependencies will be installed asynchronously, to install them synchronously pass `{sync = true}` as an argument. ============================================================================== @@ -77,7 +79,6 @@ You can look at default options |gopher.nvim-config-defaults| ---@type string|nil template_dir = nil, -- switch table tests from using slice to map (with test name for the key) - -- works only with gotests installed from develop branch named = false, }, ---@class gopher.ConfigGoTag @@ -208,24 +209,8 @@ More details about templates can be found at: https://github.com/cweill/gotests *gopher.nvim-gotests-named* You can enable named tests in the config if you prefer using named tests. -But you must install `gotests@develop` because the stable version doesn't support this feature. +See |gopher.nvim-config|. ->lua - -- simply run go get in your shell: - go install github.com/cweill/gotests/...@develop - - -- if you want to install it within neovim, you can use one of this: - -- if you choose to install gotests this way i reocmmend adding it to your `build` section in your |lazy.nvim| - - vim.fn.jobstart("go install github.com/cweill/gotests/...@develop") - - -- or if you want to use mason: - require("mason-tool-installer").setup { - ensure_installed = { - { "gotests", version = "develop" }, - } - } -< ============================================================================== ------------------------------------------------------------------------------ diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index 7c67773..74cbff3 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -55,7 +55,6 @@ local default_config = { ---@type string|nil template_dir = nil, -- switch table tests from using slice to map (with test name for the key) - -- works only with gotests installed from develop branch named = false, }, ---@class gopher.ConfigGoTag diff --git a/lua/gopher/gotests.lua b/lua/gopher/gotests.lua index deab8b3..c5dac08 100644 --- a/lua/gopher/gotests.lua +++ b/lua/gopher/gotests.lua @@ -19,24 +19,7 @@ ---@tag gopher.nvim-gotests-named ---@text --- You can enable named tests in the config if you prefer using named tests. ---- But you must install `gotests@develop` because the stable version doesn't support this feature. ---- ---- >lua ---- -- simply run go get in your shell: ---- go install github.com/cweill/gotests/...@develop ---- ---- -- if you want to install it within neovim, you can use one of this: ---- -- if you choose to install gotests this way i reocmmend adding it to your `build` section in your |lazy.nvim| ---- ---- vim.fn.jobstart("go install github.com/cweill/gotests/...@develop") ---- ---- -- or if you want to use mason: ---- require("mason-tool-installer").setup { ---- ensure_installed = { ---- { "gotests", version = "develop" }, ---- } ---- } ---- < +--- See |gopher.nvim-config|. local c = require "gopher.config" local ts_utils = require "gopher._utils.ts" diff --git a/lua/gopher/init.lua b/lua/gopher/init.lua index bb9888b..e1d136c 100644 --- a/lua/gopher/init.lua +++ b/lua/gopher/init.lua @@ -19,7 +19,8 @@ local gopher = {} ---@tag gopher.nvim-setup ---@text Setup function. This method simply merges default config with opts table. --- You can read more about configuration at |gopher.nvim-config| ---- Calling this function is optional, if you ok with default settings. Look |gopher.nvim.config-defaults| +--- Calling this function is optional, if you ok with default settings. +--- See |gopher.nvim.config-defaults| --- ---@usage `require("gopher").setup {}` (replace `{}` with your `config` table) ---@param user_config gopher.Config @@ -34,6 +35,7 @@ end ---@text Gopher.nvim implements most of its features using third-party tools. --- To install these tools, you can run `:GoInstallDeps` command --- or call `require("gopher").install_deps()` if you want to use lua api. +--- By default dependencies will be installed asynchronously, to install them synchronously pass `{sync = true}` as an argument. gopher.install_deps = require("gopher.installer").install_deps gopher.impl = require("gopher.impl").impl diff --git a/lua/gopher/installer.lua b/lua/gopher/installer.lua index 526e688..adcbec6 100644 --- a/lua/gopher/installer.lua +++ b/lua/gopher/installer.lua @@ -5,10 +5,10 @@ local log = require "gopher._utils.log" local installer = {} local urls = { - gomodifytags = "github.com/fatih/gomodifytags", - impl = "github.com/josharian/impl", - gotests = "github.com/cweill/gotests/...", - iferr = "github.com/koron/iferr", + gomodifytags = "github.com/fatih/gomodifytags@latest", + impl = "github.com/josharian/impl@latest", + gotests = "github.com/cweill/gotests/...@develop", + iferr = "github.com/koron/iferr@latest", } ---@param opt vim.SystemCompleted @@ -40,8 +40,7 @@ end ---@param opts? {sync:boolean} function installer.install_deps(opts) opts = opts or {} - for pkg, _ in pairs(urls) do - local url = urls[pkg] .. "@latest" + for url, _ in pairs(urls) do if opts.sync then install_sync(url) else From 9aa003812504dce50715fe11c194181209bf8316 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Fri, 21 Mar 2025 21:44:42 +0200 Subject: [PATCH 13/25] refactor: minimize amount of vimscript (#96) * refactor: remove autoload * since nvim 0.9 health.lua files are threaded as checkhealth provider * prof of concept * fix(runner.gocmd)!: i forgot to update it when i was working on #85 * fix(plugin): now commands register properly * fix(plugin): fix command name for :GoIfErr * fix(plugin): respect `setup_commands` option * docs: update * refactor(plugin): use vim.schedule * docs: update CONTRIBUTING --- CONTRIBUTING.md | 9 ++-- Taskfile.yml | 12 +---- autoload/health/gopher.vim | 3 -- doc/gopher.nvim.txt | 4 ++ lua/gopher/_utils/runner/gocmd.lua | 20 ++++---- lua/gopher/config.lua | 12 +++++ lua/gopher/init.lua | 8 ++-- plugin/gopher.lua | 74 ++++++++++++++++++++++++++++++ plugin/gopher.vim | 16 ------- 9 files changed, 108 insertions(+), 50 deletions(-) delete mode 100644 autoload/health/gopher.vim create mode 100644 plugin/gopher.lua delete mode 100644 plugin/gopher.vim diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1a825db..ef377e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,9 +22,8 @@ sudo pacman -S selene stylua For formatting use this following commands, or setup your editor to integrate with selene/stylua: ```bash -task format -task format:check # will check if your code formatted -task lint +task stylua +task lint # lintering and format chewing ``` ### Documentation @@ -43,8 +42,8 @@ We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), p ### Testing -For testing this plugins uses [plenary.nvim](https://github.com/nvim-lua/plenary.nvim). -All tests live in [/spec](https://github.com/olexsmir/gopher.nvim/tree/main/spec) dir. +For testing this plugins uses [mini.test](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-test.md). +All tests live in [/spec](./spec) dir. You can run tests with: ```bash diff --git a/Taskfile.yml b/Taskfile.yml index c6f5339..3900751 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,26 +1,16 @@ version: "3" tasks: - format: - desc: formats all lua files in repo - cmds: - - stylua . - lint: desc: runs all linters cmds: - task: selene - - task: stylua:check + - stylua --check . selene: desc: runs lua linter(selene) cmds: - selene . - stylua:check: - desc: runs stylua in check mode - cmds: - - stylua --check . - stylua: desc: runs lua formatter cmds: diff --git a/autoload/health/gopher.vim b/autoload/health/gopher.vim deleted file mode 100644 index 4e3c56d..0000000 --- a/autoload/health/gopher.vim +++ /dev/null @@ -1,3 +0,0 @@ -function! health#gopher#check() - lua require("gopher.health").check() -endfunction diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index a266fc9..1ad1cb5 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -61,6 +61,10 @@ You can look at default options |gopher.nvim-config-defaults| ---@type number timeout = 2000, + --- whether to setup plugin commands or not + ---@type boolean + setup_commands = true, + -- user specified paths to binaries ---@class gopher.ConfigCommand commands = { diff --git a/lua/gopher/_utils/runner/gocmd.lua b/lua/gopher/_utils/runner/gocmd.lua index 7e14456..a091c4f 100644 --- a/lua/gopher/_utils/runner/gocmd.lua +++ b/lua/gopher/_utils/runner/gocmd.lua @@ -25,9 +25,9 @@ end ---@param subcmd string ---@param args string[] ----@return string[]|nil +---@return string function gocmd.run(subcmd, args) - if #args == 0 then + if #args == 0 and subcmd ~= "generate" then error "please provide any arguments" end @@ -39,15 +39,13 @@ function gocmd.run(subcmd, args) args = if_generate(args) end - return r.sync(c.go, { - args = { subcmd, unpack(args) }, - on_exit = function(data, status) - if status ~= 0 then - error("gocmd failed: " .. data) - end - u.notify(c.go .. " " .. subcmd .. " ran successful") - end, - }) + local rs = r.sync { c.go, subcmd, unpack(args) } + if rs.code ~= 0 then + error("go " .. subcmd .. " failed: " .. rs.stderr) + end + + u.notify(c.go .. " " .. subcmd .. " ran successful") + return rs.stdout end return gocmd diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index 74cbff3..f5ea7ba 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -37,6 +37,10 @@ local default_config = { ---@type number timeout = 2000, + --- whether to setup plugin commands or not + ---@type boolean + setup_commands = true, + -- user specified paths to binaries ---@class gopher.ConfigCommand commands = { @@ -90,10 +94,18 @@ function config.setup(user_config) _config = vim.tbl_deep_extend("force", default_config, user_config or {}) end +---@return boolean +---@private +function config.should_setup_commands() + return config.setup_commands +end + setmetatable(config, { __index = function(_, key) return _config[key] end, }) +---@return gopher.Config +---@private return config diff --git a/lua/gopher/init.lua b/lua/gopher/init.lua index e1d136c..dfcefc9 100644 --- a/lua/gopher/init.lua +++ b/lua/gopher/init.lua @@ -55,19 +55,19 @@ gopher.test = { } gopher.get = function(...) - gocmd("get", { ... }) + gocmd("get", ...) end gopher.mod = function(...) - gocmd("mod", { ... }) + gocmd("mod", ...) end gopher.generate = function(...) - gocmd("generate", { ... }) + gocmd("generate", ...) end gopher.work = function(...) - gocmd("work", { ... }) + gocmd("work", ...) end return gopher diff --git a/plugin/gopher.lua b/plugin/gopher.lua new file mode 100644 index 0000000..1e9eb99 --- /dev/null +++ b/plugin/gopher.lua @@ -0,0 +1,74 @@ +--- NOTE: runs in defer since this file before gopher.config +--- I'm not sure if this is the best to do this +vim.schedule(function() + if require("gopher.config").should_setup_commands() then + vim.api.nvim_create_user_command("GopherLog", function() + vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile()) + end, { nargs = 0 }) + + vim.api.nvim_create_user_command("GoIfErr", function() + require("gopher").iferr() + end, { nargs = 0 }) + + vim.api.nvim_create_user_command("GoCmt", function() + require("gopher").comment() + end, { nargs = 0 }) + + vim.api.nvim_create_user_command("GoImpl", function(args) + require("gopher").impl(unpack(args.fargs)) + end, { nargs = "*" }) + + -- :GoInstall + vim.api.nvim_create_user_command("GoInstallDeps", function() + require("gopher").install_deps() + end, { nargs = 0 }) + + vim.api.nvim_create_user_command("GoInstallDepsSync", function() + require("gopher").install_deps { sync = true } + end, { nargs = 0 }) + + --- :GoTag + vim.api.nvim_create_user_command("GoTagAdd", function(opts) + require("gopher").tags.add(unpack(opts.fargs)) + end, { nargs = "*" }) + + vim.api.nvim_create_user_command("GoTagRm", function(opts) + require("gopher").tags.rm(unpack(opts.fargs)) + end, { nargs = "*" }) + + vim.api.nvim_create_user_command("GoTagClear", function() + require("gopher").tags.clear() + end, { nargs = 0 }) + + --- :GoTest + vim.api.nvim_create_user_command("GoTestAdd", function() + require("gopher").test.add() + end, { nargs = 0 }) + + vim.api.nvim_create_user_command("GoTestsAll", function() + require("gopher").test.all() + end, { nargs = 0 }) + + vim.api.nvim_create_user_command("GoTestsExp", function() + require("gopher").test.exported() + end, { nargs = 0 }) + + -- :Go + vim.api.nvim_create_user_command("GoMod", function(opts) + require("gopher").mod(opts.fargs) + end, { nargs = "*" }) + + vim.api.nvim_create_user_command("GoGet", function(opts) + vim.print(opts) + require("gopher").get(opts.fargs) + end, { nargs = "*" }) + + vim.api.nvim_create_user_command("GoWork", function(opts) + require("gopher").get(opts.fargs) + end, { nargs = "*" }) + + vim.api.nvim_create_user_command("GoGenerate", function(opts) + require("gopher").generate(opts.fargs or "") + end, { nargs = "?" }) + end +end) diff --git a/plugin/gopher.vim b/plugin/gopher.vim deleted file mode 100644 index dc924b9..0000000 --- a/plugin/gopher.vim +++ /dev/null @@ -1,16 +0,0 @@ -command! -nargs=* GoTagAdd :lua require"gopher".tags.add() -command! -nargs=* GoTagRm :lua require"gopher".tags.rm() -command! GoTagClear :lua require"gopher".tags.clear() -command! GoTestAdd :lua require"gopher".test.add() -command! GoTestsAll :lua require"gopher".test.all() -command! GoTestsExp :lua require"gopher".test.exported() -command! -nargs=* GoMod :lua require"gopher".mod() -command! -nargs=* GoGet :lua require"gopher".get() -command! -nargs=* GoWork :lua require"gopher".work() -command! -nargs=* GoImpl :lua require"gopher".impl() -command! -nargs=* GoGenerate :lua require"gopher".generate() -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()) From 7af08c978057499384c5dfcfd67e951d12cfb875 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Fri, 21 Mar 2025 22:10:40 +0200 Subject: [PATCH 14/25] test: unit (#97) * refactor(utils): remove unused function * fix(installer): actually pass what should be passed * docs: add explanation comment * test: add utils * refactor(utils): flatten the dir of files * remove .luarc --- .envrc | 1 + .luarc.json | 10 ------- lua/gopher/_utils/{runner => }/gocmd.lua | 0 lua/gopher/_utils/init.lua | 12 -------- .../_utils/{runner/init.lua => runner.lua} | 0 lua/gopher/init.lua | 2 +- lua/gopher/installer.lua | 11 +++++-- spec/unit/utils_test.lua | 29 +++++++++++++++++++ 8 files changed, 39 insertions(+), 26 deletions(-) delete mode 100644 .luarc.json rename lua/gopher/_utils/{runner => }/gocmd.lua (100%) rename lua/gopher/_utils/{runner/init.lua => runner.lua} (100%) create mode 100644 spec/unit/utils_test.lua diff --git a/.envrc b/.envrc index dd6cca9..1973517 100644 --- a/.envrc +++ b/.envrc @@ -1,3 +1,4 @@ dotenv +# GOPHER_DIR - needed only for tests, to find the root of the project env_vars_required GOPHER_DIR diff --git a/.luarc.json b/.luarc.json deleted file mode 100644 index a0d5712..0000000 --- a/.luarc.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "diagnostics.globals": [ - "describe", - "it", - "before_each", - "after_each", - "before_all", - "after_all" - ] -} diff --git a/lua/gopher/_utils/runner/gocmd.lua b/lua/gopher/_utils/gocmd.lua similarity index 100% rename from lua/gopher/_utils/runner/gocmd.lua rename to lua/gopher/_utils/gocmd.lua diff --git a/lua/gopher/_utils/init.lua b/lua/gopher/_utils/init.lua index 1ff0f28..0d9f5e2 100644 --- a/lua/gopher/_utils/init.lua +++ b/lua/gopher/_utils/init.lua @@ -2,18 +2,6 @@ local c = require "gopher.config" local log = require "gopher._utils.log" local utils = {} ----@param msg string ----@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, - }) - log.debug(msg) - end, 0) -end - ---@param msg string ---@param lvl? number function utils.notify(msg, lvl) diff --git a/lua/gopher/_utils/runner/init.lua b/lua/gopher/_utils/runner.lua similarity index 100% rename from lua/gopher/_utils/runner/init.lua rename to lua/gopher/_utils/runner.lua diff --git a/lua/gopher/init.lua b/lua/gopher/init.lua index dfcefc9..0028dcc 100644 --- a/lua/gopher/init.lua +++ b/lua/gopher/init.lua @@ -12,7 +12,7 @@ local log = require "gopher._utils.log" local tags = require "gopher.struct_tags" local tests = require "gopher.gotests" -local gocmd = require("gopher._utils.runner.gocmd").run +local gocmd = require("gopher._utils.gocmd").run local gopher = {} ---@toc_entry Setup diff --git a/lua/gopher/installer.lua b/lua/gopher/installer.lua index adcbec6..cc61f92 100644 --- a/lua/gopher/installer.lua +++ b/lua/gopher/installer.lua @@ -15,12 +15,17 @@ local urls = { ---@param url string local function handle_intall_exit(opt, url) if opt.code ~= 0 then - u.deferred_notify("go install failed: " .. url) + vim.schedule(function() + u.notify("go install failed: " .. url) + end) + log.error("go install failed:", "url", url, "opt", vim.inspect(opt)) return end - u.deferred_notify("go install-ed: " .. url) + vim.schedule(function() + u.notify("go install-ed: " .. url) + end) end ---@param url string @@ -40,7 +45,7 @@ end ---@param opts? {sync:boolean} function installer.install_deps(opts) opts = opts or {} - for url, _ in pairs(urls) do + for _, url in pairs(urls) do if opts.sync then install_sync(url) else diff --git a/spec/unit/utils_test.lua b/spec/unit/utils_test.lua new file mode 100644 index 0000000..7001261 --- /dev/null +++ b/spec/unit/utils_test.lua @@ -0,0 +1,29 @@ +local t = require "spec.testutils" +local child = MiniTest.new_child_neovim() +local T = MiniTest.new_set { + hooks = { + post_once = child.stop, + pre_case = function() + child.restart { "-u", t.mininit_path } + end, + }, +} + +T["utils"] = MiniTest.new_set() +T["utils"]["should .remove_empty_lines()"] = function() + local u = require "gopher._utils" + local inp = { "hi", "", "a", "", "", "asdf" } + + t.eq(u.remove_empty_lines(inp), { "hi", "a", "asdf" }) +end + +T["utils"]["should .readfile_joined()"] = function() + local data = "line1\nline2\nline3" + local tmp = t.tmpfile() + local u = require "gopher._utils" + + t.writefile(tmp, data) + t.eq(u.readfile_joined(tmp), data) +end + +return T From 6ee261cfa60672104e93de3584bc84b0a7bb90fa Mon Sep 17 00:00:00 2001 From: Oleksandr Smirnov Date: Fri, 21 Mar 2025 22:16:44 +0200 Subject: [PATCH 15/25] docs: update readme --- README.md | 15 ++++++++++++--- doc/gopher.nvim.txt | 3 +-- lua/gopher/config.lua | 3 +-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1dfff49..3e3c2ff 100644 --- a/README.md +++ b/README.md @@ -39,17 +39,21 @@ Requirements: > > If you need more info look `:h gopher.nvim` -**Take a look at default options** +**Take a look at default options (might be a bit outdated, look `:h gopher.nvim-config-defaults`)** ```lua require("gopher").setup { + log_level = vim.log.levels.INFO, + + -- timeout for running internal commands + timeout = 2000, + commands = { go = "go", gomodifytags = "gomodifytags", gotests = "gotests", impl = "impl", iferr = "iferr", - dlv = "dlv", }, gotests = { -- gotests doesn't have template named "default" so this plugin uses "default" to set the default template @@ -61,6 +65,12 @@ require("gopher").setup { }, gotag = { transform = "snakecase", + -- default tags to add to struct fields + default_tag = "json", + }, + iferr = { + -- choose a custom error message + message = nil, }, } ``` @@ -84,7 +94,6 @@ require("gopher").setup { - [impl](https://github.com/josharian/impl) - [gotests](https://github.com/cweill/gotests) - [iferr](https://github.com/koron/iferr) - - [dlv](github.com/go-delve/delve/cmd/dlv)
diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index 1ad1cb5..4eb8207 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -57,7 +57,7 @@ You can look at default options |gopher.nvim-config-defaults| ---@type number log_level = vim.log.levels.INFO, - -- timeout for running commands + -- timeout for running internal commands ---@type number timeout = 2000, @@ -73,7 +73,6 @@ You can look at default options |gopher.nvim-config-defaults| gotests = "gotests", impl = "impl", iferr = "iferr", - dlv = "dlv", }, ---@class gopher.ConfigGotests gotests = { diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index f5ea7ba..3da70cd 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -33,7 +33,7 @@ local default_config = { ---@type number log_level = vim.log.levels.INFO, - -- timeout for running commands + -- timeout for running internal commands ---@type number timeout = 2000, @@ -49,7 +49,6 @@ local default_config = { gotests = "gotests", impl = "impl", iferr = "iferr", - dlv = "dlv", }, ---@class gopher.ConfigGotests gotests = { From a993ece59fcac975f811363dc22ec46e76401415 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Sat, 22 Mar 2025 13:14:28 +0200 Subject: [PATCH 16/25] feat(config): vim.validate all config after setup (#98) --- lua/gopher/config.lua | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index 3da70cd..721d5cc 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -90,7 +90,30 @@ _config.___plugin_name = "gopher.nvim" ---@diagnostic disable-line: inject-field ---@param user_config? gopher.Config ---@private function config.setup(user_config) - _config = vim.tbl_deep_extend("force", default_config, user_config or {}) + vim.validate { user_config = { user_config, "table", true } } + + _config = vim.tbl_deep_extend("force", vim.deepcopy(default_config), user_config or {}) + + vim.validate { + log_level = { _config.log_level, "number" }, + timeout = { _config.timeout, "number" }, + setup_commands = { _config.setup_commands, "boolean" }, + ["commands"] = { _config.commands, "table" }, + ["commands.go"] = { _config.commands.go, "string" }, + ["commands.gomodifytags"] = { _config.commands.gomodifytags, "string" }, + ["commands.gotests"] = { _config.commands.gotests, "string" }, + ["commands.impl"] = { _config.commands.impl, "string" }, + ["commands.iferr"] = { _config.commands.iferr, "string" }, + ["gotests"] = { _config.gotests, "table" }, + ["gotests.template"] = { _config.gotests.template, "string" }, + ["gotests.template_dir"] = { _config.gotests.template, "string", true }, + ["gotests.named"] = { _config.gotests.named, "boolean" }, + ["gotag"] = { _config.gotag, "table" }, + ["gotag.transform"] = { _config.gotag.transform, "string" }, + ["gotag.default_tag"] = { _config.gotag.default_tag, "string" }, + ["iferr"] = { _config.iferr, "table" }, + ["iferr.message"] = { _config.iferr.message, "string", true }, + } end ---@return boolean From 969db908f8304aa18fc127a1fb73f1c8de8a5fd0 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Sat, 22 Mar 2025 13:21:15 +0200 Subject: [PATCH 17/25] fix(struct_tags): edge case with structs declared as var (#99) * fix(struct_tags): edge case with structs declared as var * test: test it and fix it * fixup! test: test it and fix it * fixup! fix(struct_tags): edge case with structs declared as var * fixup! test: test it and fix it --- lua/gopher/_utils/ts.lua | 28 +++++++++++++++++++++++---- lua/gopher/struct_tags.lua | 10 ++++++++-- spec/fixtures/tags/svar_input.go | 11 +++++++++++ spec/fixtures/tags/svar_output.go | 11 +++++++++++ spec/fixtures/tags/var_input.go | 8 ++++++++ spec/fixtures/tags/var_output.go | 8 ++++++++ spec/integration/struct_tags_test.lua | 26 +++++++++++++++++++++++++ 7 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 spec/fixtures/tags/svar_input.go create mode 100644 spec/fixtures/tags/svar_output.go create mode 100644 spec/fixtures/tags/var_input.go create mode 100644 spec/fixtures/tags/var_output.go diff --git a/lua/gopher/_utils/ts.lua b/lua/gopher/_utils/ts.lua index 9c74f47..b5e6d32 100644 --- a/lua/gopher/_utils/ts.lua +++ b/lua/gopher/_utils/ts.lua @@ -1,8 +1,15 @@ local ts = {} local queries = { struct = [[ - (type_spec name: (type_identifier) @_name - type: (struct_type)) + [(type_spec name: (type_identifier) @_name + type: (struct_type)) + (var_declaration (var_spec + name: (identifier) @_name @_var + type: (struct_type))) + (short_var_declaration + left: (expression_list (identifier) @_name @_var) + right: (expression_list (composite_literal + type: (struct_type))))] ]], func = [[ [(function_declaration name: (identifier) @_name) @@ -40,7 +47,7 @@ end ---@param query vim.treesitter.Query ---@param node TSNode ---@param bufnr integer ----@return {name:string} +---@return {name:string, is_varstruct:boolean} local function get_captures(query, node, bufnr) local res = {} for _, match, _ in query:iter_matches(node, bufnr) do @@ -49,6 +56,10 @@ local function get_captures(query, node, bufnr) if capture_name == "_name" then res["name"] = vim.treesitter.get_node_text(captured_node, bufnr) end + + if capture_name == "_var" then + res["is_varstruct"] = true + end end end @@ -59,6 +70,7 @@ end ---@field name string ---@field start_line integer ---@field end_line integer +---@field is_varstruct boolean ---@param bufnr integer ---@param parent_type string[] @@ -93,7 +105,15 @@ function ts.get_struct_under_cursor(bufnr) --- should be both type_spec and type_declaration --- because in cases like `type ( T struct{}, U strict{} )` --- i will be choosing always last struct in the list - return do_stuff(bufnr, { "type_spec", "type_declaration" }, queries.struct) + --- + --- var_declaration is for cases like `var x struct{}` + --- short_var_declaration is for cases like `x := struct{}{}` + return do_stuff(bufnr, { + "type_spec", + "type_declaration", + "var_declaration", + "short_var_declaration", + }, queries.struct) end ---@param bufnr integer diff --git a/lua/gopher/struct_tags.lua b/lua/gopher/struct_tags.lua index 4545640..86689e4 100644 --- a/lua/gopher/struct_tags.lua +++ b/lua/gopher/struct_tags.lua @@ -44,11 +44,18 @@ local function handle_tags(fpath, bufnr, user_args) c.commands.gomodifytags, "-transform", c.gotag.transform, "-format", "json", - "-struct", st.name, "-file", fpath, "-w", } + if st.is_varstruct then + table.insert(cmd, "-line") + table.insert(cmd, string.format("%d,%d", st.start_line, st.end_line)) + else + table.insert(cmd, "-struct") + table.insert(cmd, st.name) + end + for _, v in ipairs(user_args) do table.insert(cmd, v) end @@ -60,7 +67,6 @@ local function handle_tags(fpath, bufnr, user_args) end local res = vim.json.decode(rs.stdout) - if res["errors"] then log.error("tags: got an error " .. vim.inspect(res)) error("failed to set tags " .. vim.inspect(res["errors"])) diff --git a/spec/fixtures/tags/svar_input.go b/spec/fixtures/tags/svar_input.go new file mode 100644 index 0000000..7831d01 --- /dev/null +++ b/spec/fixtures/tags/svar_input.go @@ -0,0 +1,11 @@ +package main + +func main() { + s := struct { + API string + Key string + }{ + API: "api.com", + Key: "key", + } +} diff --git a/spec/fixtures/tags/svar_output.go b/spec/fixtures/tags/svar_output.go new file mode 100644 index 0000000..f320eb2 --- /dev/null +++ b/spec/fixtures/tags/svar_output.go @@ -0,0 +1,11 @@ +package main + +func main() { + s := struct { + API string `xml:"api"` + Key string `xml:"key"` + }{ + API: "api.com", + Key: "key", + } +} diff --git a/spec/fixtures/tags/var_input.go b/spec/fixtures/tags/var_input.go new file mode 100644 index 0000000..97b4bc3 --- /dev/null +++ b/spec/fixtures/tags/var_input.go @@ -0,0 +1,8 @@ +package main + +func main() { + var a struct { + TestField1 string + TestField2 string + } +} diff --git a/spec/fixtures/tags/var_output.go b/spec/fixtures/tags/var_output.go new file mode 100644 index 0000000..e158a3a --- /dev/null +++ b/spec/fixtures/tags/var_output.go @@ -0,0 +1,8 @@ +package main + +func main() { + var a struct { + TestField1 string `yaml:"test_field_1"` + TestField2 string `yaml:"test_field_2"` + } +} diff --git a/spec/integration/struct_tags_test.lua b/spec/integration/struct_tags_test.lua index 4ac245b..9552339 100644 --- a/spec/integration/struct_tags_test.lua +++ b/spec/integration/struct_tags_test.lua @@ -82,4 +82,30 @@ T["struct_tags"]["should add more than one tag"] = function() t.eq(t.readfile(tmp), fixtures.output) end +T["struct_tags"]["should add tags on var"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tags/var" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr(tmp), 5, 3 }) + child.cmd "GoTagAdd yaml" + child.cmd "write" + + t.eq(t.readfile(tmp), fixtures.output) +end + +T["struct_tags"]["should add tags on short declr var"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tags/svar" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr(tmp), 4, 3 }) + child.cmd "GoTagAdd xml" + child.cmd "write" + + t.eq(t.readfile(tmp), fixtures.output) +end + return T From acd4e6fc300c690cf89efef39e9da4a71e6708ca Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Sat, 22 Mar 2025 16:10:17 +0200 Subject: [PATCH 18/25] fix!: fix "Command X not found after loading gopher.nvim" (#100) BREAKING CHANGE: now calling .setup is required --- README.md | 2 +- lua/gopher/commands.lua | 82 +++++++++++++++++++++++++++++++++++++++++ lua/gopher/config.lua | 4 ++ plugin/gopher.lua | 74 ------------------------------------- 4 files changed, 87 insertions(+), 75 deletions(-) create mode 100644 lua/gopher/commands.lua delete mode 100644 plugin/gopher.lua diff --git a/README.md b/README.md index 3e3c2ff..a17a3e6 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Requirements: vim.cmd.GoInstallDeps() end, ---@type gopher.Config - opts = {}, + opts = {}, -- required } ``` diff --git a/lua/gopher/commands.lua b/lua/gopher/commands.lua new file mode 100644 index 0000000..63e9f3d --- /dev/null +++ b/lua/gopher/commands.lua @@ -0,0 +1,82 @@ +local commands = {} + +---@param name string +---@param fn fun(args: table) +---@param nargs? number|"*"|"?" +local function cmd(name, fn, nargs) + nargs = nargs or 0 + vim.api.nvim_create_user_command(name, fn, { nargs = nargs }) +end + +function commands.register() + cmd("GopherLog", function() + vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile()) + end) + + cmd("GoIfErr", function() + require("gopher").iferr() + end) + + cmd("GoCmt", function() + require("gopher").comment() + end) + + cmd("GoImpl", function(args) + require("gopher").impl(unpack(args.fargs)) + end, "*") + + -- :GoInstall + cmd("GoInstallDeps", function() + require("gopher").install_deps() + end) + + cmd("GoInstallDepsSync", function() + require("gopher").install_deps { sync = true } + end) + + --- :GoTag + cmd("GoTagAdd", function(opts) + require("gopher").tags.add(unpack(opts.fargs)) + end, "*") + + cmd("GoTagRm", function(opts) + require("gopher").tags.rm(unpack(opts.fargs)) + end, "*") + + cmd("GoTagClear", function() + require("gopher").tags.clear() + end) + + --- :GoTest + cmd("GoTestAdd", function() + require("gopher").test.add() + end) + + cmd("GoTestsAll", function() + require("gopher").test.all() + end) + + cmd("GoTestsExp", function() + require("gopher").test.exported() + end) + + -- :Go + cmd("GoMod", function(opts) + require("gopher").mod(opts.fargs) + end, "*") + + cmd("GoGet", function(opts) + vim.print(opts) + require("gopher").get(opts.fargs) + end, "*") + + cmd("GoWork", function(opts) + require("gopher").get(opts.fargs) + end, "*") + + cmd("GoGenerate", function(opts) + require("gopher").generate(opts.fargs or "") + end, "?") +end + +return commands diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index 721d5cc..e0ef554 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -114,6 +114,10 @@ function config.setup(user_config) ["iferr"] = { _config.iferr, "table" }, ["iferr.message"] = { _config.iferr.message, "string", true }, } + + if _config.setup_commands then + require("gopher.commands").register() + end end ---@return boolean diff --git a/plugin/gopher.lua b/plugin/gopher.lua deleted file mode 100644 index 1e9eb99..0000000 --- a/plugin/gopher.lua +++ /dev/null @@ -1,74 +0,0 @@ ---- NOTE: runs in defer since this file before gopher.config ---- I'm not sure if this is the best to do this -vim.schedule(function() - if require("gopher.config").should_setup_commands() then - vim.api.nvim_create_user_command("GopherLog", function() - vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile()) - end, { nargs = 0 }) - - vim.api.nvim_create_user_command("GoIfErr", function() - require("gopher").iferr() - end, { nargs = 0 }) - - vim.api.nvim_create_user_command("GoCmt", function() - require("gopher").comment() - end, { nargs = 0 }) - - vim.api.nvim_create_user_command("GoImpl", function(args) - require("gopher").impl(unpack(args.fargs)) - end, { nargs = "*" }) - - -- :GoInstall - vim.api.nvim_create_user_command("GoInstallDeps", function() - require("gopher").install_deps() - end, { nargs = 0 }) - - vim.api.nvim_create_user_command("GoInstallDepsSync", function() - require("gopher").install_deps { sync = true } - end, { nargs = 0 }) - - --- :GoTag - vim.api.nvim_create_user_command("GoTagAdd", function(opts) - require("gopher").tags.add(unpack(opts.fargs)) - end, { nargs = "*" }) - - vim.api.nvim_create_user_command("GoTagRm", function(opts) - require("gopher").tags.rm(unpack(opts.fargs)) - end, { nargs = "*" }) - - vim.api.nvim_create_user_command("GoTagClear", function() - require("gopher").tags.clear() - end, { nargs = 0 }) - - --- :GoTest - vim.api.nvim_create_user_command("GoTestAdd", function() - require("gopher").test.add() - end, { nargs = 0 }) - - vim.api.nvim_create_user_command("GoTestsAll", function() - require("gopher").test.all() - end, { nargs = 0 }) - - vim.api.nvim_create_user_command("GoTestsExp", function() - require("gopher").test.exported() - end, { nargs = 0 }) - - -- :Go - vim.api.nvim_create_user_command("GoMod", function(opts) - require("gopher").mod(opts.fargs) - end, { nargs = "*" }) - - vim.api.nvim_create_user_command("GoGet", function(opts) - vim.print(opts) - require("gopher").get(opts.fargs) - end, { nargs = "*" }) - - vim.api.nvim_create_user_command("GoWork", function(opts) - require("gopher").get(opts.fargs) - end, { nargs = "*" }) - - vim.api.nvim_create_user_command("GoGenerate", function(opts) - require("gopher").generate(opts.fargs or "") - end, { nargs = "?" }) - end -end) From ab68a58b343dcf64267daa01a82ffd963836a656 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Sat, 22 Mar 2025 16:15:17 +0200 Subject: [PATCH 19/25] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8596ec0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Oleksandr Smirnov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 7ebe190c962f16681f82d9a394b3256833b7616e Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Sat, 22 Mar 2025 17:06:57 +0200 Subject: [PATCH 20/25] refactor/comamnds dont require .setup (#101) * refactor(commands)!: change the way of disabling commands BREAKING CHANGE: not it does not require calling .setup() * docs: update docs --- doc/gopher.nvim.txt | 16 +++++--- lua/gopher/commands.lua | 82 -------------------------------------- lua/gopher/config.lua | 15 ------- lua/gopher/init.lua | 3 +- plugin/gopher.lua | 87 +++++++++++++++++++++++++++++++++++++++++ scripts/docgen.lua | 1 + 6 files changed, 101 insertions(+), 103 deletions(-) delete mode 100644 lua/gopher/commands.lua create mode 100644 plugin/gopher.lua diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index 4eb8207..e4338d6 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -11,6 +11,7 @@ Table of Contents Setup....................................................|gopher.nvim-setup| Install dependencies..............................|gopher.nvim-install-deps| Configuration...........................................|gopher.nvim-config| + Commands..............................................|gopher.nvim-commands| Modify struct tags.................................|gopher.nvim-struct-tags| Auto implementation of interface methods..................|gopher.nvim-impl| Generating unit tests boilerplate......................|gopher.nvim-gotests| @@ -36,7 +37,8 @@ Parameters ~ Gopher.nvim implements most of its features using third-party tools. To install these tools, you can run `:GoInstallDeps` command or call `require("gopher").install_deps()` if you want to use lua api. -By default dependencies will be installed asynchronously, to install them synchronously pass `{sync = true}` as an argument. +By default dependencies will be installed asynchronously, +to install them synchronously pass `{sync = true}` as an argument. ============================================================================== @@ -61,10 +63,6 @@ You can look at default options |gopher.nvim-config-defaults| ---@type number timeout = 2000, - --- whether to setup plugin commands or not - ---@type boolean - setup_commands = true, - -- user specified paths to binaries ---@class gopher.ConfigCommand commands = { @@ -103,6 +101,14 @@ Class ~ {gopher.Config} +============================================================================== +------------------------------------------------------------------------------ + *gopher.nvim-commands* + +If don't want to automatically register plugins' commands, +you can set `vim.g.gopher_register_commands` to `false`, before loading the plugin. + + ============================================================================== ------------------------------------------------------------------------------ *gopher.nvim-struct-tags* diff --git a/lua/gopher/commands.lua b/lua/gopher/commands.lua deleted file mode 100644 index 63e9f3d..0000000 --- a/lua/gopher/commands.lua +++ /dev/null @@ -1,82 +0,0 @@ -local commands = {} - ----@param name string ----@param fn fun(args: table) ----@param nargs? number|"*"|"?" -local function cmd(name, fn, nargs) - nargs = nargs or 0 - vim.api.nvim_create_user_command(name, fn, { nargs = nargs }) -end - -function commands.register() - cmd("GopherLog", function() - vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile()) - end) - - cmd("GoIfErr", function() - require("gopher").iferr() - end) - - cmd("GoCmt", function() - require("gopher").comment() - end) - - cmd("GoImpl", function(args) - require("gopher").impl(unpack(args.fargs)) - end, "*") - - -- :GoInstall - cmd("GoInstallDeps", function() - require("gopher").install_deps() - end) - - cmd("GoInstallDepsSync", function() - require("gopher").install_deps { sync = true } - end) - - --- :GoTag - cmd("GoTagAdd", function(opts) - require("gopher").tags.add(unpack(opts.fargs)) - end, "*") - - cmd("GoTagRm", function(opts) - require("gopher").tags.rm(unpack(opts.fargs)) - end, "*") - - cmd("GoTagClear", function() - require("gopher").tags.clear() - end) - - --- :GoTest - cmd("GoTestAdd", function() - require("gopher").test.add() - end) - - cmd("GoTestsAll", function() - require("gopher").test.all() - end) - - cmd("GoTestsExp", function() - require("gopher").test.exported() - end) - - -- :Go - cmd("GoMod", function(opts) - require("gopher").mod(opts.fargs) - end, "*") - - cmd("GoGet", function(opts) - vim.print(opts) - require("gopher").get(opts.fargs) - end, "*") - - cmd("GoWork", function(opts) - require("gopher").get(opts.fargs) - end, "*") - - cmd("GoGenerate", function(opts) - require("gopher").generate(opts.fargs or "") - end, "?") -end - -return commands diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index e0ef554..a5f0652 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -37,10 +37,6 @@ local default_config = { ---@type number timeout = 2000, - --- whether to setup plugin commands or not - ---@type boolean - setup_commands = true, - -- user specified paths to binaries ---@class gopher.ConfigCommand commands = { @@ -97,7 +93,6 @@ function config.setup(user_config) vim.validate { log_level = { _config.log_level, "number" }, timeout = { _config.timeout, "number" }, - setup_commands = { _config.setup_commands, "boolean" }, ["commands"] = { _config.commands, "table" }, ["commands.go"] = { _config.commands.go, "string" }, ["commands.gomodifytags"] = { _config.commands.gomodifytags, "string" }, @@ -114,16 +109,6 @@ function config.setup(user_config) ["iferr"] = { _config.iferr, "table" }, ["iferr.message"] = { _config.iferr.message, "string", true }, } - - if _config.setup_commands then - require("gopher.commands").register() - end -end - ----@return boolean ----@private -function config.should_setup_commands() - return config.setup_commands end setmetatable(config, { diff --git a/lua/gopher/init.lua b/lua/gopher/init.lua index 0028dcc..16cfb17 100644 --- a/lua/gopher/init.lua +++ b/lua/gopher/init.lua @@ -35,7 +35,8 @@ end ---@text Gopher.nvim implements most of its features using third-party tools. --- To install these tools, you can run `:GoInstallDeps` command --- or call `require("gopher").install_deps()` if you want to use lua api. ---- By default dependencies will be installed asynchronously, to install them synchronously pass `{sync = true}` as an argument. +--- By default dependencies will be installed asynchronously, +--- to install them synchronously pass `{sync = true}` as an argument. gopher.install_deps = require("gopher.installer").install_deps gopher.impl = require("gopher.impl").impl diff --git a/plugin/gopher.lua b/plugin/gopher.lua new file mode 100644 index 0000000..1ccc74e --- /dev/null +++ b/plugin/gopher.lua @@ -0,0 +1,87 @@ +---@toc_entry Commands +---@tag gopher.nvim-commands +---@text +--- If don't want to automatically register plugins' commands, +--- you can set `vim.g.gopher_register_commands` to `false`, before loading the plugin. + +if vim.g.gopher_register_commands == false then + return +end + +---@param name string +---@param fn fun(args: table) +---@param nargs? number|"*"|"?" +---@private +local function cmd(name, fn, nargs) + nargs = nargs or 0 + vim.api.nvim_create_user_command(name, fn, { nargs = nargs }) +end + +cmd("GopherLog", function() + vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile()) +end) + +cmd("GoIfErr", function() + require("gopher").iferr() +end) + +cmd("GoCmt", function() + require("gopher").comment() +end) + +cmd("GoImpl", function(args) + require("gopher").impl(unpack(args.fargs)) +end, "*") + +-- :GoInstall +cmd("GoInstallDeps", function() + require("gopher").install_deps() +end) + +cmd("GoInstallDepsSync", function() + require("gopher").install_deps { sync = true } +end) + +-- :GoTag +cmd("GoTagAdd", function(opts) + require("gopher").tags.add(unpack(opts.fargs)) +end, "*") + +cmd("GoTagRm", function(opts) + require("gopher").tags.rm(unpack(opts.fargs)) +end, "*") + +cmd("GoTagClear", function() + require("gopher").tags.clear() +end) + +-- :GoTest +cmd("GoTestAdd", function() + require("gopher").test.add() +end) + +cmd("GoTestsAll", function() + require("gopher").test.all() +end) + +cmd("GoTestsExp", function() + require("gopher").test.exported() +end) + +-- :Go +cmd("GoMod", function(opts) + require("gopher").mod(opts.fargs) +end, "*") + +cmd("GoGet", function(opts) + vim.print(opts) + require("gopher").get(opts.fargs) +end, "*") + +cmd("GoWork", function(opts) + require("gopher").get(opts.fargs) +end, "*") + +cmd("GoGenerate", function(opts) + require("gopher").generate(opts.fargs or "") +end, "?") diff --git a/scripts/docgen.lua b/scripts/docgen.lua index 87cbd3a..381d3c4 100644 --- a/scripts/docgen.lua +++ b/scripts/docgen.lua @@ -10,6 +10,7 @@ end local files = { "lua/gopher/init.lua", "lua/gopher/config.lua", + "plugin/gopher.lua", "lua/gopher/struct_tags.lua", "lua/gopher/impl.lua", "lua/gopher/gotests.lua", From 3dcf708d3457661d6d22449954c2e3f4cc64fc2c Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Sat, 22 Mar 2025 21:08:33 +0200 Subject: [PATCH 21/25] refactor: some refactoring of tests (#102) * feat(tests): add utils that does most of tests boilerplate * refactor(tests): rewrite using new thing * refactor(tests): clean up everywhere * refactor(tests): remove boilerplate even further * refactor(tests): soon it will be too far * refactor(tests): some test renaming --- spec/integration/comment_test.lua | 25 ++------- spec/integration/gotests_test.lua | 32 +++--------- spec/integration/iferr_test.lua | 39 +++++--------- spec/integration/impl_test.lua | 52 ++++++------------- spec/integration/struct_tags_test.lua | 74 ++++++++------------------- spec/testutils.lua | 53 ++++++++++++++++++- 6 files changed, 114 insertions(+), 161 deletions(-) diff --git a/spec/integration/comment_test.lua b/spec/integration/comment_test.lua index 9cad5f7..38e7909 100644 --- a/spec/integration/comment_test.lua +++ b/spec/integration/comment_test.lua @@ -1,32 +1,15 @@ local t = require "spec.testutils" - -local child = MiniTest.new_child_neovim() -local T = MiniTest.new_set { - hooks = { - post_once = child.stop, - pre_case = function() - child.restart { "-u", t.mininit_path } - end, - }, -} +local child, T = t.setup "comment" local function do_the_test(fixture, pos) - local tmp = t.tmpfile() - local fixtures = t.get_fixtures("comment/" .. fixture) - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr "%", unpack(pos) }) + local rs = t.setup_test("comment/" .. fixture, child, pos) child.cmd "GoCmt" child.cmd "write" - t.eq(t.readfile(tmp), fixtures.output) - - -- without it all other(not even from this module) tests are falling - t.deletefile(tmp) + t.eq(t.readfile(rs.tmp), rs.fixtures.output) + t.cleanup(rs) end -T["comment"] = MiniTest.new_set {} T["comment"]["should add comment to package"] = function() do_the_test("package", { 1, 1 }) end diff --git a/spec/integration/gotests_test.lua b/spec/integration/gotests_test.lua index 57f3142..fcba8a7 100644 --- a/spec/integration/gotests_test.lua +++ b/spec/integration/gotests_test.lua @@ -1,15 +1,5 @@ local t = require "spec.testutils" - -local child = MiniTest.new_child_neovim() -local T = MiniTest.new_set { - hooks = { - post_once = child.stop, - pre_case = function() - child.restart { "-u", t.mininit_path } - end, - }, -} -T["gotests"] = MiniTest.new_set {} +local child, T = t.setup "gotests" --- NOTE: :GoTestAdd is the only place that has actual logic --- All other parts are handled `gotests` itself. @@ -21,27 +11,19 @@ local function read_testfile(fpath) end T["gotests"]["should add test for function under cursor"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "tests/function" - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr "%", 3, 6 }) + local rs = t.setup_test("tests/function", child, { 3, 5 }) child.cmd "GoTestAdd" - t.eq(fixtures.output, read_testfile(tmp)) + t.eq(rs.fixtures.output, read_testfile(rs.tmp)) + t.cleanup(rs) end T["gotests"]["should add test for method under cursor"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "tests/method" - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr "%", 5, 19 }) + local rs = t.setup_test("tests/method", child, { 5, 19 }) child.cmd "GoTestAdd" - t.eq(fixtures.output, read_testfile(tmp)) + t.eq(rs.fixtures.output, read_testfile(rs.tmp)) + t.cleanup(rs) end return T diff --git a/spec/integration/iferr_test.lua b/spec/integration/iferr_test.lua index 9c5ffdb..fff53ba 100644 --- a/spec/integration/iferr_test.lua +++ b/spec/integration/iferr_test.lua @@ -1,40 +1,27 @@ local t = require "spec.testutils" +local child, T = t.setup "iferr" -local child = MiniTest.new_child_neovim() -local T = MiniTest.new_set { - hooks = { - post_once = child.stop, - pre_case = function() - child.restart { "-u", t.mininit_path } - end, - }, -} -T["iferr"] = MiniTest.new_set {} -T["iferr"]["works"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "iferr/iferr" - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr "%", 8, 2, 0 }) +T["iferr"]["should add if != nil {"] = function() + local rs = t.setup_test("iferr/iferr", child, { 8, 2 }) child.cmd "GoIfErr" child.cmd "write" - t.eq(t.readfile(tmp), fixtures.output) + t.eq(t.readfile(rs.tmp), rs.fixtures.output) + t.cleanup(rs) end -T["iferr"]["works with custom message"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "iferr/message" - t.writefile(tmp, fixtures.input) +T["iferr"]["should add if err with custom message"] = function() + child.lua [[ + require("gopher").setup { + iferr = { message = 'fmt.Errorf("failed to %w", err)' } + } ]] - child.lua [[ require("gopher").setup { iferr = { message = 'fmt.Errorf("failed to %w", err)' } } ]] - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr "%", 6, 2, 0 }) + local rs = t.setup_test("iferr/message", child, { 6, 2 }) child.cmd "GoIfErr" child.cmd "write" - t.eq(t.readfile(tmp), fixtures.output) + t.eq(t.readfile(rs.tmp), rs.fixtures.output) + t.cleanup(rs) end return T diff --git a/spec/integration/impl_test.lua b/spec/integration/impl_test.lua index bbbbf6f..602d2b1 100644 --- a/spec/integration/impl_test.lua +++ b/spec/integration/impl_test.lua @@ -1,55 +1,35 @@ local t = require "spec.testutils" +local child, T = t.setup "impl" -local child = MiniTest.new_child_neovim() -local T = MiniTest.new_set { - hooks = { - post_once = child.stop, - pre_case = function() - child.restart { "-u", t.mininit_path } - end, - }, -} -T["impl"] = MiniTest.new_set {} -T["impl"]["works w io.Writer"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "impl/writer" - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 0 }) +T["impl"]["should do impl with 'w io.Writer'"] = function() + local rs = t.setup_test("impl/writer", child, { 3, 0 }) child.cmd "GoImpl w io.Writer" child.cmd "write" -- NOTE: since "impl" won't implement interface if it's already implemented i went with this hack - local rhs = fixtures.output:gsub("Test2", "Test") - t.eq(t.readfile(tmp), rhs) + local rhs = rs.fixtures.output:gsub("Test2", "Test") + t.eq(t.readfile(rs.tmp), rhs) + t.cleanup(rs) end -T["impl"]["works r Read io.Reader"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "impl/reader" - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) +T["impl"]["should work with full input, 'r Read io.Reader'"] = function() + local rs = t.setup_test("impl/reader", child) child.cmd "GoImpl r Read io.Reader" child.cmd "write" - local rhs = fixtures.output:gsub("Read2", "Read") - t.eq(t.readfile(tmp), rhs) + local rhs = rs.fixtures.output:gsub("Read2", "Read") + t.eq(t.readfile(rs.tmp), rhs) + t.cleanup(rs) end -T["impl"]["works io.Closer"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "impl/closer" - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 6 }) +T["impl"]["should work with minimal input 'io.Closer'"] = function() + local rs = t.setup_test("impl/closer", child, { 3, 6 }) child.cmd "GoImpl io.Closer" child.cmd "write" - local rhs = fixtures.output:gsub("Test2", "Test") - t.eq(t.readfile(tmp), rhs) + local rhs = rs.fixtures.output:gsub("Test2", "Test") + t.eq(t.readfile(rs.tmp), rhs) + t.cleanup(rs) end return T diff --git a/spec/integration/struct_tags_test.lua b/spec/integration/struct_tags_test.lua index 9552339..1c97001 100644 --- a/spec/integration/struct_tags_test.lua +++ b/spec/integration/struct_tags_test.lua @@ -1,65 +1,40 @@ local t = require "spec.testutils" +local child, T = t.setup "struct_tags" -local child = MiniTest.new_child_neovim() -local T = MiniTest.new_set { - hooks = { - post_once = child.stop, - pre_case = function() - child.restart { "-u", t.mininit_path } - end, - }, -} -T["struct_tags"] = MiniTest.new_set {} T["struct_tags"]["should add tag"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "tags/add" - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 6, 0 }) + local rs = t.setup_test("tags/add", child, { 3, 6 }) child.cmd "GoTagAdd json" child.cmd "write" - t.eq(t.readfile(tmp), fixtures.output) + t.eq(t.readfile(rs.tmp), rs.fixtures.output) + t.cleanup(rs) end T["struct_tags"]["should remove tag"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "tags/remove" - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr(tmp), 4, 6, 0 }) + local rs = t.setup_test("tags/remove", child, { 4, 6 }) child.cmd "GoTagRm json" child.cmd "write" - t.eq(t.readfile(tmp), fixtures.output) + t.eq(t.readfile(rs.tmp), rs.fixtures.output) + t.cleanup(rs) end T["struct_tags"]["should be able to handle many structs"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "tags/many" - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr(tmp), 10, 3, 0 }) + local rs = t.setup_test("tags/many", child, { 10, 3 }) child.cmd "GoTagAdd testing" child.cmd "write" - t.eq(t.readfile(tmp), fixtures.output) + t.eq(t.readfile(rs.tmp), rs.fixtures.output) + t.cleanup(rs) end T["struct_tags"]["should clear struct"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "tags/clear" - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 1, 0 }) + local rs = t.setup_test("tags/clear", child, { 3, 1 }) child.cmd "GoTagClear" child.cmd "write" - t.eq(t.readfile(tmp), fixtures.output) + t.eq(t.readfile(rs.tmp), rs.fixtures.output) + t.cleanup(rs) end T["struct_tags"]["should add more than one tag"] = function() @@ -80,32 +55,27 @@ T["struct_tags"]["should add more than one tag"] = function() child.cmd "write" t.eq(t.readfile(tmp), fixtures.output) + + ---@diagnostic disable-next-line:missing-fields + t.cleanup { tmp = tmp } end T["struct_tags"]["should add tags on var"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "tags/var" - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr(tmp), 5, 3 }) + local rs = t.setup_test("tags/var", child, { 5, 6 }) child.cmd "GoTagAdd yaml" child.cmd "write" - t.eq(t.readfile(tmp), fixtures.output) + t.eq(t.readfile(rs.tmp), rs.fixtures.output) + t.cleanup(rs) end T["struct_tags"]["should add tags on short declr var"] = function() - local tmp = t.tmpfile() - local fixtures = t.get_fixtures "tags/svar" - t.writefile(tmp, fixtures.input) - - child.cmd("silent edit " .. tmp) - child.fn.setpos(".", { child.fn.bufnr(tmp), 4, 3 }) + local rs = t.setup_test("tags/svar", child, { 4, 3 }) child.cmd "GoTagAdd xml" child.cmd "write" - t.eq(t.readfile(tmp), fixtures.output) + t.eq(t.readfile(rs.tmp), rs.fixtures.output) + t.cleanup(rs) end return T diff --git a/spec/testutils.lua b/spec/testutils.lua index 32c19ab..1a31e10 100644 --- a/spec/testutils.lua +++ b/spec/testutils.lua @@ -6,6 +6,23 @@ local testutils = {} testutils.mininit_path = vim.fs.joinpath(base_dir, "scripts", "minimal_init.lua") testutils.fixtures_dir = vim.fs.joinpath(base_dir, "spec/fixtures") +---@param name string +---@return MiniTest.child, table +function testutils.setup(name) + local child = MiniTest.new_child_neovim() + local T = MiniTest.new_set { + hooks = { + post_once = child.stop, + pre_case = function() + child.restart { "-u", testutils.mininit_path } + end, + }, + } + + T[name] = MiniTest.new_set {} + return child, T +end + ---@generic T ---@param a T ---@param b T @@ -36,8 +53,12 @@ function testutils.deletefile(fpath) vim.fn.delete(fpath) end +---@class gopher.TestUtilsFixtures +---@field input string +---@field output string + ---@param fixture string ----@return {input: string, output: string} +---@return gopher.TestUtilsFixtures function testutils.get_fixtures(fixture) return { input = testutils.readfile(vim.fs.joinpath(testutils.fixtures_dir, fixture) .. "_input.go"), @@ -45,4 +66,34 @@ function testutils.get_fixtures(fixture) } end +---@class gopher.TestUtilsSetup +---@field tmp string +---@field fixtures gopher.TestUtilsFixtures + +---@param fixture string +---@param child MiniTest.child +---@param pos? number[] +---@return gopher.TestUtilsSetup +function testutils.setup_test(fixture, child, pos) + local tmp = testutils.tmpfile() + local fixtures = testutils.get_fixtures(fixture) + + testutils.writefile(tmp, fixtures.input) + child.cmd("silent edit " .. tmp) + + if pos then + child.fn.setpos(".", { child.fn.bufnr(tmp), unpack(pos) }) + end + + return { + tmp = tmp, + fixtures = fixtures, + } +end + +---@param inp gopher.TestUtilsSetup +function testutils.cleanup(inp) + testutils.deletefile(inp.tmp) +end + return testutils From 592fe82760129685bcca93e2d6e66f52fdce7ec4 Mon Sep 17 00:00:00 2001 From: Oleksandr Smirnov Date: Sat, 22 Mar 2025 21:12:03 +0200 Subject: [PATCH 22/25] chore(ci): update `checkout` in tests --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b575f94..2eaf9fa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -35,7 +35,7 @@ jobs: neovim: true version: ${{ matrix.version }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Cache .tests uses: actions/cache@v4 From c0b2834652a6eacedc87e8744045e0e68c54681e Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Sun, 23 Mar 2025 15:34:47 +0200 Subject: [PATCH 23/25] docs: update (#103) * chore: add @dochide annotation - it's easier to distinguish @private and something i dont want to see in docs * docs: update * refactor: move thing out to utils * fix: lua-ls error * fixup! refactor: move thing out to utils * docs: update --- README.md | 5 ++- doc/gopher.nvim.txt | 82 ++++++++++++++------------------------ lua/gopher/_utils/init.lua | 8 ++++ lua/gopher/_utils/log.lua | 1 + lua/gopher/comment.lua | 10 +++-- lua/gopher/config.lua | 24 +++-------- lua/gopher/gotests.lua | 8 +--- lua/gopher/iferr.lua | 8 +++- lua/gopher/impl.lua | 7 ++-- lua/gopher/init.lua | 19 +++++---- lua/gopher/struct_tags.lua | 27 +++++++++---- scripts/docgen.lua | 4 ++ spec/unit/utils_test.lua | 16 +++----- 13 files changed, 107 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index a17a3e6..acb0554 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Requirements: vim.cmd.GoInstallDeps() end, ---@type gopher.Config - opts = {}, -- required + opts = {}, } ``` @@ -39,10 +39,11 @@ Requirements: > > If you need more info look `:h gopher.nvim` -**Take a look at default options (might be a bit outdated, look `:h gopher.nvim-config-defaults`)** +**Take a look at default options (might be a bit outdated, look `:h gopher.nvim-config`)** ```lua require("gopher").setup { + -- log level, you might consider using DEBUG or TRACE for debugging the plugin log_level = vim.log.levels.INFO, -- timeout for running internal commands diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index e4338d6..3d653ca 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -1,16 +1,16 @@ -*gopher.nvim* +*gopher.nvim* Enhance your golang experience + +MIT License Copyright (c) 2025 Oleksandr Smirnov ============================================================================== gopher.nvim is a minimalistic plugin for Go development in Neovim written in Lua. It's not an LSP tool, the main goal of this plugin is add go tooling support in Neovim. ------------------------------------------------------------------------------- - *gopher.nvim-table-of-contents* Table of Contents - Setup....................................................|gopher.nvim-setup| - Install dependencies..............................|gopher.nvim-install-deps| - Configuration...........................................|gopher.nvim-config| + Setup..................................................|gopher.nvim-setup()| + Install dependencies..............................|gopher.nvim-dependencies| + Config..................................................|gopher.nvim-config| Commands..............................................|gopher.nvim-commands| Modify struct tags.................................|gopher.nvim-struct-tags| Auto implementation of interface methods..................|gopher.nvim-impl| @@ -19,20 +19,22 @@ Table of Contents Generate comments.....................................|gopher.nvim-comments| ------------------------------------------------------------------------------ - *gopher.nvim-setup* + *gopher.nvim-setup()* `gopher.setup`({user_config}) Setup function. This method simply merges default config with opts table. You can read more about configuration at |gopher.nvim-config| Calling this function is optional, if you ok with default settings. -See |gopher.nvim.config-defaults| +See |gopher.nvim.config| Usage ~ -`require("gopher").setup {}` (replace `{}` with your `config` table) +>lua + require("gopher").setup {} -- use default config or replace {} with your own +< Parameters ~ -{user_config} `(gopher.Config)` +{user_config} `(gopher.Config)` See |gopher.nvim-config| ------------------------------------------------------------------------------ - *gopher.nvim-install-deps* + *gopher.nvim-dependencies* `gopher.install_deps` Gopher.nvim implements most of its features using third-party tools. To install these tools, you can run `:GoInstallDeps` command @@ -44,17 +46,9 @@ to install them synchronously pass `{sync = true}` as an argument. ============================================================================== ------------------------------------------------------------------------------ *gopher.nvim-config* -config it is the place where you can configure the plugin. -also this is optional is you're ok with default settings. -You can look at default options |gopher.nvim-config-defaults| - ------------------------------------------------------------------------------- - *gopher.nvim-config-defaults* `default_config` >lua local default_config = { - --minidoc_replace_end - -- log level, you might consider using DEBUG or TRACE for debugging the plugin ---@type number log_level = vim.log.levels.INFO, @@ -112,15 +106,18 @@ you can set `vim.g.gopher_register_commands` to `false`, before loading the plug ============================================================================== ------------------------------------------------------------------------------ *gopher.nvim-struct-tags* -struct-tags is utilizing the `gomodifytags` tool to add or remove tags to struct fields. + +`struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to struct fields. + Usage ~ How to add/remove tags to struct fields: - ------------------------------------------------------------------------------- +1. Place cursor on the struct 2. Run `:GoTagAdd json` to add json tags to struct fields 3. Run `:GoTagRm json` to remove json tags to struct fields +To clear all tags from struct run: `:GoTagClear` + NOTE: if you dont specify the tag it will use `json` as default Example: @@ -139,32 +136,14 @@ Example: Name string `yaml:name` } < ------------------------------------------------------------------------------- - *struct_tags.add()* - `struct_tags.add`({...}) -tags to a struct under the cursor -Parameters ~ -{...} `(string)` Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] - ------------------------------------------------------------------------------- - *struct_tags.remove()* - `struct_tags.remove`({...}) -tags from a struct under the cursor -Parameters ~ -{...} `(string)` Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] - ------------------------------------------------------------------------------- - *struct_tags.clear()* - `struct_tags.clear`() -all tags from a struct under the cursor - ============================================================================== ------------------------------------------------------------------------------ *gopher.nvim-impl* -impl is utilizing the `impl` tool to generate method stubs for interfaces. -Usage ~ +Integration of `impl` tool to generate method stubs for interfaces. + +Usage ~ 1. Automatically implement an interface for a struct: - Place your cursor on the struct where you want to implement the interface. - Run `:GoImpl io.Reader` @@ -214,17 +193,16 @@ Usage ~ You can also specify the template to use for generating the tests. See |gopher.nvim-config| More details about templates can be found at: https://github.com/cweill/gotests ------------------------------------------------------------------------------- - *gopher.nvim-gotests-named* - -You can enable named tests in the config if you prefer using named tests. -See |gopher.nvim-config|. +If you prefer named tests, you can enable them in |gopher.nvim-config|. ============================================================================== ------------------------------------------------------------------------------ *gopher.nvim-iferr* -If you're using `iferr` tool, this module provides a way to automatically insert `if err != nil` check. + +`iferr` provides a way to way to automatically insert `if err != nil` check. +If you want to change `-message` option of `iferr` tool, see |gopher.nvim-config| + Usage ~ Execute `:GoIfErr` near any `err` variable to insert the check @@ -232,9 +210,11 @@ Execute `:GoIfErr` near any `err` variable to insert the check ============================================================================== ------------------------------------------------------------------------------ *gopher.nvim-comments* -Usage ~ -Execute `:GoCmt` to generate a comment for the current function/method/struct/etc on this line. + This module provides a way to generate comments for Go code. +Usage ~ +Set cursor on line with function/method/struct/etc and run `:GoCmt` to generate a comment. + vim:tw=78:ts=8:noet:ft=help:norl: \ No newline at end of file diff --git a/lua/gopher/_utils/init.lua b/lua/gopher/_utils/init.lua index 0d9f5e2..1a0ce75 100644 --- a/lua/gopher/_utils/init.lua +++ b/lua/gopher/_utils/init.lua @@ -7,6 +7,7 @@ local utils = {} function utils.notify(msg, lvl) lvl = lvl or vim.log.levels.INFO vim.notify(msg, lvl, { + ---@diagnostic disable-next-line:undefined-field title = c.___plugin_name, }) log.debug(msg) @@ -30,4 +31,11 @@ function utils.remove_empty_lines(t) return res end +---@param s string +---@return string +function utils.trimend(s) + local r, _ = string.gsub(s, "%s+$", "") + return r +end + return utils diff --git a/lua/gopher/_utils/log.lua b/lua/gopher/_utils/log.lua index 9c22a4e..1c77369 100644 --- a/lua/gopher/_utils/log.lua +++ b/lua/gopher/_utils/log.lua @@ -18,6 +18,7 @@ local c = require "gopher.config" local config = { -- Name of the plugin. Prepended to log messages + ---@diagnostic disable-next-line:undefined-field name = c.___plugin_name, -- Should print the output to neovim while running diff --git a/lua/gopher/comment.lua b/lua/gopher/comment.lua index 6f68a3f..5a91ed1 100644 --- a/lua/gopher/comment.lua +++ b/lua/gopher/comment.lua @@ -1,7 +1,9 @@ ---@toc_entry Generate comments ---@tag gopher.nvim-comments ----@usage Execute `:GoCmt` to generate a comment for the current function/method/struct/etc on this line. ----@text This module provides a way to generate comments for Go code. +---@text +--- This module provides a way to generate comments for Go code. +--- +---@usage Set cursor on line with function/method/struct/etc and run `:GoCmt` to generate a comment. local ts = require "gopher._utils.ts" local log = require "gopher._utils.log" @@ -9,14 +11,14 @@ local comment = {} ---@param name string ---@return string ----@private +---@dochide local function template(name) return "// " .. name .. " " end ---@param bufnr integer ---@return string ----@private +---@dochide local function generate(bufnr) local s_ok, s_res = pcall(ts.get_struct_under_cursor, bufnr) if s_ok then diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index a5f0652..815b14b 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -1,17 +1,9 @@ ----@toc_entry Configuration ----@tag gopher.nvim-config ----@text config it is the place where you can configure the plugin. ---- also this is optional is you're ok with default settings. ---- You can look at default options |gopher.nvim-config-defaults| - ----@type gopher.Config ----@private local config = {} ---@tag gopher.nvim-config.ConfigGoTagTransform ---@text Possible values for |gopher.Config|.gotag.transform: --- ----@private +---@dochide ---@alias gopher.ConfigGoTagTransform ---| "snakecase" "GopherUser" -> "gopher_user" ---| "camelcase" "GopherUser" -> "gopherUser" @@ -20,15 +12,11 @@ local config = {} ---| "titlecase" "GopherUser" -> "Gopher User" ---| "keep" keeps the original field name ---minidoc_replace_start { - ----@tag gopher.nvim-config-defaults +---@toc_entry Config +---@tag gopher.nvim-config ---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section) ---- ---@class gopher.Config local default_config = { - --minidoc_replace_end - -- log level, you might consider using DEBUG or TRACE for debugging the plugin ---@type number log_level = vim.log.levels.INFO, @@ -73,7 +61,7 @@ local default_config = { --minidoc_afterlines_end ---@type gopher.Config ----@private +---@dochide local _config = default_config -- I am kinda secret so don't tell anyone about me even dont use me @@ -84,7 +72,7 @@ local _config = default_config _config.___plugin_name = "gopher.nvim" ---@diagnostic disable-line: inject-field ---@param user_config? gopher.Config ----@private +---@dochide function config.setup(user_config) vim.validate { user_config = { user_config, "table", true } } @@ -117,6 +105,6 @@ setmetatable(config, { end, }) +---@dochide ---@return gopher.Config ----@private return config diff --git a/lua/gopher/gotests.lua b/lua/gopher/gotests.lua index c5dac08..d896864 100644 --- a/lua/gopher/gotests.lua +++ b/lua/gopher/gotests.lua @@ -15,11 +15,7 @@ --- You can also specify the template to use for generating the tests. See |gopher.nvim-config| --- More details about templates can be found at: https://github.com/cweill/gotests --- - ----@tag gopher.nvim-gotests-named ----@text ---- You can enable named tests in the config if you prefer using named tests. ---- See |gopher.nvim-config|. +--- If you prefer named tests, you can enable them in |gopher.nvim-config|. local c = require "gopher.config" local ts_utils = require "gopher._utils.ts" @@ -29,7 +25,7 @@ local log = require "gopher._utils.log" local gotests = {} ---@param args table ----@private +---@dochide local function add_test(args) if c.gotests.named then table.insert(args, "-named") diff --git a/lua/gopher/iferr.lua b/lua/gopher/iferr.lua index c7c7450..78d060f 100644 --- a/lua/gopher/iferr.lua +++ b/lua/gopher/iferr.lua @@ -1,6 +1,11 @@ +-- Thanks https://github.com/koron/iferr for vim implementation + ---@toc_entry Iferr ---@tag gopher.nvim-iferr ----@text If you're using `iferr` tool, this module provides a way to automatically insert `if err != nil` check. +---@text +--- `iferr` provides a way to way to automatically insert `if err != nil` check. +--- If you want to change `-message` option of `iferr` tool, see |gopher.nvim-config| +--- ---@usage Execute `:GoIfErr` near any `err` variable to insert the check local c = require "gopher.config" @@ -9,7 +14,6 @@ local r = require "gopher._utils.runner" local log = require "gopher._utils.log" local iferr = {} --- That's Lua implementation: https://github.com/koron/iferr function iferr.iferr() local curb = vim.fn.wordcount().cursor_bytes local pos = vim.fn.getcurpos()[2] diff --git a/lua/gopher/impl.lua b/lua/gopher/impl.lua index 3a731bf..702d58e 100644 --- a/lua/gopher/impl.lua +++ b/lua/gopher/impl.lua @@ -1,8 +1,9 @@ ---@toc_entry Auto implementation of interface methods ---@tag gopher.nvim-impl ----@text impl is utilizing the `impl` tool to generate method stubs for interfaces. ----@usage ---- 1. Automatically implement an interface for a struct: +---@text +--- Integration of `impl` tool to generate method stubs for interfaces. +--- +---@usage 1. Automatically implement an interface for a struct: --- - Place your cursor on the struct where you want to implement the interface. --- - Run `:GoImpl io.Reader` --- - This will automatically determine the receiver and implement the `io.Reader` interface. diff --git a/lua/gopher/init.lua b/lua/gopher/init.lua index 16cfb17..9afac0c 100644 --- a/lua/gopher/init.lua +++ b/lua/gopher/init.lua @@ -1,12 +1,13 @@ ---- *gopher.nvim* +--- *gopher.nvim* Enhance your golang experience +--- +--- MIT License Copyright (c) 2025 Oleksandr Smirnov --- --- ============================================================================== --- --- gopher.nvim is a minimalistic plugin for Go development in Neovim written in Lua. --- It's not an LSP tool, the main goal of this plugin is add go tooling support in Neovim. - +--- --- Table of Contents ----@tag gopher.nvim-table-of-contents ---@toc local log = require "gopher._utils.log" @@ -16,14 +17,16 @@ local gocmd = require("gopher._utils.gocmd").run local gopher = {} ---@toc_entry Setup ----@tag gopher.nvim-setup +---@tag gopher.nvim-setup() ---@text Setup function. This method simply merges default config with opts table. --- You can read more about configuration at |gopher.nvim-config| --- Calling this function is optional, if you ok with default settings. ---- See |gopher.nvim.config-defaults| +--- See |gopher.nvim.config| --- ----@usage `require("gopher").setup {}` (replace `{}` with your `config` table) ----@param user_config gopher.Config +---@usage >lua +--- require("gopher").setup {} -- use default config or replace {} with your own +--- < +---@param user_config gopher.Config See |gopher.nvim-config| gopher.setup = function(user_config) log.debug "setting up config" require("gopher.config").setup(user_config) @@ -31,7 +34,7 @@ gopher.setup = function(user_config) end ---@toc_entry Install dependencies ----@tag gopher.nvim-install-deps +---@tag gopher.nvim-dependencies ---@text Gopher.nvim implements most of its features using third-party tools. --- To install these tools, you can run `:GoInstallDeps` command --- or call `require("gopher").install_deps()` if you want to use lua api. diff --git a/lua/gopher/struct_tags.lua b/lua/gopher/struct_tags.lua index 86689e4..cf990bf 100644 --- a/lua/gopher/struct_tags.lua +++ b/lua/gopher/struct_tags.lua @@ -1,12 +1,16 @@ ---@toc_entry Modify struct tags ---@tag gopher.nvim-struct-tags ----@text struct-tags is utilizing the `gomodifytags` tool to add or remove tags to struct fields. +---@text +--- `struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to struct fields. +--- ---@usage --- How to add/remove tags to struct fields: --- 1. Place cursor on the struct +--- 1. Place cursor on the struct --- 2. Run `:GoTagAdd json` to add json tags to struct fields --- 3. Run `:GoTagRm json` to remove json tags to struct fields --- +--- To clear all tags from struct run: `:GoTagClear` +--- --- NOTE: if you dont specify the tag it will use `json` as default --- --- Example: @@ -29,13 +33,14 @@ local ts = require "gopher._utils.ts" local r = require "gopher._utils.runner" local c = require "gopher.config" +local u = require "gopher._utils" local log = require "gopher._utils.log" local struct_tags = {} ---@param fpath string ---@param bufnr integer ---@param user_args string[] ----@private +---@dochide local function handle_tags(fpath, bufnr, user_args) local st = ts.get_struct_under_cursor(bufnr) @@ -73,7 +78,7 @@ local function handle_tags(fpath, bufnr, user_args) end for i, v in ipairs(res["lines"]) do - res["lines"][i] = string.gsub(v, "%s+$", "") + res["lines"][i] = u.trimend(v) end vim.api.nvim_buf_set_lines( @@ -87,7 +92,7 @@ end ---@param args string[] ---@return string ----@private +---@dochide local function handler_user_args(args) if #args == 0 then return c.gotag.default_tag @@ -95,8 +100,10 @@ local function handler_user_args(args) return table.concat(args, ",") end ----Adds tags to a struct under the cursor +-- Adds tags to a struct under the cursor +-- See |gopher.nvim-struct-tags| ---@param ... string Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] +---@dochide function struct_tags.add(...) local args = { ... } local fpath = vim.fn.expand "%" @@ -106,7 +113,9 @@ function struct_tags.add(...) handle_tags(fpath, bufnr, { "-add-tags", user_tags }) end ----Removes tags from a struct under the cursor +-- Removes tags from a struct under the cursor +-- See `:h gopher.nvim-struct-tags` +---@dochide ---@param ... string Tags to add to the struct fields. If not provided, it will use [config.gotag.default_tag] function struct_tags.remove(...) local args = { ... } @@ -117,7 +126,9 @@ function struct_tags.remove(...) handle_tags(fpath, bufnr, { "-remove-tags", user_tags }) end ----Removes all tags from a struct under the cursor +-- Removes all tags from a struct under the cursor +-- See `:h gopher.nvim-struct-tags` +---@dochide function struct_tags.clear() local fpath = vim.fn.expand "%" local bufnr = vim.api.nvim_get_current_buf() diff --git a/scripts/docgen.lua b/scripts/docgen.lua index 381d3c4..1c66b90 100644 --- a/scripts/docgen.lua +++ b/scripts/docgen.lua @@ -30,4 +30,8 @@ hooks.write_pre = function(lines) return lines end +hooks.sections["@dochide"] = function(s) + s.parent:clear_lines() +end + MiniDoc.generate(files, "doc/gopher.nvim.txt", { hooks = hooks }) diff --git a/spec/unit/utils_test.lua b/spec/unit/utils_test.lua index 7001261..4be8126 100644 --- a/spec/unit/utils_test.lua +++ b/spec/unit/utils_test.lua @@ -1,15 +1,6 @@ local t = require "spec.testutils" -local child = MiniTest.new_child_neovim() -local T = MiniTest.new_set { - hooks = { - post_once = child.stop, - pre_case = function() - child.restart { "-u", t.mininit_path } - end, - }, -} +local _, T = t.setup "utils" -T["utils"] = MiniTest.new_set() T["utils"]["should .remove_empty_lines()"] = function() local u = require "gopher._utils" local inp = { "hi", "", "a", "", "", "asdf" } @@ -26,4 +17,9 @@ T["utils"]["should .readfile_joined()"] = function() t.eq(u.readfile_joined(tmp), data) end +T["utils"]["should .trimend()"] = function() + local u = require "gopher._utils" + t.eq(u.trimend " hi ", " hi") +end + return T From 77754fe3624eaa9a59139452cf869b5fa91b6b28 Mon Sep 17 00:00:00 2001 From: Smirnov Oleksandr Date: Sun, 23 Mar 2025 19:06:13 +0200 Subject: [PATCH 24/25] fix: nightly(0.11) (#104) * chore: fix minimal_init, load default plugins correctly * refactor(ts): make it work on nightly * chore: get nightly back in ci * fix(tests): some how i now i need to run vim.treesitter.start() to make it work * feat(ts): check if parser is found * chore: use --clean instead --noplugin * refactor(tests): use auto commands instead of putting it in each test * chore: show the diff of the doc --- .github/workflows/linters.yml | 4 +++- .github/workflows/tests.yml | 2 +- Taskfile.yml | 5 ++--- doc/gopher.nvim.txt | 18 +++++++++--------- lua/gopher/_utils/ts.lua | 19 ++++++++++--------- scripts/minimal_init.lua | 12 ++++++++++-- spec/testutils.lua | 5 ++++- 7 files changed, 39 insertions(+), 26 deletions(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 175465e..0122b93 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -53,4 +53,6 @@ jobs: run: task docgen - name: Diff - run: exit $(git status --porcelain doc | wc -l | tr -d " ") + run: | + git diff doc + exit $(git status --porcelain doc | wc -l | tr -d " ") diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2eaf9fa..103b18e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: os: [ubuntu-latest] version: - stable - # - nightly # TODO: enable when stable + - nightly runs-on: ${{ matrix.os }} steps: - name: Install Task diff --git a/Taskfile.yml b/Taskfile.yml index 3900751..19cbc8f 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -20,7 +20,7 @@ tasks: desc: run all tests cmds: - | - nvim --headless \ + nvim --clean --headless \ -u ./scripts/minimal_init.lua \ -c "lua MiniTest.run()" @@ -28,8 +28,7 @@ tasks: desc: generate vimhelp cmds: - | - nvim --noplugin \ - --headless \ + nvim --clean --headless \ -u "./scripts/minimal_init.lua" \ -c "luafile ./scripts/docgen.lua" \ -c ":qa!" diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt index 3d653ca..47aa005 100644 --- a/doc/gopher.nvim.txt +++ b/doc/gopher.nvim.txt @@ -8,15 +8,15 @@ gopher.nvim is a minimalistic plugin for Go development in Neovim written in Lua It's not an LSP tool, the main goal of this plugin is add go tooling support in Neovim. Table of Contents - Setup..................................................|gopher.nvim-setup()| - Install dependencies..............................|gopher.nvim-dependencies| - Config..................................................|gopher.nvim-config| - Commands..............................................|gopher.nvim-commands| - Modify struct tags.................................|gopher.nvim-struct-tags| - Auto implementation of interface methods..................|gopher.nvim-impl| - Generating unit tests boilerplate......................|gopher.nvim-gotests| - Iferr....................................................|gopher.nvim-iferr| - Generate comments.....................................|gopher.nvim-comments| + Setup ................................................ |gopher.nvim-setup()| + Install dependencies ............................ |gopher.nvim-dependencies| + Config ................................................ |gopher.nvim-config| + Commands ............................................ |gopher.nvim-commands| + Modify struct tags ............................... |gopher.nvim-struct-tags| + Auto implementation of interface methods ................ |gopher.nvim-impl| + Generating unit tests boilerplate .................... |gopher.nvim-gotests| + Iferr .................................................. |gopher.nvim-iferr| + Generate comments ................................... |gopher.nvim-comments| ------------------------------------------------------------------------------ *gopher.nvim-setup()* diff --git a/lua/gopher/_utils/ts.lua b/lua/gopher/_utils/ts.lua index b5e6d32..29d38fe 100644 --- a/lua/gopher/_utils/ts.lua +++ b/lua/gopher/_utils/ts.lua @@ -50,16 +50,13 @@ end ---@return {name:string, is_varstruct:boolean} local function get_captures(query, node, bufnr) local res = {} - for _, match, _ in query:iter_matches(node, bufnr) do - for capture_id, captured_node in pairs(match) do - local capture_name = query.captures[capture_id] - if capture_name == "_name" then - res["name"] = vim.treesitter.get_node_text(captured_node, bufnr) - end + for id, _node in query:iter_captures(node, bufnr) do + if query.captures[id] == "_name" then + res["name"] = vim.treesitter.get_node_text(_node, bufnr) + end - if capture_name == "_var" then - res["is_varstruct"] = true - end + if query.captures[id] == "_var" then + res["is_varstruct"] = true end end @@ -77,6 +74,10 @@ end ---@param query string ---@return gopher.TsResult local function do_stuff(bufnr, parent_type, query) + if not vim.treesitter.get_parser(bufnr, "go") then + error "No treesitter parser found for go" + end + local node = vim.treesitter.get_node { bufnr = bufnr, } diff --git a/scripts/minimal_init.lua b/scripts/minimal_init.lua index 503bf49..b0f51e6 100644 --- a/scripts/minimal_init.lua +++ b/scripts/minimal_init.lua @@ -30,9 +30,8 @@ vim.env.XDG_DATA_HOME = root ".tests/data" vim.env.XDG_STATE_HOME = root ".tests/state" vim.env.XDG_CACHE_HOME = root ".tests/cache" -vim.cmd [[set runtimepath=$VIMRUNTIME]] vim.opt.runtimepath:append(root()) -vim.opt.packpath = { root ".tests/site" } +vim.opt.packpath:append(root ".tests/site") vim.notify = vim.print -- install go treesitter parse @@ -53,3 +52,12 @@ if #vim.api.nvim_list_uis() == 0 then }, } end + +-- needed for tests, i dont know the reason why, but on start +-- vim is not able to use treesitter for go by default +vim.api.nvim_create_autocmd("FileType", { + pattern = "go", + callback = function(args) + vim.treesitter.start(args.buf, "go") + end, +}) diff --git a/spec/testutils.lua b/spec/testutils.lua index 1a31e10..b359b4b 100644 --- a/spec/testutils.lua +++ b/spec/testutils.lua @@ -69,6 +69,7 @@ end ---@class gopher.TestUtilsSetup ---@field tmp string ---@field fixtures gopher.TestUtilsFixtures +---@field bufnr number ---@param fixture string ---@param child MiniTest.child @@ -81,12 +82,14 @@ function testutils.setup_test(fixture, child, pos) testutils.writefile(tmp, fixtures.input) child.cmd("silent edit " .. tmp) + local bufnr = child.fn.bufnr(tmp) if pos then - child.fn.setpos(".", { child.fn.bufnr(tmp), unpack(pos) }) + child.fn.setpos(".", { bufnr, unpack(pos) }) end return { tmp = tmp, + bufnr = bufnr, fixtures = fixtures, } end From 7725689d1d811d60cc719e31249a17b8d2701375 Mon Sep 17 00:00:00 2001 From: Oleksandr Smirnov Date: Sun, 23 Mar 2025 22:42:56 +0200 Subject: [PATCH 25/25] feat: add pkg.json --- pkg.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 pkg.json diff --git a/pkg.json b/pkg.json new file mode 100644 index 0000000..4118802 --- /dev/null +++ b/pkg.json @@ -0,0 +1,13 @@ +{ + "name": "gopher.nvim", + "engines": { + "nvim": "^0.10.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/olexsmir/gopher.nvim" + }, + "dependencies": { + "https://github.com/nvim-treesitter/nvim-treesitter": "*" + } +}