[mirror] Make your go dev experience better github.com/olexsmir/gopher.nvim
neovim golang

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

authored by olexsmir.xyz and committed by GitHub e9f2eef5 f171953e

-40
.github/workflows/docs.yml
··· 1 - name: docs 2 - 3 - on: 4 - push: 5 - branches: 6 - - main 7 - - develop 8 - pull_request: 9 - 10 - jobs: 11 - docs: 12 - name: linters 13 - runs-on: ubuntu-latest 14 - steps: 15 - - uses: actions/checkout@v4 16 - 17 - - name: Install Task 18 - uses: arduino/setup-task@v1 19 - with: 20 - version: 3.x 21 - repo-token: ${{ secrets.GITHUB_TOKEN }} 22 - 23 - - name: Install NeoVim 24 - uses: rhysd/action-setup-vim@v1 25 - with: 26 - neovim: true 27 - version: stable 28 - 29 - - name: Cache .tests 30 - uses: actions/cache@v4 31 - with: 32 - path: | 33 - ${{ github.workspace }}/.tests 34 - key: ${{ runner.os }}-tests-${{ hashFiles('${{ github.workspace }}/.tests') }} 35 - 36 - - name: Generate docs 37 - run: task docgen 38 - 39 - - name: Check docs diff 40 - run: exit $(git status --porcelain doc | wc -l | tr -d " ")
+32 -1
.github/workflows/linters.yml
··· 9 9 10 10 jobs: 11 11 linters: 12 - name: linters 12 + name: Lua 13 13 runs-on: ubuntu-latest 14 14 steps: 15 15 - uses: actions/checkout@v4 ··· 23 23 with: 24 24 token: ${{ secrets.GITHUB_TOKEN }} 25 25 args: . 26 + 27 + docs: 28 + name: Docs 29 + runs-on: ubuntu-latest 30 + steps: 31 + - uses: actions/checkout@v4 32 + 33 + - name: Install Task 34 + uses: arduino/setup-task@v1 35 + with: 36 + version: 3.x 37 + repo-token: ${{ secrets.GITHUB_TOKEN }} 38 + 39 + - name: Install NeoVim 40 + uses: rhysd/action-setup-vim@v1 41 + with: 42 + neovim: true 43 + version: stable 44 + 45 + - name: Cache .tests 46 + uses: actions/cache@v4 47 + with: 48 + path: | 49 + ${{ github.workspace }}/.tests 50 + key: ${{ runner.os }}-tests-${{ hashFiles('${{ github.workspace }}/.tests') }} 51 + 52 + - name: Generate docs 53 + run: task docgen 54 + 55 + - name: Diff 56 + run: exit $(git status --porcelain doc | wc -l | tr -d " ")
+1 -1
.github/workflows/tests.yml
··· 14 14 os: [ubuntu-latest] 15 15 version: 16 16 - stable 17 - - nightly 17 + # - nightly # TODO: enable when stable 18 18 runs-on: ${{ matrix.os }} 19 19 steps: 20 20 - name: Install Task
+1 -1
lua/gopher/_utils/log.lua
··· 91 91 local log_at_level = function(level_config, message_maker, ...) 92 92 -- Return early if we're below the current_log_level 93 93 -- 94 - -- the log level source get from config directly because otherwise it doesnt work 94 + -- the log level source get from config directly because otherwise it doesn't work 95 95 if level_config.level < c.log_level then 96 96 return 97 97 end
+2 -2
lua/gopher/_utils/runner/gocmd.lua
··· 28 28 ---@return string[]|nil 29 29 function gocmd.run(subcmd, args) 30 30 if #args == 0 then 31 - error "please provice any arguments" 31 + error "please provide any arguments" 32 32 end 33 33 34 34 if subcmd == "get" then ··· 45 45 if status ~= 0 then 46 46 error("gocmd failed: " .. data) 47 47 end 48 - u.notify(c.go .. " " .. subcmd .. " successful runned") 48 + u.notify(c.go .. " " .. subcmd .. " ran successful") 49 49 end, 50 50 }) 51 51 end
+115
lua/gopher/_utils/ts.lua
··· 1 + local ts = {} 2 + local queries = { 3 + struct = [[ 4 + (type_spec name: (type_identifier) @_name 5 + type: (struct_type)) 6 + ]], 7 + func = [[ 8 + [(function_declaration name: (identifier) @_name) 9 + (method_declaration name: (field_identifier) @_name)] 10 + ]], 11 + package = [[ 12 + (package_identifier) @_name 13 + ]], 14 + interface = [[ 15 + (type_spec 16 + name: (type_identifier) @_name 17 + type: (interface_type)) 18 + ]], 19 + } 20 + 21 + ---@param parent_type string[] 22 + ---@param node TSNode 23 + ---@return TSNode? 24 + local function get_parrent_node(parent_type, node) 25 + ---@type TSNode? 26 + local current = node 27 + while current do 28 + if vim.tbl_contains(parent_type, current:type()) then 29 + break 30 + end 31 + 32 + current = current:parent() 33 + if current == nil then 34 + return nil 35 + end 36 + end 37 + return current 38 + end 39 + 40 + ---@param query vim.treesitter.Query 41 + ---@param node TSNode 42 + ---@param bufnr integer 43 + ---@return {name:string} 44 + local function get_captures(query, node, bufnr) 45 + local res = {} 46 + for _, match, _ in query:iter_matches(node, bufnr) do 47 + for capture_id, captured_node in pairs(match) do 48 + local capture_name = query.captures[capture_id] 49 + if capture_name == "_name" then 50 + res["name"] = vim.treesitter.get_node_text(captured_node, bufnr) 51 + end 52 + end 53 + end 54 + 55 + return res 56 + end 57 + 58 + ---@class gopher.TsResult 59 + ---@field name string 60 + ---@field start_line integer 61 + ---@field end_line integer 62 + 63 + ---@param bufnr integer 64 + ---@param parent_type string[] 65 + ---@param query string 66 + ---@return gopher.TsResult 67 + local function do_stuff(bufnr, parent_type, query) 68 + local node = vim.treesitter.get_node { 69 + bufnr = bufnr, 70 + } 71 + if not node then 72 + error "No nodes found under cursor" 73 + end 74 + 75 + local parent_node = get_parrent_node(parent_type, node) 76 + if not parent_node then 77 + error "No parent node found under cursor" 78 + end 79 + 80 + local q = vim.treesitter.query.parse("go", query) 81 + local res = get_captures(q, parent_node, bufnr) 82 + assert(res.name ~= nil, "No capture name found") 83 + 84 + local start_row, _, end_row, _ = parent_node:range() 85 + res["start_line"] = start_row + 1 86 + res["end_line"] = end_row + 1 87 + 88 + return res 89 + end 90 + 91 + ---@param bufnr integer 92 + function ts.get_struct_under_cursor(bufnr) 93 + --- should be both type_spec and type_declaration 94 + --- because in cases like `type ( T struct{}, U strict{} )` 95 + --- i will be choosing always last struct in the list 96 + return do_stuff(bufnr, { "type_spec", "type_declaration" }, queries.struct) 97 + end 98 + 99 + ---@param bufnr integer 100 + function ts.get_func_under_cursor(bufnr) 101 + --- since this handles both and funcs and methods we should check for both parent nodes 102 + return do_stuff(bufnr, { "function_declaration", "method_declaration" }, queries.func) 103 + end 104 + 105 + ---@param bufnr integer 106 + function ts.get_package_under_cursor(bufnr) 107 + return do_stuff(bufnr, { "package_clause" }, queries.package) 108 + end 109 + 110 + ---@param bufnr integer 111 + function ts.get_interface_under_cursor(bufnr) 112 + return do_stuff(bufnr, { "type_declaration" }, queries.interface) 113 + end 114 + 115 + return ts
-104
lua/gopher/_utils/ts/init.lua
··· 1 - ---@diagnostic disable: param-type-mismatch 2 - local nodes = require "gopher._utils.ts.nodes" 3 - local u = require "gopher._utils" 4 - local ts = { 5 - querys = { 6 - struct_block = [[((type_declaration (type_spec name:(type_identifier) @struct.name type: (struct_type)))@struct.declaration)]], 7 - em_struct_block = [[(field_declaration name:(field_identifier)@struct.name type: (struct_type)) @struct.declaration]], 8 - package = [[(package_clause (package_identifier)@package.name)@package.clause]], 9 - interface = [[((type_declaration (type_spec name:(type_identifier) @interface.name type:(interface_type)))@interface.declaration)]], 10 - method_name = [[((method_declaration receiver: (parameter_list)@method.receiver name: (field_identifier)@method.name body:(block))@method.declaration)]], 11 - func = [[((function_declaration name: (identifier)@function.name) @function.declaration)]], 12 - }, 13 - } 14 - 15 - ---@return table 16 - local function get_name_defaults() 17 - return { 18 - ["func"] = "function", 19 - ["if"] = "if", 20 - ["else"] = "else", 21 - ["for"] = "for", 22 - } 23 - end 24 - 25 - ---@param row string 26 - ---@param col string 27 - ---@param bufnr string|nil 28 - ---@param do_notify boolean|nil 29 - ---@return table|nil 30 - function ts.get_struct_node_at_pos(row, col, bufnr, do_notify) 31 - local notify = do_notify or true 32 - local query = ts.querys.struct_block .. " " .. ts.querys.em_struct_block 33 - local bufn = bufnr or vim.api.nvim_get_current_buf() 34 - local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) 35 - if ns == nil then 36 - if notify then 37 - u.deferred_notify("struct not found", vim.log.levels.WARN) 38 - end 39 - else 40 - return ns[#ns] 41 - end 42 - end 43 - 44 - ---@param row string 45 - ---@param col string 46 - ---@param bufnr string|nil 47 - ---@param do_notify boolean|nil 48 - ---@return table|nil 49 - function ts.get_func_method_node_at_pos(row, col, bufnr, do_notify) 50 - local notify = do_notify or true 51 - local query = ts.querys.func .. " " .. ts.querys.method_name 52 - local bufn = bufnr or vim.api.nvim_get_current_buf() 53 - local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) 54 - if ns == nil then 55 - if notify then 56 - u.deferred_notify("function not found", vim.log.levels.WARN) 57 - end 58 - else 59 - return ns[#ns] 60 - end 61 - end 62 - 63 - ---@param row string 64 - ---@param col string 65 - ---@param bufnr string|nil 66 - ---@param do_notify boolean|nil 67 - ---@return table|nil 68 - function ts.get_package_node_at_pos(row, col, bufnr, do_notify) 69 - local notify = do_notify or true 70 - -- stylua: ignore 71 - if row > 10 then return end 72 - local query = ts.querys.package 73 - local bufn = bufnr or vim.api.nvim_get_current_buf() 74 - local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) 75 - if ns == nil then 76 - if notify then 77 - u.deferred_notify("package not found", vim.log.levels.WARN) 78 - return nil 79 - end 80 - else 81 - return ns[#ns] 82 - end 83 - end 84 - 85 - ---@param row string 86 - ---@param col string 87 - ---@param bufnr string|nil 88 - ---@param do_notify boolean|nil 89 - ---@return table|nil 90 - function ts.get_interface_node_at_pos(row, col, bufnr, do_notify) 91 - local notify = do_notify or true 92 - local query = ts.querys.interface 93 - local bufn = bufnr or vim.api.nvim_get_current_buf() 94 - local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) 95 - if ns == nil then 96 - if notify then 97 - u.deferred_notify("interface not found", vim.log.levels.WARN) 98 - end 99 - else 100 - return ns[#ns] 101 - end 102 - end 103 - 104 - return ts
-143
lua/gopher/_utils/ts/nodes.lua
··· 1 - local ts_query = require "nvim-treesitter.query" 2 - local parsers = require "nvim-treesitter.parsers" 3 - local locals = require "nvim-treesitter.locals" 4 - local u = require "gopher._utils" 5 - local M = {} 6 - 7 - local function intersects(row, col, sRow, sCol, eRow, eCol) 8 - if sRow > row or eRow < row then 9 - return false 10 - end 11 - 12 - if sRow == row and sCol > col then 13 - return false 14 - end 15 - 16 - if eRow == row and eCol < col then 17 - return false 18 - end 19 - 20 - return true 21 - end 22 - 23 - ---@param nodes table 24 - ---@param row string 25 - ---@param col string 26 - ---@return table 27 - function M.intersect_nodes(nodes, row, col) 28 - local found = {} 29 - for idx = 1, #nodes do 30 - local node = nodes[idx] 31 - local sRow = node.dim.s.r 32 - local sCol = node.dim.s.c 33 - local eRow = node.dim.e.r 34 - local eCol = node.dim.e.c 35 - 36 - if intersects(row, col, sRow, sCol, eRow, eCol) then 37 - table.insert(found, node) 38 - end 39 - end 40 - 41 - return found 42 - end 43 - 44 - ---@param nodes table 45 - ---@return table 46 - function M.sort_nodes(nodes) 47 - table.sort(nodes, function(a, b) 48 - return M.count_parents(a) < M.count_parents(b) 49 - end) 50 - 51 - return nodes 52 - end 53 - 54 - ---@param query string 55 - ---@param lang string 56 - ---@param bufnr integer 57 - ---@param pos_row string 58 - ---@return string 59 - function M.get_all_nodes(query, lang, _, bufnr, pos_row, _) 60 - bufnr = bufnr or 0 61 - pos_row = pos_row or 30000 62 - 63 - local ok, parsed_query = pcall(function() 64 - return vim.treesitter.query.parse(lang, query) 65 - end) 66 - if not ok then 67 - return nil 68 - end 69 - 70 - local parser = parsers.get_parser(bufnr, lang) 71 - local root = parser:parse()[1]:root() 72 - local start_row, _, end_row, _ = root:range() 73 - local results = {} 74 - 75 - for match in ts_query.iter_prepared_matches(parsed_query, root, bufnr, start_row, end_row) do 76 - local sRow, sCol, eRow, eCol, declaration_node 77 - local type, name, op = "", "", "" 78 - locals.recurse_local_nodes(match, function(_, node, path) 79 - local idx = string.find(path, ".[^.]*$") 80 - op = string.sub(path, idx + 1, #path) 81 - type = string.sub(path, 1, idx - 1) 82 - 83 - if op == "name" then 84 - name = vim.treesitter.get_node_text(node, bufnr) 85 - elseif op == "declaration" or op == "clause" then 86 - declaration_node = node 87 - sRow, sCol, eRow, eCol = node:range() 88 - sRow = sRow + 1 89 - eRow = eRow + 1 90 - sCol = sCol + 1 91 - eCol = eCol + 1 92 - end 93 - end) 94 - 95 - if declaration_node ~= nil then 96 - table.insert(results, { 97 - declaring_node = declaration_node, 98 - dim = { s = { r = sRow, c = sCol }, e = { r = eRow, c = eCol } }, 99 - name = name, 100 - operator = op, 101 - type = type, 102 - }) 103 - end 104 - end 105 - 106 - return results 107 - end 108 - 109 - ---@param query string 110 - ---@param default string 111 - ---@param bufnr string 112 - ---@param row string 113 - ---@param col string 114 - ---@return table 115 - function M.nodes_at_cursor(query, default, bufnr, row, col) 116 - bufnr = bufnr or vim.api.nvim_get_current_buf() 117 - local ft = vim.api.nvim_buf_get_option(bufnr, "ft") 118 - if row == nil or col == nil then 119 - row, col = unpack(vim.api.nvim_win_get_cursor(0)) 120 - end 121 - 122 - local nodes = M.get_all_nodes(query, ft, default, bufnr, row, col) 123 - if nodes == nil then 124 - u.deferred_notify( 125 - "Unable to find any nodes. Place your cursor on a go symbol and try again", 126 - vim.log.levels.DEBUG 127 - ) 128 - return nil 129 - end 130 - 131 - nodes = M.sort_nodes(M.intersect_nodes(nodes, row, col)) 132 - if nodes == nil or #nodes == 0 then 133 - u.deferred_notify( 134 - "Unable to find any nodes at pos. " .. tostring(row) .. ":" .. tostring(col), 135 - vim.log.levels.DEBUG 136 - ) 137 - return nil 138 - end 139 - 140 - return nodes 141 - end 142 - 143 - return M
+35 -39
lua/gopher/comment.lua
··· 3 3 ---@usage Execute `:GoCmt` to generate a comment for the current function/method/struct/etc on this line. 4 4 ---@text This module provides a way to generate comments for Go code. 5 5 6 + local ts = require "gopher._utils.ts" 6 7 local log = require "gopher._utils.log" 8 + local comment = {} 7 9 8 - local function generate(row, col) 9 - local ts_utils = require "gopher._utils.ts" 10 - local comment, ns = nil, nil 10 + ---@param name string 11 + ---@return string 12 + ---@private 13 + local function template(name) 14 + return "// " .. name .. " " 15 + end 11 16 12 - ns = ts_utils.get_package_node_at_pos(row, col, nil, false) 13 - if ns ~= nil then 14 - comment = "// Package " .. ns.name .. " provides " .. ns.name 15 - return comment, ns 17 + ---@param bufnr integer 18 + ---@return string 19 + ---@private 20 + local function generate(bufnr) 21 + local s_ok, s_res = pcall(ts.get_struct_under_cursor, bufnr) 22 + if s_ok then 23 + return template(s_res.name) 16 24 end 17 25 18 - ns = ts_utils.get_struct_node_at_pos(row, col, nil, false) 19 - if ns ~= nil then 20 - comment = "// " .. ns.name .. " " .. ns.type .. " " 21 - return comment, ns 26 + local f_ok, f_res = pcall(ts.get_func_under_cursor, bufnr) 27 + if f_ok then 28 + return template(f_res.name) 22 29 end 23 30 24 - ns = ts_utils.get_func_method_node_at_pos(row, col, nil, false) 25 - if ns ~= nil then 26 - comment = "// " .. ns.name .. " " .. ns.type .. " " 27 - return comment, ns 31 + local i_ok, i_res = pcall(ts.get_interface_under_cursor, bufnr) 32 + if i_ok then 33 + return template(i_res.name) 28 34 end 29 35 30 - ns = ts_utils.get_interface_node_at_pos(row, col, nil, false) 31 - if ns ~= nil then 32 - comment = "// " .. ns.name .. " " .. ns.type .. " " 33 - return comment, ns 36 + local p_ok, p_res = pcall(ts.get_package_under_cursor, bufnr) 37 + if p_ok then 38 + return "// Package " .. p_res.name .. " provides " 34 39 end 35 40 36 - return "// ", {} 41 + return "// " 37 42 end 38 43 39 - return function() 40 - local row, col = unpack(vim.api.nvim_win_get_cursor(0)) 41 - local comment, ns = generate(row + 1, col + 1) 44 + function comment.comment() 45 + local bufnr = vim.api.nvim_get_current_buf() 46 + local cmt = generate(bufnr) 47 + log.debug("generated comment: " .. cmt) 42 48 43 - log.debug("generated comment: " .. comment) 44 - 45 - vim.api.nvim_win_set_cursor(0, { 46 - ns.dim.s.r, 47 - ns.dim.s.c, 48 - }) 49 - 50 - ---@diagnostic disable-next-line: param-type-mismatch 51 - vim.fn.append(row - 1, comment) 49 + local pos = vim.fn.getcurpos()[2] 50 + vim.fn.append(pos - 1, cmt) 51 + vim.fn.setpos(".", { 0, pos, #cmt }) 52 + vim.cmd "startinsert!" 53 + end 52 54 53 - vim.api.nvim_win_set_cursor(0, { 54 - ns.dim.s.r, 55 - #comment + 1, 56 - }) 57 - 58 - vim.cmd [[startinsert!]] 59 - end 55 + return comment
+3 -6
lua/gopher/gotests.lua
··· 77 77 78 78 -- generate unit test for one function 79 79 function gotests.func_test() 80 - local ns = ts_utils.get_func_method_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0))) 81 - if ns == nil or ns.name == nil then 82 - u.notify("cursor on func/method and execute the command again", vim.log.levels.WARN) 83 - return 84 - end 80 + local bufnr = vim.api.nvim_get_current_buf() 81 + local func = ts_utils.get_func_under_cursor(bufnr) 85 82 86 - add_test { "-only", ns.name } 83 + add_test { "-only", func.name } 87 84 end 88 85 89 86 -- generate unit tests for all functions in current file
+12 -38
lua/gopher/impl.lua
··· 38 38 local u = require "gopher._utils" 39 39 local impl = {} 40 40 41 - ---@return string 42 - ---@private 43 - local function get_struct() 44 - local ns = ts_utils.get_struct_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0))) 45 - if ns == nil then 46 - u.notify "put cursor on a struct or specify a receiver" 47 - return "" 48 - end 49 - 50 - vim.api.nvim_win_set_cursor(0, { 51 - ns.dim.e.r, 52 - ns.dim.e.c, 53 - }) 54 - 55 - return ns.name 56 - end 57 - 58 41 function impl.impl(...) 59 42 local args = { ... } 60 - local iface, recv_name = "", "" 61 - local recv = get_struct() 43 + local iface, recv = "", "" 44 + local bufnr = vim.api.nvim_get_current_buf() 62 45 63 - if #args == 0 then 64 - iface = vim.fn.input "impl: generating method stubs for interface: " 65 - vim.cmd "redraw!" 66 - if iface == "" then 67 - u.deferred_notify("usage: GoImpl f *File io.Reader", vim.log.levels.INFO) 68 - return 69 - end 70 - elseif #args == 1 then -- :GoImpl io.Reader 71 - recv = string.lower(recv) .. " *" .. recv 72 - vim.cmd "redraw!" 73 - iface = select(1, ...) 46 + if #args == 1 then -- :GoImpl io.Reader 47 + local st = ts_utils.get_struct_under_cursor(bufnr) 48 + iface = args[1] 49 + recv = string.lower(st.name) .. " *" .. st.name 74 50 elseif #args == 2 then -- :GoImpl w io.Writer 75 - recv_name = select(1, ...) 76 - recv = string.format("%s *%s", recv_name, recv) 77 - iface = select(#args, ...) 78 - elseif #args > 2 then 79 - iface = select(#args, ...) 80 - recv = select(#args - 1, ...) 81 - recv_name = select(#args - 2, ...) 82 - recv = string.format("%s %s", recv_name, recv) 51 + local st = ts_utils.get_struct_under_cursor(bufnr) 52 + iface = args[2] 53 + recv = args[1] .. " *" .. st.name 54 + elseif #args == 3 then -- :GoImpl r Struct io.Reader 55 + recv = args[1] .. " *" .. args[2] 56 + iface = args[3] 83 57 end 84 58 85 59 local rs = r.sync { c.impl, "-dir", vim.fn.fnameescape(vim.fn.expand "%:p:h"), recv, iface }
+1 -1
lua/gopher/init.lua
··· 38 38 39 39 gopher.impl = require("gopher.impl").impl 40 40 gopher.iferr = require("gopher.iferr").iferr 41 - gopher.comment = require "gopher.comment" 41 + gopher.comment = require("gopher.comment").comment 42 42 43 43 gopher.tags = { 44 44 add = tags.add,
+5 -16
lua/gopher/struct_tags.lua
··· 33 33 34 34 local function modify(...) 35 35 local fpath = vim.fn.expand "%" ---@diagnostic disable-line: missing-parameter 36 - local ns = ts_utils.get_struct_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0))) 37 - if ns == nil then 38 - return 39 - end 40 - 41 - -- by struct name of line pos 42 - local cmd_args = {} 43 - if ns.name == nil then 44 - local _, csrow, _, _ = unpack(vim.fn.getpos ".") 45 - table.insert(cmd_args, "-line") 46 - table.insert(cmd_args, csrow) 47 - else 48 - table.insert(cmd_args, "-struct") 49 - table.insert(cmd_args, ns.name) 50 - end 36 + local bufnr = vim.api.nvim_get_current_buf() 37 + local struct = ts_utils.get_struct_under_cursor(bufnr) 51 38 52 39 -- set user args for cmd 40 + local cmd_args = {} 53 41 local arg = { ... } 54 42 for _, v in ipairs(arg) do 55 43 table.insert(cmd_args, v) ··· 61 49 c.gotag.transform, 62 50 "-format", 63 51 "json", 52 + "-struct", 53 + struct.name, 64 54 "-w", 65 55 "-file", 66 56 fpath, ··· 96 86 function struct_tags.add(...) 97 87 local user_tags = { ... } 98 88 if #user_tags == 0 then 99 - vim.print("c.gotag.default_tag", c.gotag.default_tag) 100 89 user_tags = { c.gotag.default_tag } 101 90 end 102 91
spec/fixtures/comment/empty_input.go

