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

Compare changes

Choose any two refs to compare.

+1
.github/workflows/tests.yml
··· 21 21 - v0.11.1 22 22 - v0.11.2 23 23 - v0.11.3 24 + - v0.11.4 24 25 runs-on: ${{ matrix.os }} 25 26 steps: 26 27 - name: Install Task
+69 -6
README.md
··· 8 8 9 9 > All development of new and maybe undocumented, and unstable features is happening on [develop](https://github.com/olexsmir/gopher.nvim/tree/develop) branch. 10 10 11 + ## Table of content 12 + * [How to install](#install-using-lazynvim) 13 + * [Features](#features) 14 + * [Configuration](#configuration) 15 + * [Troubleshooting](#troubleshooting) 16 + * [Contributing](#contributing) 17 + 11 18 ## Install (using [lazy.nvim](https://github.com/folke/lazy.nvim)) 12 19 13 20 Requirements: ··· 16 23 - Treesitter parser for `go`(`:TSInstall go` if you use [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter)) 17 24 - [Go](https://github.com/golang/go) installed 18 25 26 + > [!IMPORTANT] 27 + > If you prefer using other forges, this repository is also mirrored at: 28 + > - [tangled.org](https://tangled.org): [`https://tangled.org/olexsmir.xyz/gopher.nvim`](https://tangled.org/olexsmir.xyz/gopher.nvim) 29 + > - [codeberg.org](https://codeberg.org): [`https://codeberg.org/olexsmir/gopher.nvim`](https://codeberg.org/olexsmir/gopher.nvim) 30 + 19 31 ```lua 20 - -- NOTE: this plugin is already lazy-loaded, it adds only about 1ms of load 21 - -- time to your config 32 + -- NOTE: this plugin is already lazy-loaded and adds only about 1ms 33 + -- of load time to your config 22 34 { 23 35 "olexsmir/gopher.nvim", 24 36 ft = "go", 25 37 -- branch = "develop" 26 - -- (optional) will update plugin's deps on every update 38 + -- (optional) updates the plugin's dependencies on each update 27 39 build = function() 28 40 vim.cmd.GoInstallDeps() 29 41 end, ··· 50 62 - [impl](https://github.com/josharian/impl) 51 63 - [gotests](https://github.com/cweill/gotests) 52 64 - [iferr](https://github.com/koron/iferr) 65 + - [json2go](https://github.com/olexsmir/json2go) 53 66 </details> 54 67 55 68 <details> ··· 64 77 ```vim 65 78 " add json tag 66 79 :GoTagAdd json 80 + 81 + " add json tag with omitempty option 82 + :GoTagAdd json=omitempty 67 83 68 84 " remove yaml tag 69 85 :GoTagRm yaml ··· 170 186 ``` 171 187 </details> 172 188 189 + <details> 190 + <summary> 191 + <b>Convert json to Go types</b> 192 + </summary> 193 + 194 + ![Convert JSON to Go types](./vhs/json2go.gif) 195 + 196 + `:GoJson` opens a temporary buffer where you can paste or write JSON. 197 + Saving the buffer (`:w` or `:wq`) automatically closes it and inserts the generated Go struct into the original buffer at the cursor position. 198 + 199 + Alternatively, you can pass JSON directly as an argument: 200 + ```vim 201 + :GoJson {"name": "Alice", "age": 30} 202 + ``` 203 + 204 + Additionally, `gopher.json2go` provides lua api, see `:h gopher.nvim-json2go` for details. 205 + </details> 206 + 173 207 174 208 <details> 175 209 <summary> ··· 201 235 -- timeout for running internal commands 202 236 timeout = 2000, 203 237 238 + -- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync) 239 + installer_timeout = 999999, 240 + 241 + -- user specified paths to binaries 204 242 commands = { 205 243 go = "go", 206 244 gomodifytags = "gomodifytags", ··· 209 247 iferr = "iferr", 210 248 }, 211 249 gotests = { 212 - -- gotests doesn't have template named "default" so this plugin uses "default" to set the default template 250 + -- a default template that gotess will use. 251 + -- gotets doesn't have template named `default`, we use it to represent absence of the provided template. 213 252 template = "default", 253 + 214 254 -- path to a directory containing custom test code templates 215 255 template_dir = nil, 216 - -- switch table tests from using slice to map (with test name for the key) 256 + 257 + -- use named tests(map with test name as key) in table tests(slice of structs by default) 217 258 named = false, 218 259 }, 219 260 gotag = { 220 261 transform = "snakecase", 262 + 221 263 -- default tags to add to struct fields 222 264 default_tag = "json", 265 + 266 + -- default tag option added struct fields, set to nil to disable 267 + -- e.g: `option = "json=omitempty,xml=omitempty` 268 + option = nil, 223 269 }, 224 270 iferr = { 225 - -- choose a custom error message 271 + -- choose a custom error message, nil to use default 272 + -- e.g: `message = 'fmt.Errorf("failed to %w", err)'` 226 273 message = nil, 227 274 }, 275 + json2go = { 276 + -- command used to open interactive input. 277 + -- e.g: `split`, `botright split`, `tabnew` 278 + interactive_cmd = "vsplit", 279 + 280 + -- name of autogenerated struct 281 + -- e.g: "MySuperCoolName" 282 + type_name = nil, 283 + }, 228 284 } 229 285 ``` 286 + 287 + ## Troubleshooting 288 + The most common issue with the plugin is missing dependencies. 289 + Run `:checkhealth gopher` to verify that the plugin is installed correctly. 290 + If any binaries are missing, install them using `:GoInstallDeps`. 291 + 292 + If the issue persists, feel free to [open a new issue](https://github.com/olexsmir/gopher.nvim/issues/new). 230 293 231 294 ## Contributing 232 295
+2
Taskfile.yml
··· 21 21 -u ./scripts/minimal_init.lua \ 22 22 -c "lua MiniTest.run()" \ 23 23 -c ":qa!" 24 + nvim: 25 + cmd: nvim --clean -u "./scripts/minimal_init.lua" {{ .CLI_ARGS }} 24 26 25 27 docgen: 26 28 desc: generate vimhelp
+102 -31
doc/gopher.nvim.txt
··· 13 13 Config ................................................ |gopher.nvim-config| 14 14 Commands ............................................ |gopher.nvim-commands| 15 15 Modify struct tags ............................... |gopher.nvim-struct-tags| 16 + json2go .............................................. |gopher.nvim-json2go| 16 17 Auto implementation of interface methods ................ |gopher.nvim-impl| 17 18 Generating unit tests boilerplate .................... |gopher.nvim-gotests| 18 19 Iferr .................................................. |gopher.nvim-iferr| ··· 36 37 ------------------------------------------------------------------------------ 37 38 *gopher.nvim-dependencies* 38 39 `gopher.install_deps` 39 - Gopher.nvim implements most of its features using third-party tools. 40 - To install these tools, you can run `:GoInstallDeps` command 41 - or call `require("gopher").install_deps()` if you want to use lua api. 42 - By default dependencies will be installed asynchronously, 43 - to install them synchronously pass `{sync = true}` as an argument. 40 + 41 + Gopher.nvim implements most of its features using third-party tools. To 42 + install plugin's dependencies, you can run: 43 + `:GoInstallDeps` or `:GoInstallDepsSync` 44 + or use `require("gopher").install_deps()` if you prefer lua api. 44 45 45 46 46 47 ============================================================================== ··· 57 58 ---@type number 58 59 timeout = 2000, 59 60 60 - --- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync) 61 + -- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync) 62 + ---@type number 61 63 installer_timeout = 999999, 62 64 63 65 -- user specified paths to binaries ··· 68 70 gotests = "gotests", 69 71 impl = "impl", 70 72 iferr = "iferr", 73 + json2go = "json2go", 71 74 }, 72 75 ---@class gopher.ConfigGotests 73 76 gotests = { 74 - -- gotests doesn't have template named "default" so this plugin uses "default" to set the default template 77 + -- a default template that gotess will use. 78 + -- gotets doesn't have template named `default`, we use it to represent absence of the provided template. 75 79 template = "default", 80 + 76 81 -- path to a directory containing custom test code templates 77 82 ---@type string|nil 78 83 template_dir = nil, 79 - -- switch table tests from using slice to map (with test name for the key) 84 + 85 + -- use named tests(map with test name as key) in table tests(slice of structs by default) 80 86 named = false, 81 87 }, 82 88 ---@class gopher.ConfigGoTag ··· 86 92 87 93 -- default tags to add to struct fields 88 94 default_tag = "json", 95 + 96 + -- default tag option added struct fields, set to nil to disable 97 + -- e.g: `option = "json=omitempty,xml=omitempty` 98 + ---@type string|nil 99 + option = nil, 89 100 }, 101 + ---@class gopher.ConfigIfErr 90 102 iferr = { 91 - -- choose a custom error message 103 + -- choose a custom error message, nil to use default 104 + -- e.g: `message = 'fmt.Errorf("failed to %w", err)'` 92 105 ---@type string|nil 93 106 message = nil, 94 107 }, 108 + ---@class gopher.ConfigJson2Go 109 + json2go = { 110 + -- command used to open interactive input. 111 + -- e.g: `split`, `botright split`, `tabnew` 112 + interactive_cmd = "vsplit", 113 + 114 + -- name of autogenerated struct, if nil none, will the default one of json2go. 115 + -- e.g: "MySuperCoolName" 116 + ---@type string|nil 117 + type_name = nil, 118 + }, 95 119 } 96 120 < 97 121 Class ~ 98 122 {gopher.Config} 123 + Fields ~ 124 + {setup} `(fun(user_config?: gopher.Config))` 99 125 100 126 101 127 ============================================================================== ··· 110 136 ------------------------------------------------------------------------------ 111 137 *gopher.nvim-struct-tags* 112 138 113 - `struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to struct fields. 139 + `struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to 140 + struct fields. 114 141 115 142 Usage ~ 116 143 117 - How to add/remove tags to struct fields: 144 + How to add/remove/clear tags to struct fields: 118 145 1. Place cursor on the struct 119 146 2. Run `:GoTagAdd json` to add json tags to struct fields 120 147 3. Run `:GoTagRm json` to remove json tags to struct fields 148 + 4. Run `:GoTagClear` to clear all tags from struct fields 121 149 122 - To clear all tags from struct run: `:GoTagClear` 150 + If you want to add/remove tag with options, you can use `json=omitempty` 151 + (where json is tag, and omitempty is its option). 152 + Example: `:GoTagAdd xml json=omitempty` 153 + 123 154 124 155 NOTE: if you dont specify the tag it will use `json` as default 125 156 ··· 142 173 143 174 ============================================================================== 144 175 ------------------------------------------------------------------------------ 176 + *gopher.nvim-json2go* 177 + 178 + Convert json to go type annotations. 179 + 180 + Usage ~ 181 + 182 + `:GoJson` opens a temporary buffer where you can paste or write JSON. 183 + Saving the buffer (`:w` or `:wq`) automatically closes it and inserts the 184 + generated Go struct into the original buffer at the cursor position. 185 + 186 + Alternatively, you can pass JSON directly as an argument: 187 + >vim 188 + :GoJson {"name": "Alice", "age": 30} 189 + < 190 + ------------------------------------------------------------------------------ 191 + *json2go.transform()* 192 + `json2go.transform`({json_str}) 193 + 194 + Parameters ~ 195 + {json_str} `(string)` Json string that is going to be converted to go type. 196 + Return ~ 197 + `(string)` `(optional)` Go type, or nil if failed. 198 + 199 + ------------------------------------------------------------------------------ 200 + *json2go.json2go()* 201 + `json2go.json2go`({json_str}) 202 + Converts json string to go type, and puts result under the cursor. If 203 + [json_str] is nil, will open an interactive prompt (with cmd set in 204 + config). 205 + 206 + Parameters ~ 207 + {json_str} `(optional)` `(string)` 208 + 209 + 210 + ============================================================================== 211 + ------------------------------------------------------------------------------ 145 212 *gopher.nvim-impl* 146 213 147 214 Integration of `impl` tool to generate method stubs for interfaces. 148 215 149 216 Usage ~ 217 + 150 218 1. Automatically implement an interface for a struct: 151 - - Place your cursor on the struct where you want to implement the interface. 152 - - Run `:GoImpl io.Reader` 153 - - This will automatically determine the receiver and implement the `io.Reader` interface. 219 + - Place your cursor on the struct where you want to implement the interface. 220 + - Run `:GoImpl io.Reader` 221 + - This will automatically determine the receiver and implement the `io.Reader` interface. 154 222 155 223 2. Specify a custom receiver: 156 - - Place your cursor on the struct 157 - - Run `:GoImpl w io.Writer`, where: 158 - - `w` is the receiver. 159 - - `io.Writer` is the interface to implement. 224 + - Place your cursor on the struct 225 + - Run `:GoImpl w io.Writer`, where: 226 + - `w` is the receiver. 227 + - `io.Writer` is the interface to implement. 160 228 161 229 3. Explicitly specify the receiver, struct, and interface: 162 - - No need to place the cursor on the struct if all arguments are provided. 163 - - Run `:GoImpl r RequestReader io.Reader`, where: 164 - - `r` is the receiver. 165 - - `RequestReader` is the struct. 166 - - `io.Reader` is the interface to implement. 230 + - No need to place the cursor on the struct if all arguments are provided. 231 + - Run `:GoImpl r RequestReader io.Reader`, where: 232 + - `r` is the receiver. 233 + - `RequestReader` is the struct. 234 + - `io.Reader` is the interface to implement. 167 235 168 236 Example: 169 237 >go ··· 184 252 Usage ~ 185 253 186 254 - Generate unit test for specific function/method: 187 - 1. Place your cursor on the desired function/method. 188 - 2. Run `:GoTestAdd` 255 + 1. Place your cursor on the desired function/method. 256 + 2. Run `:GoTestAdd` 189 257 190 258 - Generate unit tests for *all* functions/methods in current file: 191 - - run `:GoTestsAll` 259 + - run `:GoTestsAll` 192 260 193 261 - Generate unit tests *only* for *exported(public)* functions/methods: 194 - - run `:GoTestsExp` 262 + - run `:GoTestsExp` 195 263 196 - You can also specify the template to use for generating the tests. See |gopher.nvim-config| 197 - More details about templates can be found at: https://github.com/cweill/gotests 264 + You can also specify the template to use for generating the tests. 265 + See |gopher.nvim-config|. 266 + More details about templates: https://github.com/cweill/gotests 198 267 199 268 If you prefer named tests, you can enable them in |gopher.nvim-config|. 200 269 ··· 217 286 This module provides a way to generate comments for Go code. 218 287 219 288 Usage ~ 220 - Set cursor on line with function/method/struct/etc and run `:GoCmt` to generate a comment. 289 + 290 + Set cursor on line with function/method/struct/etc and 291 + run `:GoCmt` to generate a comment. 221 292 222 293 223 294 vim:tw=78:ts=8:noet:ft=help:norl:
-51
lua/gopher/_utils/gocmd.lua
··· 1 - local r = require "gopher._utils.runner" 2 - local c = require("gopher.config").commands 3 - local u = require "gopher._utils" 4 - local gocmd = {} 5 - 6 - ---@param args string[] 7 - ---@return string[] 8 - local function if_get(args) 9 - for i, arg in ipairs(args) do 10 - local m = string.match(arg, "^https://(.*)$") or string.match(arg, "^http://(.*)$") or arg 11 - table.remove(args, i) 12 - table.insert(args, i, m) 13 - end 14 - return args 15 - end 16 - 17 - ---@param args unknown[] 18 - ---@return string[] 19 - local function if_generate(args) 20 - if #args == 1 and args[1] == "%" then 21 - args[1] = vim.fn.expand "%" 22 - end 23 - return args 24 - end 25 - 26 - ---@param subcmd string 27 - ---@param args string[] 28 - ---@return string 29 - function gocmd.run(subcmd, args) 30 - if #args == 0 and subcmd ~= "generate" then 31 - error "please provide any arguments" 32 - end 33 - 34 - if subcmd == "get" then 35 - args = if_get(args) 36 - end 37 - 38 - if subcmd == "generate" then 39 - args = if_generate(args) 40 - end 41 - 42 - local rs = r.sync { c.go, subcmd, unpack(args) } 43 - if rs.code ~= 0 then 44 - error("go " .. subcmd .. " failed: " .. rs.stderr) 45 - end 46 - 47 - u.notify(c.go .. " " .. subcmd .. " ran successful") 48 - return rs.stdout 49 - end 50 - 51 - return gocmd
+32 -1
lua/gopher/_utils/init.lua
··· 3 3 local utils = {} 4 4 5 5 ---@param msg string 6 - ---@param lvl? number by default `vim.log.levels.INFO` 6 + ---@param lvl? integer by default `vim.log.levels.INFO` 7 7 function utils.notify(msg, lvl) 8 8 lvl = lvl or vim.log.levels.INFO 9 9 vim.notify(msg, lvl, { ··· 36 36 function utils.trimend(s) 37 37 local r, _ = string.gsub(s, "%s+$", "") 38 38 return r 39 + end 40 + 41 + -- Since indentation can be spaces or tabs, that's my hack around it 42 + ---@param line string 43 + ---@param indent integer 44 + ---@return string 45 + function utils.indent(line, indent) 46 + local char = string.sub(line, 1, 1) 47 + if char ~= " " and char ~= "\t" then 48 + char = " " 49 + end 50 + return string.rep(char, indent) 51 + end 52 + 53 + ---@generic T 54 + ---@param tbl T[] 55 + ---@return T[] 56 + function utils.list_unique(tbl) 57 + if vim.fn.has "nvim-0.12" == 1 then 58 + return vim.list.unique(tbl) 59 + end 60 + 61 + for i = #tbl, 1, -1 do 62 + for j = 1, i - 1 do 63 + if tbl[i] == tbl[j] then 64 + table.remove(tbl, i) 65 + break 66 + end 67 + end 68 + end 69 + return tbl 39 70 end 40 71 41 72 return utils
+2 -2
lua/gopher/_utils/log.lua
··· 3 3 -- for the code i have stolen(or have inspected by idk) 4 4 local c = require "gopher.config" 5 5 6 - ---@class Gopher.Logger 6 + ---@class gopher.Logger 7 7 ---@field get_outfile fun():string 8 8 ---@field trace fun(...) 9 9 ---@field fmt_trace fun(...) ··· 44 44 float_precision = 0.01, 45 45 } 46 46 47 - ---@type Gopher.Logger 47 + ---@type gopher.Logger 48 48 ---@diagnostic disable-next-line: missing-fields 49 49 local log = {} 50 50
+40 -16
lua/gopher/_utils/ts.lua
··· 11 11 right: (expression_list (composite_literal 12 12 type: (struct_type))))] 13 13 ]], 14 + struct_field = [[ 15 + (field_declaration name: (field_identifier) @_name) 16 + ]], 14 17 func = [[ 15 18 [(function_declaration name: (identifier) @_name) 16 - (method_declaration name: (field_identifier) @_name)] 19 + (method_declaration name: (field_identifier) @_name) 20 + (method_elem name: (field_identifier) @_name)] 17 21 ]], 18 22 package = [[ 19 23 (package_identifier) @_name ··· 23 27 name: (type_identifier) @_name 24 28 type: (interface_type)) 25 29 ]], 30 + var = [[ 31 + [(var_declaration (var_spec name: (identifier) @_name)) 32 + (short_var_declaration 33 + left: (expression_list (identifier) @_name @_var))] 34 + ]], 26 35 } 27 36 28 37 ---@param parent_type string[] 29 38 ---@param node TSNode 30 39 ---@return TSNode? 31 - local function get_parrent_node(parent_type, node) 40 + local function get_parent_node(parent_type, node) 32 41 ---@type TSNode? 33 42 local current = node 34 43 while current do ··· 64 73 end 65 74 66 75 ---@class gopher.TsResult 67 - ---@field name string 68 - ---@field start integer 69 - ---@field end_ integer 70 - ---@field is_varstruct boolean 76 + ---@field name string Name of the struct, function, etc 77 + ---@field start integer Line number where the declaration starts 78 + ---@field end_ integer Line number where the declaration ends 79 + ---@field indent integer Number of spaces/tabs in the current cursor line 80 + ---@field is_varstruct boolean Is struct declared as `var S struct{}` or `s := struct{}{}` 71 81 72 82 ---@param bufnr integer 73 83 ---@param parent_type string[] ··· 78 88 error "No treesitter parser found for go" 79 89 end 80 90 81 - local node = vim.treesitter.get_node { 82 - bufnr = bufnr, 83 - } 91 + local node = vim.treesitter.get_node { bufnr = bufnr } 84 92 if not node then 85 - error "No nodes found under cursor" 93 + error "No nodes found under the cursor" 86 94 end 87 95 88 - local parent_node = get_parrent_node(parent_type, node) 96 + local parent_node = get_parent_node(parent_type, node) 89 97 if not parent_node then 90 - error "No parent node found under cursor" 98 + error "No parent node found under the cursor" 91 99 end 92 100 93 101 local q = vim.treesitter.query.parse("go", query) 94 102 local res = get_captures(q, parent_node, bufnr) 95 103 assert(res.name ~= nil, "No capture name found") 96 104 97 - local start_row, _, end_row, _ = parent_node:range() 105 + local start_row, start_col, end_row, _ = parent_node:range() 106 + res["indent"] = start_col 98 107 res["start"] = start_row + 1 99 108 res["end_"] = end_row + 1 100 109 ··· 104 113 ---@param bufnr integer 105 114 function ts.get_struct_under_cursor(bufnr) 106 115 --- should be both type_spec and type_declaration 107 - --- because in cases like `type ( T struct{}, U strict{} )` 108 - --- i will be choosing always last struct in the list 116 + --- because in cases like `type ( T struct{}, U struct{} )` 109 117 --- 110 118 --- var_declaration is for cases like `var x struct{}` 111 119 --- short_var_declaration is for cases like `x := struct{}{}` 120 + --- 121 + --- it always chooses last struct type in the list 112 122 return do_stuff(bufnr, { 113 123 "type_spec", 114 124 "type_declaration", ··· 118 128 end 119 129 120 130 ---@param bufnr integer 131 + function ts.get_struct_field_under_cursor(bufnr) 132 + return do_stuff(bufnr, { "field_declaration" }, queries.struct_field) 133 + end 134 + 135 + ---@param bufnr integer 121 136 function ts.get_func_under_cursor(bufnr) 122 137 --- since this handles both and funcs and methods we should check for both parent nodes 123 - return do_stuff(bufnr, { "function_declaration", "method_declaration" }, queries.func) 138 + return do_stuff(bufnr, { 139 + "method_elem", 140 + "function_declaration", 141 + "method_declaration", 142 + }, queries.func) 124 143 end 125 144 126 145 ---@param bufnr integer ··· 131 150 ---@param bufnr integer 132 151 function ts.get_interface_under_cursor(bufnr) 133 152 return do_stuff(bufnr, { "type_declaration" }, queries.interface) 153 + end 154 + 155 + ---@param bufnr integer 156 + function ts.get_variable_under_cursor(bufnr) 157 + return do_stuff(bufnr, { "var_declaration", "short_var_declaration" }, queries.var) 134 158 end 135 159 136 160 return ts
+31 -17
lua/gopher/comment.lua
··· 3 3 ---@text 4 4 --- This module provides a way to generate comments for Go code. 5 5 --- 6 - ---@usage Set cursor on line with function/method/struct/etc and run `:GoCmt` to generate a comment. 6 + ---@usage 7 + --- Set cursor on line with function/method/struct/etc and 8 + --- run `:GoCmt` to generate a comment. 7 9 8 10 local ts = require "gopher._utils.ts" 9 11 local log = require "gopher._utils.log" 12 + local u = require "gopher._utils" 10 13 local comment = {} 11 14 12 - ---@param name string 13 - ---@return string 14 - ---@dochide 15 - local function template(name) 16 - return "// " .. name .. " " 17 - end 18 - 15 + --- NOTE: The order of functions executed inside this function is IMPORTANT. 16 + --- This function is extremely fragile; run tests after making any changes. 17 + --- 19 18 ---@param bufnr integer 19 + ---@param line string 20 20 ---@return string 21 21 ---@dochide 22 - local function generate(bufnr) 22 + local function generate(bufnr, line) 23 + local sf_ok, sf_res = pcall(ts.get_struct_field_under_cursor, bufnr) 24 + if sf_ok then 25 + return u.indent(line, sf_res.indent) .. "// " .. sf_res.name .. " " 26 + end 27 + 23 28 local s_ok, s_res = pcall(ts.get_struct_under_cursor, bufnr) 24 29 if s_ok then 25 - return template(s_res.name) 30 + return u.indent(line, s_res.indent) .. "// " .. s_res.name .. " " 31 + end 32 + 33 + local v_ok, v_res = pcall(ts.get_variable_under_cursor, bufnr) 34 + if v_ok then 35 + return u.indent(line, v_res.indent) .. "// " .. v_res.name .. " " 26 36 end 27 37 28 38 local f_ok, f_res = pcall(ts.get_func_under_cursor, bufnr) 29 39 if f_ok then 30 - return template(f_res.name) 40 + return u.indent(line, f_res.indent) .. "// " .. f_res.name .. " " 31 41 end 32 42 33 43 local i_ok, i_res = pcall(ts.get_interface_under_cursor, bufnr) 34 44 if i_ok then 35 - return template(i_res.name) 45 + return u.indent(line, i_res.indent) .. "// " .. i_res.name .. " " 36 46 end 37 47 38 48 local p_ok, p_res = pcall(ts.get_package_under_cursor, bufnr) ··· 45 55 46 56 function comment.comment() 47 57 local bufnr = vim.api.nvim_get_current_buf() 48 - local cmt = generate(bufnr) 49 - log.debug("generated comment: " .. cmt) 58 + local lnum = vim.fn.getcurpos()[2] 59 + local line = vim.fn.getline(lnum) 60 + local cmt = generate(bufnr, line) 61 + log.debug("generated comment:", { 62 + comment = cmt, 63 + line = line, 64 + }) 50 65 51 - local pos = vim.fn.getcurpos()[2] 52 - vim.fn.append(pos - 1, cmt) 53 - vim.fn.setpos(".", { 0, pos, #cmt }) 66 + vim.fn.append(lnum - 1, cmt) 67 + vim.fn.setpos(".", { bufnr, lnum, #cmt }) 54 68 vim.cmd "startinsert!" 55 69 end 56 70
+38 -8
lua/gopher/config.lua
··· 1 + ---@type gopher.Config 2 + ---@dochide 3 + ---@diagnostic disable-next-line: missing-fields .setup() gets injected later 1 4 local config = {} 2 5 3 6 ---@tag gopher.nvim-config.ConfigGoTagTransform ··· 16 19 ---@tag gopher.nvim-config 17 20 ---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section) 18 21 ---@class gopher.Config 22 + ---@field setup fun(user_config?: gopher.Config) 19 23 local default_config = { 20 24 -- log level, you might consider using DEBUG or TRACE for debugging the plugin 21 25 ---@type number ··· 25 29 ---@type number 26 30 timeout = 2000, 27 31 28 - --- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync) 32 + -- timeout for running installer commands(e.g :GoDepsInstall, :GoDepsInstallSync) 33 + ---@type number 29 34 installer_timeout = 999999, 30 35 31 36 -- user specified paths to binaries ··· 36 41 gotests = "gotests", 37 42 impl = "impl", 38 43 iferr = "iferr", 44 + json2go = "json2go", 39 45 }, 40 46 ---@class gopher.ConfigGotests 41 47 gotests = { 42 - -- gotests doesn't have template named "default" so this plugin uses "default" to set the default template 48 + -- a default template that gotess will use. 49 + -- gotets doesn't have template named `default`, we use it to represent absence of the provided template. 43 50 template = "default", 51 + 44 52 -- path to a directory containing custom test code templates 45 53 ---@type string|nil 46 54 template_dir = nil, 47 - -- switch table tests from using slice to map (with test name for the key) 55 + 56 + -- use named tests(map with test name as key) in table tests(slice of structs by default) 48 57 named = false, 49 58 }, 50 59 ---@class gopher.ConfigGoTag ··· 54 63 55 64 -- default tags to add to struct fields 56 65 default_tag = "json", 66 + 67 + -- default tag option added struct fields, set to nil to disable 68 + -- e.g: `option = "json=omitempty,xml=omitempty` 69 + ---@type string|nil 70 + option = nil, 57 71 }, 72 + ---@class gopher.ConfigIfErr 58 73 iferr = { 59 - -- choose a custom error message 74 + -- choose a custom error message, nil to use default 75 + -- e.g: `message = 'fmt.Errorf("failed to %w", err)'` 60 76 ---@type string|nil 61 77 message = nil, 78 + }, 79 + ---@class gopher.ConfigJson2Go 80 + json2go = { 81 + -- command used to open interactive input. 82 + -- e.g: `split`, `botright split`, `tabnew` 83 + interactive_cmd = "vsplit", 84 + 85 + -- name of autogenerated struct, if nil none, will the default one of json2go. 86 + -- e.g: "MySuperCoolName" 87 + ---@type string|nil 88 + type_name = nil, 62 89 }, 63 90 } 64 91 --minidoc_afterlines_end ··· 90 117 vim.validate("commands.gotests", _config.commands.gotests, "string") 91 118 vim.validate("commands.impl", _config.commands.impl, "string") 92 119 vim.validate("commands.iferr", _config.commands.iferr, "string") 120 + vim.validate("commands.json2go", _config.commands.json2go, "string") 93 121 vim.validate("gotests", _config.gotests, "table") 94 122 vim.validate("gotests.template", _config.gotests.template, "string") 95 - vim.validate("gotests.template_dir", _config.gotests.template_dir, "string", true) 123 + vim.validate("gotests.template_dir", _config.gotests.template_dir, { "string", "nil" }) 96 124 vim.validate("gotests.named", _config.gotests.named, "boolean") 97 125 vim.validate("gotag", _config.gotag, "table") 98 126 vim.validate("gotag.transform", _config.gotag.transform, "string") 99 127 vim.validate("gotag.default_tag", _config.gotag.default_tag, "string") 128 + vim.validate("gotag.option", _config.gotag.option, { "string", "nil" }) 100 129 vim.validate("iferr", _config.iferr, "table") 101 - vim.validate("iferr.message", _config.iferr.message, "string", true) 130 + vim.validate("iferr.message", _config.iferr.message, { "string", "nil" }) 131 + vim.validate("json2go.installer_timeout", _config.json2go.interactive_cmd, "string") 132 + vim.validate("json2go.type_name", _config.json2go.type_name, { "string", "nil" }) 102 133 end 103 134 104 135 setmetatable(config, { ··· 108 139 }) 109 140 110 141 ---@dochide 111 - ---@return gopher.Config 112 - return config --[[ @as gopher.Config ]] 142 + return config
+56
lua/gopher/go.lua
··· 1 + local c = require "gopher.config" 2 + local u = require "gopher._utils" 3 + local r = require "gopher._utils.runner" 4 + local go = {} 5 + 6 + local function run(subcmd, args) 7 + local rs = r.sync { c.commands.go, subcmd, unpack(args) } 8 + if rs.code ~= 0 then 9 + error("go " .. subcmd .. " failed: " .. rs.stderr) 10 + end 11 + 12 + u.notify(c.commands.go .. " " .. subcmd .. " ran successful") 13 + return rs.stdout 14 + end 15 + 16 + ---@param args string[] 17 + function go.get(args) 18 + for i, arg in ipairs(args) do 19 + local m = string.match(arg, "^https://(.*)$") or string.match(arg, "^http://(.*)$") or arg 20 + table.remove(args, i) 21 + table.insert(args, i, m) 22 + end 23 + 24 + run("get", args) 25 + end 26 + 27 + ---@param args string[] 28 + function go.mod(args) 29 + run("mod", args) 30 + end 31 + 32 + ---@param args string[] 33 + function go.work(args) 34 + -- TODO: use `gopls.tidy` 35 + 36 + run("work", args) 37 + end 38 + 39 + ---Executes `go generate` 40 + ---If only argument is `%` it's going to be equivalent to `go generate <path to current file>` 41 + ---@param args string[] 42 + function go.generate(args) 43 + -- TODO: use `gopls.generate` 44 + 45 + if #args == 0 then 46 + error "please provide arguments" 47 + end 48 + 49 + if #args == 1 and args[1] == "%" then 50 + args[1] = vim.fn.expand "%" 51 + end 52 + 53 + run("generate", args) 54 + end 55 + 56 + return go
+7 -6
lua/gopher/gotests.lua
··· 3 3 ---@text gotests is utilizing the `gotests` tool to generate unit tests boilerplate. 4 4 ---@usage 5 5 --- - Generate unit test for specific function/method: 6 - --- 1. Place your cursor on the desired function/method. 7 - --- 2. Run `:GoTestAdd` 6 + --- 1. Place your cursor on the desired function/method. 7 + --- 2. Run `:GoTestAdd` 8 8 --- 9 9 --- - Generate unit tests for *all* functions/methods in current file: 10 - --- - run `:GoTestsAll` 10 + --- - run `:GoTestsAll` 11 11 --- 12 12 --- - Generate unit tests *only* for *exported(public)* functions/methods: 13 - --- - run `:GoTestsExp` 13 + --- - run `:GoTestsExp` 14 14 --- 15 - --- You can also specify the template to use for generating the tests. See |gopher.nvim-config| 16 - --- More details about templates can be found at: https://github.com/cweill/gotests 15 + --- You can also specify the template to use for generating the tests. 16 + --- See |gopher.nvim-config|. 17 + --- More details about templates: https://github.com/cweill/gotests 17 18 --- 18 19 --- If you prefer named tests, you can enable them in |gopher.nvim-config|. 19 20
+33 -37
lua/gopher/health.lua
··· 1 + local c = require("gopher.config").commands 1 2 local health = {} 2 - local cmd = require("gopher.config").commands 3 3 4 4 local deps = { 5 + vim_version = "nvim-0.10", 5 6 bin = { 6 7 { 7 - bin = cmd.go, 8 + bin = c.go, 8 9 msg = "required for `:GoGet`, `:GoMod`, `:GoGenerate`, `:GoWork`, `:GoInstallDeps`, `:GoInstallDepsSync`", 9 - optional = false, 10 10 }, 11 - { bin = cmd.gomodifytags, msg = "required for `:GoTagAdd`, `:GoTagRm`", optional = true }, 12 - { bin = cmd.impl, msg = "required for `:GoImpl`", optional = true }, 13 - { bin = cmd.iferr, msg = "required for `:GoIfErr`", optional = true }, 14 - { 15 - bin = cmd.gotests, 16 - msg = "required for `:GoTestAdd`, `:GoTestsAll`, `:GoTestsExp`", 17 - optional = true, 18 - }, 11 + { bin = c.gomodifytags, msg = "required for `:GoTagAdd`, `:GoTagRm`" }, 12 + { bin = c.impl, msg = "required for `:GoImpl`" }, 13 + { bin = c.iferr, msg = "required for `:GoIfErr`" }, 14 + { bin = c.gotests, msg = "required for `:GoTestAdd`, `:GoTestsAll`, `:GoTestsExp`" }, 19 15 }, 20 16 treesitter = { 21 17 { parser = "go", msg = "required for most of the parts of `gopher.nvim`" }, 22 18 }, 23 19 } 24 20 25 - ---@param bin string 26 - ---@return boolean 27 - local function is_binary_found(bin) 28 - return vim.fn.executable(bin) == 1 21 + ---@param bin {bin:string, msg:string, optional:boolean} 22 + local function check_binary(bin) 23 + if vim.fn.executable(bin.bin) == 1 then 24 + vim.health.ok(bin.bin .. " is found oh PATH: `" .. vim.fn.exepath(bin.bin) .. "`") 25 + else 26 + vim.health.error(bin.bin .. " not found on PATH, " .. bin.msg) 27 + end 29 28 end 30 29 31 - ---@param ft string 32 - ---@return boolean 33 - local function is_treesitter_parser_available(ft) 34 - local ok, parser = pcall(vim.treesitter.get_parser, 0, ft) 35 - return ok and parser ~= nil 30 + ---@param ts {parser:string, msg:string} 31 + local function check_treesitter(ts) 32 + local ok, parser = pcall(vim.treesitter.get_parser, 0, ts.parser) 33 + if ok and parser ~= nil then 34 + vim.health.ok("`" .. ts.parser .. "` parser is installed") 35 + else 36 + vim.health.error("`" .. ts.parser .. "` parser not found") 37 + end 36 38 end 37 39 38 40 function health.check() 39 - vim.health.start "required binaries" 40 - vim.health.info "all those binaries can be installed by `:GoInstallDeps`" 41 + vim.health.start "Neovim version" 42 + if vim.fn.has(deps.vim_version) == 1 then 43 + vim.health.ok "Neovim version is compatible" 44 + else 45 + vim.health.error(deps.vim_version .. " or newer is required") 46 + end 47 + 48 + vim.health.start "Required binaries (those can be installed with `:GoInstallDeps`)" 41 49 for _, bin in ipairs(deps.bin) do 42 - if is_binary_found(bin.bin) then 43 - vim.health.ok(bin.bin .. " installed") 44 - else 45 - if bin.optional then 46 - vim.health.warn(bin.bin .. " not found, " .. bin.msg) 47 - else 48 - vim.health.error(bin.bin .. " not found, " .. bin.msg) 49 - end 50 - end 50 + check_binary(bin) 51 51 end 52 52 53 - vim.health.start "required treesitter parsers" 53 + vim.health.start "Treesitter" 54 54 for _, parser in ipairs(deps.treesitter) do 55 - if is_treesitter_parser_available(parser.parser) then 56 - vim.health.ok(parser.parser .. " parser installed") 57 - else 58 - vim.health.error(parser.parser .. " parser not found, " .. parser.msg) 59 - end 55 + check_treesitter(parser) 60 56 end 61 57 end 62 58
+1 -1
lua/gopher/iferr.lua
··· 42 42 43 43 vim.fn.append(pos, u.remove_empty_lines(vim.split(rs.stdout, "\n"))) 44 44 vim.cmd [[silent normal! j=2j]] 45 - vim.fn.setpos(".", pos) 45 + vim.fn.setpos(".", pos --[[@as integer[] ]]) 46 46 end 47 47 48 48 return iferr
+14 -13
lua/gopher/impl.lua
··· 3 3 ---@text 4 4 --- Integration of `impl` tool to generate method stubs for interfaces. 5 5 --- 6 - ---@usage 1. Automatically implement an interface for a struct: 7 - --- - Place your cursor on the struct where you want to implement the interface. 8 - --- - Run `:GoImpl io.Reader` 9 - --- - This will automatically determine the receiver and implement the `io.Reader` interface. 6 + ---@usage 7 + --- 1. Automatically implement an interface for a struct: 8 + --- - Place your cursor on the struct where you want to implement the interface. 9 + --- - Run `:GoImpl io.Reader` 10 + --- - This will automatically determine the receiver and implement the `io.Reader` interface. 10 11 --- 11 12 --- 2. Specify a custom receiver: 12 - --- - Place your cursor on the struct 13 - --- - Run `:GoImpl w io.Writer`, where: 14 - --- - `w` is the receiver. 15 - --- - `io.Writer` is the interface to implement. 13 + --- - Place your cursor on the struct 14 + --- - Run `:GoImpl w io.Writer`, where: 15 + --- - `w` is the receiver. 16 + --- - `io.Writer` is the interface to implement. 16 17 --- 17 18 --- 3. Explicitly specify the receiver, struct, and interface: 18 - --- - No need to place the cursor on the struct if all arguments are provided. 19 - --- - Run `:GoImpl r RequestReader io.Reader`, where: 20 - --- - `r` is the receiver. 21 - --- - `RequestReader` is the struct. 22 - --- - `io.Reader` is the interface to implement. 19 + --- - No need to place the cursor on the struct if all arguments are provided. 20 + --- - Run `:GoImpl r RequestReader io.Reader`, where: 21 + --- - `r` is the receiver. 22 + --- - `RequestReader` is the struct. 23 + --- - `io.Reader` is the interface to implement. 23 24 --- 24 25 --- Example: 25 26 --- >go
+10 -21
lua/gopher/init.lua
··· 13 13 local log = require "gopher._utils.log" 14 14 local tags = require "gopher.struct_tags" 15 15 local tests = require "gopher.gotests" 16 - local gocmd = require("gopher._utils.gocmd").run 16 + local go = require "gopher.go" 17 17 local gopher = {} 18 18 19 19 ---@toc_entry Setup ··· 35 35 36 36 ---@toc_entry Install dependencies 37 37 ---@tag gopher.nvim-dependencies 38 - ---@text Gopher.nvim implements most of its features using third-party tools. 39 - --- To install these tools, you can run `:GoInstallDeps` command 40 - --- or call `require("gopher").install_deps()` if you want to use lua api. 41 - --- By default dependencies will be installed asynchronously, 42 - --- to install them synchronously pass `{sync = true}` as an argument. 38 + ---@text 39 + --- Gopher.nvim implements most of its features using third-party tools. To 40 + --- install plugin's dependencies, you can run: 41 + --- `:GoInstallDeps` or `:GoInstallDepsSync` 42 + --- or use `require("gopher").install_deps()` if you prefer lua api. 43 43 gopher.install_deps = require("gopher.installer").install_deps 44 44 45 45 gopher.impl = require("gopher.impl").impl ··· 58 58 all = tests.all_tests, 59 59 } 60 60 61 - gopher.get = function(...) 62 - gocmd("get", ...) 63 - end 64 - 65 - gopher.mod = function(...) 66 - gocmd("mod", ...) 67 - end 68 - 69 - gopher.generate = function(...) 70 - gocmd("generate", ...) 71 - end 72 - 73 - gopher.work = function(...) 74 - gocmd("work", ...) 75 - end 61 + gopher.get = go.get 62 + gopher.mod = go.mod 63 + gopher.work = go.work 64 + gopher.generate = go.generate 76 65 77 66 return gopher
+1
lua/gopher/installer.lua
··· 9 9 impl = "github.com/josharian/impl@latest", 10 10 gotests = "github.com/cweill/gotests/...@develop", 11 11 iferr = "github.com/koron/iferr@latest", 12 + json2go = "olexsmir.xyz/json2go/cmd/json2go@latest", 12 13 } 13 14 14 15 ---@param opt vim.SystemCompleted
+137
lua/gopher/json2go.lua
··· 1 + ---@toc_entry json2go 2 + ---@tag gopher.nvim-json2go 3 + ---@text 4 + --- Convert json to go type annotations. 5 + --- 6 + ---@usage 7 + --- `:GoJson` opens a temporary buffer where you can paste or write JSON. 8 + --- Saving the buffer (`:w` or `:wq`) automatically closes it and inserts the 9 + --- generated Go struct into the original buffer at the cursor position. 10 + --- 11 + --- Alternatively, you can pass JSON directly as an argument: 12 + --- >vim 13 + --- :GoJson {"name": "Alice", "age": 30} 14 + --- < 15 + 16 + local c = require "gopher.config" 17 + local log = require "gopher._utils.log" 18 + local u = require "gopher._utils" 19 + local r = require "gopher._utils.runner" 20 + local json2go = {} 21 + 22 + ---@dochide 23 + ---@param bufnr integer 24 + ---@param cpos integer 25 + ---@param type_ string 26 + local function apply(bufnr, cpos, type_) 27 + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) 28 + local input_lines = u.remove_empty_lines(vim.split(type_, "\n")) 29 + for i, line in pairs(input_lines) do 30 + table.insert(lines, cpos + i, line) 31 + end 32 + vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines) 33 + end 34 + 35 + -- Convert json string to go type. 36 + --- 37 + ---@param json_str string Json string that is going to be converted to go type. 38 + ---@return string? Go type, or nil if failed. 39 + function json2go.transform(json_str) 40 + local cmd = { c.commands.json2go } 41 + if c.json2go.type_name then 42 + table.insert(cmd, "-type", c.json2go.type_name) 43 + end 44 + 45 + local rs = r.sync(cmd, { stdin = json_str }) 46 + if rs.code ~= 0 then 47 + u.notify("json2go: got this error: " .. rs.stdout, vim.log.levels.ERROR) 48 + log.error("json2go: got this error: " .. rs.stdout) 49 + return 50 + end 51 + return rs.stdout 52 + end 53 + 54 + ---@dochide 55 + ---@param ocpos integer 56 + local function interactive(ocpos) 57 + local obuf = vim.api.nvim_get_current_buf() 58 + local owin = vim.api.nvim_get_current_win() 59 + 60 + -- setup the input window 61 + local buf = vim.api.nvim_create_buf(false, true) 62 + vim.cmd(c.json2go.interactive_cmd) 63 + 64 + local win = vim.api.nvim_get_current_win() 65 + vim.api.nvim_win_set_buf(win, buf) 66 + vim.api.nvim_buf_set_name(buf, "[GoJson input]") 67 + vim.api.nvim_set_option_value("filetype", "jsonc", { buf = buf }) 68 + vim.api.nvim_set_option_value("buftype", "acwrite", { buf = buf }) 69 + vim.api.nvim_set_option_value("swapfile", false, { buf = buf }) 70 + vim.api.nvim_set_option_value("bufhidden", "delete", { buf = buf }) 71 + vim.api.nvim_buf_set_lines(buf, 0, -1, false, { 72 + "// Write your json here.", 73 + "// Writing and quitting (:wq), will generate go struct under the cursor.", 74 + "", 75 + "", 76 + }) 77 + 78 + vim.api.nvim_create_autocmd("BufLeave", { buffer = buf, command = "stopinsert" }) 79 + vim.api.nvim_create_autocmd("BufWriteCmd", { 80 + buffer = buf, 81 + once = true, 82 + callback = function() 83 + local input = vim.api.nvim_buf_get_lines(buf, 0, -1, true) 84 + local inp = table.concat( 85 + vim 86 + .iter(input) 87 + :filter(function(line) 88 + local found = string.find(line, "^//.*") 89 + return (not found) or (line == "") 90 + end) 91 + :totable(), 92 + "\n" 93 + ) 94 + 95 + local go_type = json2go.transform(inp) 96 + if not go_type then 97 + error "cound't convert json to go type" 98 + end 99 + 100 + vim.api.nvim_set_option_value("modified", false, { buf = buf }) 101 + apply(obuf, ocpos, go_type) 102 + 103 + vim.api.nvim_set_current_win(owin) 104 + vim.api.nvim_win_set_cursor(owin, { ocpos + 1, 0 }) 105 + 106 + vim.schedule(function() 107 + pcall(vim.api.nvim_win_close, win, true) 108 + pcall(vim.api.nvim_buf_delete, buf, {}) 109 + end) 110 + end, 111 + }) 112 + 113 + vim.cmd "normal! G" 114 + vim.cmd "startinsert" 115 + end 116 + 117 + --- Converts json string to go type, and puts result under the cursor. If 118 + --- [json_str] is nil, will open an interactive prompt (with cmd set in 119 + --- config). 120 + --- 121 + ---@param json_str? string 122 + function json2go.json2go(json_str) 123 + local cur_line = vim.api.nvim_win_get_cursor(0)[1] 124 + if not json_str then 125 + interactive(cur_line) 126 + return 127 + end 128 + 129 + local go_type = json2go.transform(json_str) 130 + if not go_type then 131 + error "cound't convert json to go type" 132 + end 133 + 134 + apply(0, cur_line, go_type) 135 + end 136 + 137 + return json2go
+67 -19
lua/gopher/struct_tags.lua
··· 1 1 ---@toc_entry Modify struct tags 2 2 ---@tag gopher.nvim-struct-tags 3 3 ---@text 4 - --- `struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to struct fields. 4 + --- `struct_tags` is utilizing the `gomodifytags` tool to add or remove tags to 5 + --- struct fields. 5 6 --- 6 7 ---@usage 7 - --- How to add/remove tags to struct fields: 8 + --- How to add/remove/clear tags to struct fields: 8 9 --- 1. Place cursor on the struct 9 10 --- 2. Run `:GoTagAdd json` to add json tags to struct fields 10 11 --- 3. Run `:GoTagRm json` to remove json tags to struct fields 12 + --- 4. Run `:GoTagClear` to clear all tags from struct fields 11 13 --- 12 - --- To clear all tags from struct run: `:GoTagClear` 14 + --- If you want to add/remove tag with options, you can use `json=omitempty` 15 + --- (where json is tag, and omitempty is its option). 16 + --- Example: `:GoTagAdd xml json=omitempty` 17 + --- 13 18 --- 14 19 --- NOTE: if you dont specify the tag it will use `json` as default 15 20 --- ··· 39 44 40 45 ---@dochide 41 46 ---@class gopher.StructTagInput 42 - ---@field tags string[] User provided tags 47 + ---@field input string[] User provided tags 43 48 ---@field range? gopher.StructTagRange (optional) 44 49 45 50 ---@dochide ··· 102 107 ) 103 108 end 104 109 110 + ---@dochide 111 + ---@param option string 112 + local function option_to_tag(option) 113 + return option:match "^(.-)=" 114 + end 115 + 116 + ---@dochide 105 117 ---@param args string[] 106 - ---@return string 118 + local function unwrap_if_needed(args) 119 + local out = {} 120 + for _, v in pairs(args) do 121 + for _, p in pairs(vim.split(v, ",")) do 122 + table.insert(out, p) 123 + end 124 + end 125 + return out 126 + end 127 + 128 + ---@dochide 129 + ---@class gopher.StructTagsArgs 130 + ---@field tags string 131 + ---@field options string 132 + 107 133 ---@dochide 108 - local function handler_user_tags(args) 109 - if #args == 0 then 110 - return c.gotag.default_tag 134 + ---@param args string[] 135 + ---@return gopher.StructTagsArgs 136 + function struct_tags.parse_args(args) 137 + args = unwrap_if_needed(args) 138 + 139 + local tags, options = {}, {} 140 + for _, v in pairs(args) do 141 + if string.find(v, "=") then 142 + table.insert(options, v) 143 + table.insert(tags, option_to_tag(v)) 144 + else 145 + table.insert(tags, v) 146 + end 111 147 end 112 - return table.concat(args, ",") 148 + 149 + return { 150 + tags = table.concat(u.list_unique(tags), ","), 151 + options = table.concat(u.list_unique(options), ","), 152 + } 113 153 end 114 154 115 - -- Adds tags to a struct under the cursor 116 - -- See |gopher.nvim-struct-tags| 155 + -- Adds tags to a struct under the cursor. See `:h gopher.nvim-struct-tags`. 117 156 ---@param opts gopher.StructTagInput 118 157 ---@dochide 119 158 function struct_tags.add(opts) ··· 122 161 local fpath = vim.fn.expand "%" 123 162 local bufnr = vim.api.nvim_get_current_buf() 124 163 125 - local user_tags = handler_user_tags(opts.tags) 126 - handle_tags(fpath, bufnr, opts.range, { "-add-tags", user_tags }) 164 + local user_args = struct_tags.parse_args(opts.input) 165 + handle_tags(fpath, bufnr, opts.range, { 166 + "-add-tags", 167 + (user_args.tags ~= "") and user_args.tags or c.gotag.default_tag, 168 + (user_args.options ~= "" or c.gotag.option) and "-add-options" or nil, 169 + (user_args.options ~= "") and user_args.options or c.gotag.option, 170 + }) 127 171 end 128 172 129 - -- Removes tags from a struct under the cursor 130 - -- See `:h gopher.nvim-struct-tags` 173 + -- Removes tags from a struct under the cursor. See `:h gopher.nvim-struct-tags`. 131 174 ---@dochide 132 175 ---@param opts gopher.StructTagInput 133 176 function struct_tags.remove(opts) ··· 136 179 local fpath = vim.fn.expand "%" 137 180 local bufnr = vim.api.nvim_get_current_buf() 138 181 139 - local user_tags = handler_user_tags(opts.tags) 140 - handle_tags(fpath, bufnr, opts.range, { "-remove-tags", user_tags }) 182 + local user_args = struct_tags.parse_args(opts.input) 183 + handle_tags(fpath, bufnr, opts.range, { 184 + "-remove-tags", 185 + (user_args.tags ~= "") and user_args.tags or c.gotag.default_tag, 186 + (user_args.options ~= "" or c.gotag.option ~= nil) and "-remove-options" or nil, 187 + (user_args.options ~= "") and user_args.options or c.gotag.option, 188 + }) 141 189 end 142 190 143 - -- Removes all tags from a struct under the cursor 144 - -- See `:h gopher.nvim-struct-tags` 191 + -- Removes all tags from a struct under the cursor. 192 + -- See `:h gopher.nvim-struct-tags`. 145 193 ---@dochide 146 194 function struct_tags.clear() 147 195 local fpath = vim.fn.expand "%"
+9 -4
plugin/gopher.lua
··· 48 48 -- :GoTag 49 49 cmd("GoTagAdd", function(opts) 50 50 require("gopher").tags.add { 51 - tags = opts.fargs, 51 + input = opts.fargs, 52 52 range = (opts.count ~= -1) and { 53 53 start = opts.line1, 54 54 end_ = opts.line2, ··· 58 58 59 59 cmd("GoTagRm", function(opts) 60 60 require("gopher").tags.rm { 61 - tags = opts.fargs, 61 + input = opts.fargs, 62 62 range = (opts.count ~= -1) and { 63 63 start = opts.line1, 64 64 end_ = opts.line2, ··· 70 70 require("gopher").tags.clear() 71 71 end) 72 72 73 + -- :GoJson 74 + cmd("GoJson", function(opts) 75 + local inp = ((opts.args ~= "" and opts.args) or nil) 76 + require("gopher.json2go").json2go(inp) 77 + end, "*") 78 + 73 79 -- :GoTest 74 80 cmd("GoTestAdd", function() 75 81 require("gopher").test.add() ··· 89 95 end, "*") 90 96 91 97 cmd("GoGet", function(opts) 92 - vim.print(opts) 93 98 require("gopher").get(opts.fargs) 94 99 end, "*") 95 100 ··· 98 103 end, "*") 99 104 100 105 cmd("GoGenerate", function(opts) 101 - require("gopher").generate(opts.fargs or "") 106 + require("gopher").generate(opts.fargs or { "" }) 102 107 end, "?")
+1
scripts/docgen.lua
··· 12 12 "lua/gopher/config.lua", 13 13 "plugin/gopher.lua", 14 14 "lua/gopher/struct_tags.lua", 15 + "lua/gopher/json2go.lua", 15 16 "lua/gopher/impl.lua", 16 17 "lua/gopher/gotests.lua", 17 18 "lua/gopher/iferr.lua",
-1
scripts/minimal_init.lua
··· 20 20 end 21 21 end 22 22 23 - install_plug "nvim-lua/plenary.nvim" 24 23 install_plug "nvim-treesitter/nvim-treesitter" 25 24 install_plug "echasnovski/mini.doc" -- used for docs generation 26 25 install_plug "folke/tokyonight.nvim" -- theme for generating demos
+6
spec/fixtures/comment/interface_many_method_input.go
··· 1 + package main 2 + 3 + type Testinger interface { 4 + Get(id string) int 5 + Set(id string, val int) 6 + }
+7
spec/fixtures/comment/interface_many_method_output.go
··· 1 + package main 2 + 3 + type Testinger interface { 4 + Get(id string) int 5 + // Set 6 + Set(id string, val int) 7 + }
+5
spec/fixtures/comment/interface_method_input.go
··· 1 + package main 2 + 3 + type Testinger interface { 4 + Method(input string) error 5 + }
+6
spec/fixtures/comment/interface_method_output.go
··· 1 + package main 2 + 3 + type Testinger interface { 4 + // Method 5 + Method(input string) error 6 + }
+18
spec/fixtures/comment/many_structs_fields_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 + )
+19
spec/fixtures/comment/many_structs_fields_output.go
··· 1 + package main 2 + 3 + type ( 4 + TestOne struct { 5 + Asdf string 6 + ID int 7 + } 8 + 9 + TestTwo struct { 10 + // Fesa 11 + Fesa int 12 + A bool 13 + } 14 + 15 + TestThree struct { 16 + Asufj int 17 + Fs string 18 + } 19 + )
+7
spec/fixtures/comment/struct_fields_input.go
··· 1 + package main 2 + 3 + type CommentStruct struct { 4 + Name string 5 + Address string 6 + Aliases []string 7 + }
+8
spec/fixtures/comment/struct_fields_output.go
··· 1 + package main 2 + 3 + type CommentStruct struct { 4 + Name string 5 + // Address 6 + Address string 7 + Aliases []string 8 + }
+5
spec/fixtures/comment/svar_input.go
··· 1 + package main 2 + 3 + func varTest() { 4 + s := "something" 5 + }
+6
spec/fixtures/comment/svar_output.go
··· 1 + package main 2 + 3 + func varTest() { 4 + // s 5 + s := "something" 6 + }
+5
spec/fixtures/comment/var_input.go
··· 1 + package main 2 + 3 + func test() { 4 + var imAVar string 5 + }
+6
spec/fixtures/comment/var_output.go
··· 1 + package main 2 + 3 + func test() { 4 + // imAVar 5 + var imAVar string 6 + }
+8
spec/fixtures/comment/var_struct_fields_input.go
··· 1 + package main 2 + 3 + func main() { 4 + var s struct { 5 + API string 6 + Key string 7 + } 8 + }
+9
spec/fixtures/comment/var_struct_fields_output.go
··· 1 + package main 2 + 3 + func main() { 4 + var s struct { 5 + API string 6 + // Key 7 + Key string 8 + } 9 + }
+2
spec/fixtures/json2go/interativly_input.go
··· 1 + package main 2 +
+5
spec/fixtures/json2go/interativly_output.go
··· 1 + package main 2 + 3 + type AutoGenerated struct { 4 + Json bool `json:"json"` 5 + }
+2
spec/fixtures/json2go/manual_input.go
··· 1 + package main 2 +
+7
spec/fixtures/json2go/manual_output.go
··· 1 + package main 2 + 3 + type AutoGenerated struct { 4 + User struct { 5 + Name string `json:"name"` 6 + } `json:"user"` 7 + }
+8
spec/fixtures/tags/overwrite_default_option_input.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int 5 + Another struct { 6 + Second string 7 + } 8 + }
+8
spec/fixtures/tags/overwrite_default_option_output.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int `xml:"id,otheroption"` 5 + Another struct { 6 + Second string `xml:"second,otheroption"` 7 + } `xml:"another,otheroption"` 8 + }
+11
spec/fixtures/tags/remove_with_option_input.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int `json:"id,omitempty" xml:"id,someoption"` 5 + Name string `json:"name,omitempty" xml:"name,someoption"` 6 + Num int64 `json:"num,omitempty" xml:"num,someoption"` 7 + Another struct { 8 + First int `json:"first,omitempty" xml:"first,someoption"` 9 + Second string `json:"second,omitempty" xml:"second,someoption"` 10 + } `json:"another,omitempty" xml:"another,someoption"` 11 + }
+11
spec/fixtures/tags/remove_with_option_output.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int `xml:"id,someoption"` 5 + Name string `xml:"name,someoption"` 6 + Num int64 `xml:"num,someoption"` 7 + Another struct { 8 + First int `xml:"first,someoption"` 9 + Second string `xml:"second,someoption"` 10 + } `xml:"another,someoption"` 11 + }
+8
spec/fixtures/tags/with_default_option_input.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int 5 + Another struct { 6 + Second string 7 + } 8 + }
+8
spec/fixtures/tags/with_default_option_output.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int `xml:"id,theoption"` 5 + Another struct { 6 + Second string `xml:"second,theoption"` 7 + } `xml:"another,theoption"` 8 + }
+11
spec/fixtures/tags/with_option_input.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int 5 + Name string 6 + Num int64 7 + Another struct { 8 + First int 9 + Second string 10 + } 11 + }
+11
spec/fixtures/tags/with_option_output.go
··· 1 + package main 2 + 3 + type Test struct { 4 + ID int `json:"id,omitempty"` 5 + Name string `json:"name,omitempty"` 6 + Num int64 `json:"num,omitempty"` 7 + Another struct { 8 + First int `json:"first,omitempty"` 9 + Second string `json:"second,omitempty"` 10 + } `json:"another,omitempty"` 11 + }
+28
spec/integration/comment_test.lua
··· 18 18 do_the_test("struct", { 4, 1 }) 19 19 end 20 20 21 + comment["should add a comment on struct field"] = function() 22 + do_the_test("struct_fields", { 5, 8 }) 23 + end 24 + 25 + comment["should add a comment on var struct field"] = function() 26 + do_the_test("var_struct_fields", { 6, 4 }) 27 + end 28 + 29 + comment["should add a comment on one field of many structs"] = function() 30 + do_the_test("many_structs_fields", { 10, 4 }) 31 + end 32 + 21 33 comment["should add comment to function"] = function() 22 34 do_the_test("func", { 3, 1 }) 23 35 end ··· 28 40 29 41 comment["should add comment to interface"] = function() 30 42 do_the_test("interface", { 3, 6 }) 43 + end 44 + 45 + comment["should add comment on interface method"] = function() 46 + do_the_test("interface_method", { 4, 2 }) 47 + end 48 + 49 + comment["should add a comment on interface with many method"] = function() 50 + do_the_test("interface_many_method", { 5, 2 }) 51 + end 52 + 53 + comment["should add a comment on a var"] = function() 54 + do_the_test("var", { 4, 2 }) 55 + end 56 + 57 + comment["should add a comment on a short declared var"] = function() 58 + do_the_test("svar", { 4, 8 }) 31 59 end 32 60 33 61 comment["otherwise should add // above cursor"] = function()
+2 -1
spec/integration/iferr_test.lua
··· 14 14 child.lua [[ 15 15 require("gopher").setup { 16 16 iferr = { message = 'fmt.Errorf("failed to %w", err)' } 17 - } ]] 17 + } 18 + ]] 18 19 19 20 local rs = t.setup_test("iferr/message", child, { 6, 2 }) 20 21 child.cmd "GoIfErr"
+25
spec/integration/json2go_test.lua
··· 1 + local t = require "spec.testutils" 2 + local child, T, json2go = t.setup "json2go" 3 + 4 + json2go["should convert interativly"] = function() 5 + local rs = t.setup_test("json2go/interativly", child, { 2, 0 }) 6 + child.cmd "GoJson" 7 + child.type_keys [[{"json": true}]] 8 + child.type_keys "<Esc>" 9 + child.cmd "wq" -- quit prompt 10 + child.cmd "write" -- the fixture file 11 + 12 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 13 + t.cleanup(rs) 14 + end 15 + 16 + json2go["should convert argument"] = function() 17 + local rs = t.setup_test("json2go/manual", child, { 2, 0 }) 18 + child.cmd [[GoJson {"user": {"name": "user-ovic"}}]] 19 + child.cmd "write" 20 + 21 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 22 + t.cleanup(rs) 23 + end 24 + 25 + return T
+48
spec/integration/struct_tags_test.lua
··· 96 96 t.cleanup(rs) 97 97 end 98 98 99 + struct_tags["should add tags with option"] = function() 100 + local rs = t.setup_test("tags/with_option", child, { 3, 6 }) 101 + child.cmd "GoTagAdd json=omitempty" 102 + child.cmd "write" 103 + 104 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 105 + t.cleanup(rs) 106 + end 107 + 108 + struct_tags["should add tags with default option"] = function() 109 + child.lua [[ 110 + require("gopher").setup { 111 + gotag = { option = "xml=theoption" }, 112 + } 113 + ]] 114 + 115 + local rs = t.setup_test("tags/with_default_option", child, { 3, 6 }) 116 + child.cmd "GoTagAdd xml" 117 + child.cmd "write" 118 + 119 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 120 + t.cleanup(rs) 121 + end 122 + 123 + struct_tags["should add tags and overwrite default option"] = function() 124 + child.lua [[ 125 + require("gopher").setup { 126 + gotag = { option = "xml=theoption" }, 127 + } 128 + ]] 129 + 130 + local rs = t.setup_test("tags/overwrite_default_option", child, { 3, 6 }) 131 + child.cmd "GoTagAdd xml=otheroption" 132 + child.cmd "write" 133 + 134 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 135 + t.cleanup(rs) 136 + end 137 + 138 + struct_tags["should remove tag with specified option"] = function() 139 + local rs = t.setup_test("tags/remove_with_option", child, { 3, 6 }) 140 + child.cmd "GoTagRm json=omitempty" 141 + child.cmd "write" 142 + 143 + t.eq(t.readfile(rs.tmp), rs.fixtures.output) 144 + t.cleanup(rs) 145 + end 146 + 99 147 return T
+3
spec/unit/config_test.lua
··· 7 7 end 8 8 9 9 config["can be called with empty table"] = function() 10 + ---@diagnostic disable-next-line: missing-fields 10 11 require("gopher").setup {} 11 12 end 12 13 13 14 config["should change option"] = function() 14 15 local log_level = 1234567890 16 + 17 + ---@diagnostic disable-next-line: missing-fields 15 18 require("gopher").setup { 16 19 log_level = log_level, 17 20 }
+68
spec/unit/struct_tag_test.lua
··· 1 + local t = require "spec.testutils" 2 + local _, T, st = t.setup "struct_tags" 3 + 4 + st["should parse tags"] = function() 5 + local out = require("gopher.struct_tags").parse_args { "json", "yaml", "etc" } 6 + 7 + t.eq(out.tags, "json,yaml,etc") 8 + t.eq(out.options, "") 9 + end 10 + 11 + st["should parse tags separated by commas"] = function() 12 + local out = require("gopher.struct_tags").parse_args { "json,yaml,etc" } 13 + 14 + t.eq(out.tags, "json,yaml,etc") 15 + t.eq(out.options, "") 16 + end 17 + 18 + st["should parse tags separated by command and spaces"] = function() 19 + local out = require("gopher.struct_tags").parse_args { 20 + "json,yaml", 21 + "json=omitempty", 22 + "xml=something", 23 + } 24 + 25 + t.eq(out.tags, "json,yaml,xml") 26 + t.eq(out.options, "json=omitempty,xml=something") 27 + end 28 + 29 + st["should parse tag with an option"] = function() 30 + local out = require("gopher.struct_tags").parse_args { 31 + "json=omitempty", 32 + "xml", 33 + "xml=theoption", 34 + } 35 + 36 + t.eq(out.tags, "json,xml") 37 + t.eq(out.options, "json=omitempty,xml=theoption") 38 + end 39 + 40 + st["should parse tags with an option"] = function() 41 + local out = require("gopher.struct_tags").parse_args { "json=omitempty", "yaml" } 42 + 43 + t.eq(out.tags, "json,yaml") 44 + t.eq(out.options, "json=omitempty") 45 + end 46 + 47 + st["should parse tags with an option separated with comma"] = function() 48 + local out = require("gopher.struct_tags").parse_args { "json=omitempty,yaml" } 49 + 50 + t.eq(out.tags, "json,yaml") 51 + t.eq(out.options, "json=omitempty") 52 + end 53 + 54 + st["should parse tags with options specified separately"] = function() 55 + local out = require("gopher.struct_tags").parse_args { "json", "yaml", "json=omitempty" } 56 + 57 + t.eq(out.tags, "json,yaml") 58 + t.eq(out.options, "json=omitempty") 59 + end 60 + 61 + st["should parse tags with options specified separately and separated by comma"] = function() 62 + local out = require("gopher.struct_tags").parse_args { "json,yaml,json=omitempty" } 63 + 64 + t.eq(out.tags, "json,yaml") 65 + t.eq(out.options, "json=omitempty") 66 + end 67 + 68 + return T
+34
spec/unit/utils_test.lua
··· 22 22 t.eq(u.trimend " hi ", " hi") 23 23 end 24 24 25 + utils["should add .indent() spaces"] = function() 26 + local u = require "gopher._utils" 27 + local line = " func Test() error {" 28 + local indent = 4 29 + 30 + t.eq(" ", u.indent(line, indent)) 31 + end 32 + 33 + utils["should add .indent() a tab"] = function() 34 + local u = require "gopher._utils" 35 + local line = "\tfunc Test() error {" 36 + local indent = 1 37 + 38 + t.eq("\t", u.indent(line, indent)) 39 + end 40 + 41 + utils["should add .indent() 2 tabs"] = function() 42 + local u = require "gopher._utils" 43 + local line = "\t\tfunc Test() error {" 44 + local indent = 2 45 + 46 + t.eq("\t\t", u.indent(line, indent)) 47 + end 48 + 49 + utils["should .list_unique on list with duplicates"] = function() 50 + local u = require "gopher._utils" 51 + t.eq({ "json", "xml" }, u.list_unique { "json", "xml", "json" }) 52 + end 53 + 54 + utils["should .list_unique on list with no duplicates"] = function() 55 + local u = require "gopher._utils" 56 + t.eq({ "json", "xml" }, u.list_unique { "json", "xml" }) 57 + end 58 + 25 59 return T
+3
vhs/Taskfile.yml
··· 18 18 19 19 impl: 20 20 cmd: vhs impl.tape 21 + 22 + json2go: 23 + cmd: vhs json2go.tape
vhs/comment.gif

This is a binary file and will not be displayed.

+4 -9
vhs/comment.tape
··· 9 9 Set Theme "tokyonight" 10 10 Set TypingSpeed 250ms 11 11 12 - Hide 13 - Type@0ms "alias nvim='./nvim.sh'" Enter 14 - Type@0ms "clear" Enter 15 - Show 16 - 17 - Type "nvim comment.go" Sleep 150ms Enter 12 + Hide Type@0ms "./nvim.sh comment.go" Enter Show 18 13 19 14 # package 20 15 Type ":GoCmt" Enter Sleep 500ms Escape Sleep 700ms 21 16 22 17 # func 23 - Type "3j" 18 + Type@400ms "jjj" 24 19 Type ":GoCmt" Enter Sleep 500ms Escape Sleep 700ms 25 20 26 21 # struct 27 - Type "3j" 22 + Type@400ms "jjj" 28 23 Type ":GoCmt" Enter Sleep 500ms Escape Sleep 700ms 29 24 30 25 # interface 31 - Type "3j" 26 + Type@400ms "jjj" 32 27 Type ":GoCmt" Enter Sleep 500ms Escape Sleep 700ms 33 28 34 29 Sleep 5s
vhs/iferr.gif

This is a binary file and will not be displayed.

+1 -1
vhs/iferr.go
··· 1 1 package demos 2 2 3 - func ifErr() { 3 + func ifErr() error { 4 4 out, err := doSomething() 5 5 6 6 _ = out
+1 -6
vhs/iferr.tape
··· 10 10 Set Theme "tokyonight" 11 11 Set TypingSpeed 250ms 12 12 13 - Hide 14 - Type@0ms "alias nvim='./nvim.sh'" Enter 15 - Type@0ms "clear" Enter 16 - Show 17 - 18 - Type "nvim iferr.go" Sleep 150ms Enter 13 + Hide Type@0ms "./nvim.sh iferr.go" Enter Show 19 14 20 15 Type "3j" 21 16 Type ":GoIfErr" Sleep 500ms Enter
vhs/impl.gif

This is a binary file and will not be displayed.

+3 -8
vhs/impl.tape
··· 1 1 Output impl.gif 2 2 Require nvim 3 - Require iferr 3 + Require impl 4 4 5 5 Set FontFamily "JetBrains Mono" 6 6 Set Height 800 ··· 10 10 Set Theme "tokyonight" 11 11 Set TypingSpeed 250ms 12 12 13 - Hide 14 - Type@0ms "alias nvim='./nvim.sh'" Enter 15 - Type@0ms "clear" Enter 16 - Show 17 - 18 - Type "nvim impl.go" Sleep 150ms Enter 13 + Hide Type@0ms "./nvim.sh impl.go" Enter Show 19 14 20 15 Type "2j" 21 - Type ":GoImpl io.Reader" Sleep 500ms Enter 16 + Type ":GoImpl c io.Reader" Sleep 700ms Enter 22 17 23 18 Sleep 5s
vhs/json2go.gif

This is a binary file and will not be displayed.

+2
vhs/json2go.go
··· 1 + package main 2 +
+27
vhs/json2go.tape
··· 1 + Output json2go.gif 2 + Require nvim 3 + Require json2go 4 + 5 + Set FontFamily "JetBrains Mono" 6 + Set Height 800 7 + Set Width 1500 8 + Set Padding 20 9 + Set Shell "bash" 10 + Set Theme "tokyonight" 11 + Set TypingSpeed 250ms 12 + 13 + Hide Type@0ms "./nvim.sh json2go.go" Enter Show 14 + 15 + Type@0ms "G" 16 + Type@400ms ":GoJson" Sleep 500ms Enter 17 + Type@70ms "{" Enter 18 + Type@70ms `"json": true,` Enter 19 + Type@70ms `"user": {"name": "Name", "of_age": true}` Enter 20 + Type@70ms "}" 21 + Escape Type@500ms ":wq" Enter 22 + Sleep 1s 23 + 24 + Type@25ms "G2o" Escape 25 + Type@120ms `:GoJson {"json": true}` Enter 26 + 27 + Sleep 5s
vhs/tags.gif

This is a binary file and will not be displayed.

+6 -11
vhs/tags.tape
··· 10 10 Set Theme "tokyonight" 11 11 Set TypingSpeed 250ms 12 12 13 - Hide 14 - Type@0ms "alias nvim='./nvim.sh'" Enter 15 - Type@0ms "clear" Enter 16 - Show 13 + Hide Type@0ms "./nvim.sh tags.go" Enter Show 17 14 18 - Type "nvim tags.go" Sleep 150ms Enter 19 - 20 - Type "3j" 15 + Type@400ms "jjj" 21 16 Type ":GoTagAdd json yaml" Sleep 500ms Enter 22 17 Type@120ms ":w" Enter 23 18 Sleep 1s 24 19 25 20 Type ":GoTagRm json" Sleep 500ms Enter 26 - Type@120ms ":w" Enter 21 + Type ":w" Enter 27 22 Sleep 1s 28 23 29 24 Type ":GoTagClear" Sleep 500ms Enter 30 - Type@120ms ":w" Enter 25 + Type ":w" Enter 31 26 Sleep 1s 32 27 33 - Type "jV2j" 34 - Type ":GoTagAdd xml" Sleep 500ms Enter 28 + Type@400ms "jVjj" 29 + Type ":GoTagAdd json=omitempty" Sleep 500ms Enter 35 30 36 31 Sleep 5s