This is a binary file and will not be displayed.

+2
spec/fixtures/comment/empty_output.go
··· 1 + // 2 +
+5
spec/fixtures/comment/func_input.go
··· 1 + package main 2 + 3 + func Test(a int) bool { 4 + return false 5 + }
+6
spec/fixtures/comment/func_output.go
··· 1 + package main 2 + 3 + // Test 4 + func Test(a int) bool { 5 + return false 6 + }
+3
spec/fixtures/comment/interface_input.go
··· 1 + package main 2 + 3 + type Testinger interface{}
+4
spec/fixtures/comment/interface_output.go
··· 1 + package main 2 + 3 + // Testinger 4 + type Testinger interface{}
+7
spec/fixtures/comment/method_input.go
··· 1 + package main 2 + 3 + type Method struct{} 4 + 5 + func (Method) Run() error { 6 + return nil 7 + }
+8
spec/fixtures/comment/method_output.go
··· 1 + package main 2 + 3 + type Method struct{} 4 + 5 + // Run 6 + func (Method) Run() error { 7 + return nil 8 + }
+1 -1
spec/fixtures/comment/package_output.go
··· 1 - // Package main provides main 1 + // Package main provides 2 2 package main
+3
spec/fixtures/comment/struct_input.go
··· 1 + package main 2 + 3 + type CommentStruct struct{}
+4
spec/fixtures/comment/struct_output.go
··· 1 + package main 2 + 3 + // CommentStruct 4 + type CommentStruct struct{}
+1 -1
spec/fixtures/impl/reader_output.go
··· 1 1 package main 2 2 3 - func (r Read2) Read(p []byte) (n int, err error) { 3 + func (r *Read2) Read(p []byte) (n int, err error) { 4 4 panic("not implemented") // TODO: Implement 5 5 } 6 6
+18
spec/fixtures/tags/many_input.go
··· 1 + package main 2 + 3 + type ( 4 + TestOne struct { 5 + Asdf string 6 + ID int 7 + } 8 + 9 + TestTwo struct { 10 + Fesa int 11 + A bool 12 + } 13 + 14 + TestThree struct { 15 + Asufj int 16 + Fs string 17 + } 18 + )
+18
spec/fixtures/tags/many_output.go
··· 1 + package main 2 + 3 + type ( 4 + TestOne struct { 5 + Asdf string 6 + ID int 7 + } 8 + 9 + TestTwo struct { 10 + Fesa int `testing:"fesa"` 11 + A bool `testing:"a"` 12 + } 13 + 14 + TestThree struct { 15 + Asufj int 16 + Fs string 17 + } 18 + )
+35 -8
spec/integration/comment_test.lua
··· 5 5 hooks = { 6 6 post_once = child.stop, 7 7 pre_case = function() 8 - MiniTest.skip "This module should be fixed first" 9 8 child.restart { "-u", t.mininit_path } 10 9 end, 11 10 }, 12 11 } 12 + 13 + local function do_the_test(fixture, pos) 14 + local tmp = t.tmpfile() 15 + local fixtures = t.get_fixtures("comment/" .. fixture) 16 + t.writefile(tmp, fixtures.input) 17 + 18 + child.cmd("silent edit " .. tmp) 19 + child.fn.setpos(".", { child.fn.bufnr "%", unpack(pos) }) 20 + child.cmd "GoCmt" 21 + child.cmd "write" 22 + 23 + t.eq(t.readfile(tmp), fixtures.output) 24 + 25 + -- without it all other(not even from this module) tests are falling 26 + t.deletefile(tmp) 27 + end 28 + 13 29 T["comment"] = MiniTest.new_set {} 30 + T["comment"]["should add comment to package"] = function() 31 + do_the_test("package", { 1, 1 }) 32 + end 14 33 15 - T["comment"]["should add comment to package"] = function() end 34 + T["comment"]["should add comment to struct"] = function() 35 + do_the_test("struct", { 4, 1 }) 36 + end 16 37 17 - T["comment"]["should add comment to struct"] = function() end 38 + T["comment"]["should add comment to function"] = function() 39 + do_the_test("func", { 3, 1 }) 40 + end 18 41 19 - T["comment"]["should add comment to function"] = function() end 42 + T["comment"]["should add comment to method"] = function() 43 + do_the_test("method", { 5, 1 }) 44 + end 20 45 21 - T["comment"]["should add comment to method"] = function() end 22 - 23 - T["comment"]["should add comment to interface"] = function() end 46 + T["comment"]["should add comment to interface"] = function() 47 + do_the_test("interface", { 3, 6 }) 48 + end 24 49 25 - T["comment"]["otherwise should add // above cursor"] = function() end 50 + T["comment"]["otherwise should add // above cursor"] = function() 51 + do_the_test("empty", { 1, 1 }) 52 + end 26 53 27 54 return T
+1 -1
spec/integration/impl_test.lua
··· 16 16 t.writefile(tmp, fixtures.input) 17 17 18 18 child.cmd("silent edit " .. tmp) 19 - child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 6 }) 19 + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 0 }) 20 20 child.cmd "GoImpl w io.Writer" 21 21 child.cmd "write" 22 22
+12
spec/integration/struct_tags_test.lua
··· 34 34 t.eq(t.readfile(tmp), fixtures.output) 35 35 end 36 36 37 + T["struct_tags"]["works many structs"] = function() 38 + local tmp = t.tmpfile() 39 + local fixtures = t.get_fixtures "tags/many" 40 + t.writefile(tmp, fixtures.input) 41 + 42 + child.cmd("silent edit " .. tmp) 43 + child.fn.setpos(".", { child.fn.bufnr "%", 10, 3, 0 }) 44 + child.cmd "GoTagAdd testing" 45 + 46 + t.eq(t.readfile(tmp), fixtures.output) 47 + end 48 + 37 49 return T
+5
spec/testutils.lua
··· 31 31 vim.fn.writefile(vim.split(contents, "\n"), fpath) 32 32 end 33 33 34 + ---@param fpath string 35 + function testutils.deletefile(fpath) 36 + vim.fn.delete(fpath) 37 + end 38 + 34 39 ---@param fixture string 35 40 ---@return {input: string, output: string} 36 41 function testutils.get_fixtures(fixture)