diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..81eca2d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +[*.{md,yml,yaml,toml,lua,vim}] +indent_size = 2 + +[*.go] +indent_style = tab +indent_size = 4 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index b1be903..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Format and lint -on: [push, pull_request] - -jobs: - format: - name: stylua - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: JohnnyMorganz/stylua-action@1.0.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - version: 0.14.0 - args: --check . - - lint: - name: selene - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: NTBBloodbath/selene-action@v1.0.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --display-style=quiet . diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml new file mode 100644 index 0000000..a78c3c3 --- /dev/null +++ b/.github/workflows/linters.yml @@ -0,0 +1,19 @@ +name: linters +on: [push, pull_request] + +jobs: + linters: + name: linters + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: JohnnyMorganz/stylua-action@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: latest + args: --check . + + - uses: NTBBloodbath/selene-action@v1.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: . diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..09fb168 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,44 @@ +name: tests +on: [push, pull_request] + +jobs: + tests: + strategy: + matrix: + os: [ubuntu-latest] + nvim_version: + - nightly + - v0.7.0 + - v0.7.2 + - v0.8.0 + - v0.8.1 + - v0.8.2 + - v0.8.3 + - v0.9.0 + - v0.9.1 + - v0.9.2 + - v0.9.4 + - v0.9.5 + runs-on: ${{ matrix.os }} + steps: + - name: Install Task + uses: arduino/setup-task@v1 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - uses: actions/checkout@v3 + + - name: Install Neovim + run: | + mkdir -p /tmp/nvim + wget -q https://github.com/neovim/neovim/releases/download/${{ matrix.nvim_version }}/nvim.appimage -O /tmp/nvim/nvim.appimage + cd /tmp/nvim + chmod a+x ./nvim.appimage + ./nvim.appimage --appimage-extract + echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH + + - name: Run Tests + run: | + nvim --version + task test diff --git a/.gitignore b/.gitignore index a5c9323..df7d859 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -playground/ +/playground/ +/.tests/ diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000..a0d5712 --- /dev/null +++ b/.luarc.json @@ -0,0 +1,10 @@ +{ + "diagnostics.globals": [ + "describe", + "it", + "before_each", + "after_each", + "before_all", + "after_all" + ] +} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 464c44d..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,15 +0,0 @@ -repos: -- repo: local - hooks: - - id: stylua - name: StyLua - language: rust - entry: stylua - types: [lua] - args: ["--check", "-"] - - id: selene - name: Selene - language: rust - entry: selene - types: [lua] - args: ["-"] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b3a3c7b..39092b1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,14 @@ # Contributing to `gopher.nvim` -Thank you for looking to contributing +Thank you for taking the time to submit some code to gopher.nvim. It means a lot! + +### Task running + +In this codebase for running tasks is used [Taskfile](https://taskfile.dev). +You can install it with: +```bash +go install github.com/go-task/task/v3/cmd/task@latest +``` ### Styling and formatting @@ -8,6 +16,40 @@ Code is formatted by [stylua](https://github.com/JohnnyMorganz/StyLua) and linte You can install these with: ```bash -cargo install stylua -cargo install selene +sudo pacman -S selene stylua +# or whatever is your package manager +# or way of installing pkgs +``` + +For formatting use this following commands, or setup your editor to integrate with selene/stylua: +```bash +task format +task format:check # will check if your code formatted +task lint +``` + +### Documentation + +Here we are using [mini.doc](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-doc.md) +for generating help files based on EmmyLua-like annotations in comments + +You can generate docs with: +```bash +task docgen +``` + +### Commit messages +We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), please follow it. + +### Testing + +For testing this plugins uses [plenary.nvim](https://github.com/nvim-lua/plenary.nvim). +All tests live in [/spec](https://github.com/olexsmir/gopher.nvim/tree/main/spec) dir. + +You can run tests with: +```bash +task test +# also there are some aliases for that +task tests +task spec ``` diff --git a/Makefile b/Makefile deleted file mode 100644 index 2dc03c4..0000000 --- a/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -.PHONY: -.SILENT: - -format: - stylua **/*.lua - -lint: - selene **/*.lua - -test: - nvim --headless -u ./spec/minimal_init.vim -c "PlenaryBustedDirectory spec {minimal_init='./spec/minimal_init.vim'}" diff --git a/README.md b/README.md index 61f0e26..d256e5f 100644 --- a/README.md +++ b/README.md @@ -4,157 +4,230 @@ Minimalistic plugin for Go development in Neovim written in Lua. -It's not an LSP tool, the main goal of this plugin is add go tooling support in Neovim. +It's **NOT** an LSP tool, the main goal of this plugin is to add go tooling support in Neovim. -## Install +> If you want to use new and maybe undocumented, and unstable features you might use [develop](https://github.com/olexsmir/gopher.nvim/tree/develop) branch. -Pre-dependency: [go](https://github.com/golang/go) (tested on 1.17 and 1.18) +## Install (using [lazy.nvim](https://github.com/folke/lazy.nvim)) + +Pre-dependency: + +- [Go](https://github.com/golang/go) +- `go` treesitter parser, install by `:TSInstall go` ```lua -use { +{ "olexsmir/gopher.nvim", - requires = { -- dependencies + ft = "go", + -- branch = "develop", -- if you want develop branch + -- keep in mind, it might break everything + dependencies = { "nvim-lua/plenary.nvim", "nvim-treesitter/nvim-treesitter", + "mfussenegger/nvim-dap", -- (optional) only if you use `gopher.dap` }, + -- (optional) will update plugin's deps on every update + build = function() + vim.cmd.GoInstallDeps() + end, + ---@type gopher.Config + opts = {}, } ``` -Also, run `TSInstall go` if `go` parser if isn't installed yet. - -## Config +## Configuratoin -By `.setup` function you can configure the plugin. +> [!IMPORTANT] +> +> If you need more info look `:h gopher.nvim` -Note: - -- `installer` does not install the tool in user set path +**Take a look at default options** ```lua require("gopher").setup { commands = { go = "go", gomodifytags = "gomodifytags", - gotests = "~/go/bin/gotests", -- also you can set custom command path + gotests = "gotests", impl = "impl", iferr = "iferr", + dlv = "dlv", + }, + gotests = { + -- gotests doesn't have template named "default" so this plugin uses "default" to set the default template + template = "default", + -- path to a directory containing custom test code templates + template_dir = nil, + -- switch table tests from using slice to map (with test name for the key) + -- works only with gotests installed from develop branch + named = false, + }, + gotag = { + transform = "snakecase", }, } ``` ## Features -1. Installation requires this go tool: + -```vim -:GoInstallDeps -``` +
+ + Install plugin's go deps + -It will install next tools: + ```vim + :GoInstallDeps + ``` -- [gomodifytags](https://github.com/fatih/gomodifytags) -- [impl](https://github.com/josharian/impl) -- [gotests](https://github.com/cweill/gotests) -- [iferr](https://github.com/koron/iferr) + This will install the following tools: -2. Modify struct tags: - By default `json` tag will be added/removed, if not set: + - [gomodifytags](https://github.com/fatih/gomodifytags) + - [impl](https://github.com/josharian/impl) + - [gotests](https://github.com/cweill/gotests) + - [iferr](https://github.com/koron/iferr) + - [dlv](github.com/go-delve/delve/cmd/dlv) +
-```vim -:GoTagAdd json " For add json tag -:GoTagRm yaml " For remove yaml tag -``` +
+ + Add and remove tags for structs via gomodifytags + -3. Run `go mod` command: + By default `json` tag will be added/removed, if not set: -```vim -:GoMod tidy " Runs `go mod tidy` -:GoMod init asdf " Runs `go mod init asdf` -``` + ```vim + " add json tag + :GoTagAdd json + + " remove yaml tag + :GoTagRm yaml + ``` -4. Run `go get` command + ```lua + -- or you can use lua api + require("gopher").tags.add "xml" + require("gopher").tags.rm "proto" + ``` +
-Link can have a `http` or `https` prefix. +
+ + Generating tests via gotests + -You can provide more than one package url: + ```vim + " Generate one test for a specific function/method(one under cursor) + :GoTestAdd -```vim -:GoGet github.com/gorilla/mux -``` + " Generate all tests for all functions/methods in the current file + :GoTestsAll -5. Interface implementation + " Generate tests for only exported functions/methods in the current file + :GoTestsExp + ``` -Command syntax: + ```lua + -- or you can use lua api + require("gopher").test.add() + require("gopher").test.exported() + require("gopher").test.all() + ``` -```vim -:GoImpl [receiver] [interface] + For named tests see `:h gopher.nvim-gotests-named` +
-" Also you can put cursor on the struct and run: -:GoImpl [interface] -``` +
+ + Run commands like go mod/get/etc inside of nvim + -Example of usage: + ```vim + :GoGet github.com/gorilla/mux -```vim -" Example -:GoImpl r Read io.Reader -" or simply put your cursor in the struct and run: -:GoImpl io.Reader -``` + " Link can have an `http` or `https` prefix. + :GoGet https://github.com/lib/pq -6. Generate tests with [gotests](https://github.com/cweill/gotests) + " You can provide more than one package url + :GoGet github.com/jackc/pgx/v5 github.com/google/uuid/ -Generate one test for a specific function/method: + " go mod commands + :GoMod tidy + :GoMod init new-shiny-project -```vim -:GoTestAdd -``` + " go work commands + :GoWork sync -Generate all tests for all functions/methods in current file: + " run go generate in cwd + :GoGenerate -```vim -:GoTestsAll -``` + " run go generate for the current file + :GoGenerate % + ``` +
-Generate tests only for exported functions/methods in current file: +
+ + Interface implementation via impl + -```vim -:GoTestsExp -``` + Syntax of the command: + ```vim + :GoImpl [receiver] [interface] -7. Run `go generate` command; + " also you can put a cursor on the struct and run + :GoImpl [interface] + ``` -```vim -" Run `go generate` in cwd path -:GoGenerate + Usage examples: + ```vim + :GoImpl r Read io.Reader + :GoImpl Write io.Writer -" Run `go generate` for current file -:GoGenerate % -``` + " or you can simply put a cursor on the struct and run + :GoImpl io.Reader + ``` +
-8. Generate doc comment +
+ + Generate boilerplate for doc comments + -First set a cursor on **public** package/function/interface/struct and execute: + First set a cursor on **public** package/function/interface/struct and execute: -```vim -:GoCmt -``` + ```vim + :GoCmt + ``` +
-9. Generate `if err` -Set cursor on the line with **err** and execute: +
+ + Generate if err != nil { via iferr + -```vim -:GoIfErr -``` + Set the cursor on the line with `err` and execute -10. Setup nvim-dap for go in one line. + ```vim + :GoIfErr + ``` +
-Notice: [nvim-dap](https://github.com/mfussenegger/nvim-dap) is required +
+ + Setup nvim-dap for go in one line + -```lua -require"gopher.dap".setup() -``` + THIS FEATURE WILL BE REMOVED IN `0.1.6` + + note [nvim-dap](https://github.com/mfussenegger/nvim-dap) has to be installed + + ```lua + require("gopher.dap").setup() + ``` +
## Contributing @@ -164,3 +237,4 @@ PRs are always welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) - [go.nvim](https://github.com/ray-x/go.nvim) - [nvim-dap-go](https://github.com/leoluz/nvim-dap-go) +- [iferr](https://github.com/koron/iferr) diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..581f11c --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,49 @@ +version: "3" +tasks: + format: + desc: formats all lua files in repo + cmds: + - stylua . + + lint: + desc: runs all linters + cmds: + - task: selene + - task: stylua:check + + selene: + desc: runs lua linter(selene) + cmds: + - selene . + + stylua:check: + desc: runs stylua in check mode + cmds: + - stylua --check . + + stylua: + desc: runs lua formatter + cmds: + - stylua . + + test: + desc: runs all tests + aliases: [tests, spec] + cmds: + - | + nvim --headless \ + -u ./scripts/minimal_init.lua \ + -c "PlenaryBustedDirectory spec \ + {minimal_init='./scripts/minimal_init.lua' \ + ,sequential=true}" \ + -c ":qa!" + + docgen: + desc: generate vimhelp + cmds: + - | + nvim --noplugin \ + --headless \ + -u "./scripts/minimal_init.lua" \ + -c "luafile ./scripts/docgen.lua" \ + -c ":qa!" diff --git a/autoload/health/gopher.vim b/autoload/health/gopher.vim index 7ff5205..4e3c56d 100644 --- a/autoload/health/gopher.vim +++ b/autoload/health/gopher.vim @@ -1,3 +1,3 @@ function! health#gopher#check() - lua require"gopher.health".check() + lua require("gopher.health").check() endfunction diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..61ffc7c --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +/tags diff --git a/doc/gopher.nvim.txt b/doc/gopher.nvim.txt new file mode 100644 index 0000000..d4a82d0 --- /dev/null +++ b/doc/gopher.nvim.txt @@ -0,0 +1,220 @@ +*gopher.nvim* + +============================================================================== + +gopher.nvim is a minimalistic plugin for Go development in Neovim written in Lua. +It's not an LSP tool, the main goal of this plugin is add go tooling support in Neovim. + +------------------------------------------------------------------------------ + *gopher.nvim-table-of-contents* +Table of Contents + Setup....................................................|gopher.nvim-setup| + Install dependencies..............................|gopher.nvim-install-deps| + Configuration...........................................|gopher.nvim-config| + Modifty struct tags................................|gopher.nvim-struct-tags| + Auto implementation of interface methods..................|gopher.nvim-impl| + Generating unit tests boilerplate......................|gopher.nvim-gotests| + Iferr....................................................|gopher.nvim-iferr| + Generate comments.....................................|gopher.nvim-comments| + Setup `nvim-dap` for Go......................................|gopher.nvim-dap| + +------------------------------------------------------------------------------ + *gopher.nvim-setup* + `gopher.setup`({user_config}) +Setup function. This method simply merges default configs with opts table. +You can read more about configuration at |gopher.nvim-config| +Calling this function is optional, if you ok with default settings. Look |gopher.nvim.config-defaults| + +Usage ~ +`require("gopher").setup {}` (replace `{}` with your `config` table) +Parameters ~ +{user_config} gopher.Config + +------------------------------------------------------------------------------ + *gopher.nvim-install-deps* + `gopher.install_deps` +Gopher.nvim implements most of its features using third-party tools. +To install these tools, you can run `:GoInstallDeps` command +or call `require("gopher").install_deps()` if you want ues lua api. + + +============================================================================== +------------------------------------------------------------------------------ + *gopher.nvim-config* +config it is the place where you can configure the plugin. +also this is optional is you're ok with default settings. +You can look at default options |gopher.nvim-config-defaults| + +------------------------------------------------------------------------------ + *gopher.nvim-config-defaults* + `default_config` +>lua + local default_config = { + --minidoc_replace_end + + -- log level, you might consider using DEBUG or TRACE for degugging the plugin + ---@type number + log_level = vim.log.levels.INFO, + + -- user specified paths to binaries + ---@class gopher.ConfigCommand + commands = { + go = "go", + gomodifytags = "gomodifytags", + gotests = "gotests", + impl = "impl", + iferr = "iferr", + dlv = "dlv", + }, + ---@class gopher.ConfigGotests + gotests = { + -- gotests doesn't have template named "default" so this plugin uses "default" to set the default template + template = "default", + -- path to a directory containing custom test code templates + ---@type string|nil + template_dir = nil, + -- switch table tests from using slice to map (with test name for the key) + -- works only with gotests installed from develop branch + named = false, + }, + ---@class gopher.ConfigGoTag + gotag = { + ---@type gopher.ConfigGoTagTransform + transform = "snakecase", + }, + } +< +Class ~ +{gopher.Config} + + +============================================================================== +------------------------------------------------------------------------------ + *gopher.nvim-struct-tags* +struct-tags is utilizing the `gomodifytags` tool to add or remove tags to struct fields. +Usage ~ +- put your coursor on the struct +- run `:GoTagAdd json` to add json tags to struct fields +- run `:GoTagRm json` to remove json tags to struct fields + +note: if you dont spesify the tag it will use `json` as default + +simple example: +>go + // before + type User struct { + // ^ put your cursor here + // run `:GoTagAdd yaml` + ID int + Name string + } + + // after + type User struct { + ID int `yaml:id` + Name string `yaml:name` + } +< + + +============================================================================== +------------------------------------------------------------------------------ + *gopher.nvim-impl* +impl is utilizing the `impl` tool to generate method stubs for interfaces. +Usage ~ + +1. put your coursor on the struct on which you want implement the interface + and run `:GoImpl io.Reader` + which will automatically choose the reciver for the methods and + implement the `io.Reader` interface +2. same as previous but with custom receiver, so put your coursor on the struct + run `:GoImpl w io.Writer` + where `w` is the receiver and `io.Writer` is the interface +3. specift receiver, struct, and interface + there's no need to put your coursor on the struct if you specify all arguments + `:GoImpl r RequestReader io.Reader` + where `r` is the receiver, `RequestReader` is the struct and `io.Reader` is the interface + +simple example: +>go + type BytesReader struct{} + // ^ put your cursor here + // run `:GoImpl b io.Reader` + + // this is what you will get + func (b *BytesReader) Read(p []byte) (n int, err error) { + panic("not implemented") // TODO: Implement + } +< + + +============================================================================== +------------------------------------------------------------------------------ + *gopher.nvim-gotests* +gotests is utilizing the `gotests` tool to generate unit tests boilerplate. +Usage ~ + +- generate unit test for spesisfic function/method + - to specift the function/method put your cursor on it + - run `:GoTestAdd` + +- generate unit tests for all functions/methods in current file + - run `:GoTestsAll` + +- generate unit tests only for exported(public) functions/methods + - run `:GoTestsExp` + +you can also specify the template to use for generating the tests. see |gopher.nvim-config| +more details about templates can be found at: https://github.com/cweill/gotests + + +------------------------------------------------------------------------------ + *gopher.nvim-gotests-named* + +if you prefare using named tests, you can enable it in the config. +but you would need to install `gotests@develop` because stable version doesn't support this feature. +you can do it with: +>lua + -- simply run go get in your shell: + go install github.com/cweill/gotests/...@develop + + -- if you want to install it within neovim, you can use one of this: + + vim.fn.jobstart("go install github.com/cweill/gotests/...@develop") + + -- or if you want to use mason: + require("mason-tool-installer").setup { + ensure_installed = { + { "gotests", version = "develop" }, + } + } +< + +if you choose to install `gotests` within neovim, i recommend adding it to your `build` section in your |lazy.nvim| + + +============================================================================== +------------------------------------------------------------------------------ + *gopher.nvim-iferr* +if you're using `iferr` tool, this module provides a way to automatically insert `if err != nil` check. +Usage ~ +execute `:GoIfErr` near any err variable to insert the check + + +============================================================================== +------------------------------------------------------------------------------ + *gopher.nvim-comments* +Usage ~ +Execute `:GoCmt` to generate a comment for the current function/method/struct/etc on this line. +This module provides a way to generate comments for Go code. + + +============================================================================== +------------------------------------------------------------------------------ + *gopher.nvim-dap* +This module sets up `nvim-dap` for Go. +Usage ~ +just call `require("gopher.dap").setup()`, and you're good to go. + + + vim:tw=78:ts=8:noet:ft=help:norl: \ No newline at end of file diff --git a/lua/gopher/_utils/_health.lua b/lua/gopher/_utils/_health.lua deleted file mode 100644 index 5b12026..0000000 --- a/lua/gopher/_utils/_health.lua +++ /dev/null @@ -1,17 +0,0 @@ -return { - ---@param lib string - ---@return boolean - lualib_is_found = function(lib) - local is_found, _ = pcall(require, lib) - return is_found - end, - - ---@param bin string - ---@return boolean - binary_is_found = function(bin) - if vim.fn.executable(bin) == 1 then - return true - end - return false - end, -} diff --git a/lua/gopher/_utils/commands.lua b/lua/gopher/_utils/commands.lua deleted file mode 100644 index 33926a4..0000000 --- a/lua/gopher/_utils/commands.lua +++ /dev/null @@ -1,40 +0,0 @@ ----Run any go commands like `go generate`, `go get`, `go mod` ----@param cmd string ----@param ... string|string[] -return function(cmd, ...) - local Job = require "plenary.job" - local c = require("gopher.config").config.commands - local u = require "gopher._utils" - - local args = { ... } - if #args == 0 then - u.notify("please provice any arguments", "error") - return - end - - if cmd == "generate" and #args == 1 and args[1] == "%" then - args[1] = vim.fn.expand "%" ---@diagnostic disable-line: missing-parameter - elseif cmd == "get" then - for i, arg in ipairs(args) do - ---@diagnostic disable-next-line: param-type-mismatch - local m = string.match(arg, "^https://(.*)$") or string.match(arg, "^http://(.*)$") or arg - table.remove(args, i) - table.insert(args, i, m) - end - end - - local cmd_args = vim.list_extend({ cmd }, args) ---@diagnostic disable-line: missing-parameter - Job:new({ - command = c.go, - args = cmd_args, - on_exit = function(_, retval) - if retval ~= 0 then - u.notify("command 'go " .. unpack(cmd_args) .. "' exited with code " .. retval, "error") - u.notify(cmd .. " " .. unpack(cmd_args), "debug") - return - end - - u.notify("go " .. cmd .. " was success runned", "info") - end, - }):start() -end diff --git a/lua/gopher/_utils/health_util.lua b/lua/gopher/_utils/health_util.lua new file mode 100644 index 0000000..e1d44ee --- /dev/null +++ b/lua/gopher/_utils/health_util.lua @@ -0,0 +1,33 @@ +local h = vim.health or require "health" +local health = {} + +health.start = h.start or h.report_start +health.ok = h.ok or h.report_ok +health.warn = h.warn or h.report_warn +health.error = h.error or h.report_error +health.info = h.info or h.report_info + +---@param module string +---@return boolean +function health.is_lualib_found(module) + local is_found, _ = pcall(require, module) + return is_found +end + +---@param bin string +---@return boolean +function health.is_binary_found(bin) + if vim.fn.executable(bin) == 1 then + return true + end + return false +end + +---@param ft string +---@return boolean +function health.is_treesitter_parser_available(ft) + local ok, parser = pcall(vim.treesitter.get_parser, 0, ft) + return ok and parser ~= nil +end + +return health diff --git a/lua/gopher/_utils/init.lua b/lua/gopher/_utils/init.lua index 190574f..a3b567c 100644 --- a/lua/gopher/_utils/init.lua +++ b/lua/gopher/_utils/init.lua @@ -1,48 +1,34 @@ ----@diagnostic disable: param-type-mismatch -return { - ---@param t table - ---@return boolean - empty = function(t) - if t == nil then - return true - end +local c = require "gopher.config" +local log = require "gopher._utils.log" +local utils = {} - return next(t) == nil - end, +---@param msg string +---@param lvl number +function utils.deferred_notify(msg, lvl) + vim.defer_fn(function() + vim.notify(msg, lvl, { + title = c.___plugin_name, + }) + log.debug(msg) + end, 0) +end - ---@param s string - ---@return string - rtrim = function(s) - local n = #s - while n > 0 and s:find("^%s", n) do - n = n - 1 - end +---@param msg string +---@param lvl? number +function utils.notify(msg, lvl) + lvl = lvl or vim.log.levels.INFO + vim.notify(msg, lvl, { + title = c.___plugin_name, + }) + log.debug(msg) +end - return s:sub(1, n) - end, +-- safe require +---@param module string module name +function utils.sreq(module) + local ok, m = pcall(require, module) + assert(ok, string.format("gopher.nvim dependency error: %s not installed", module)) + return m +end - ---@param msg string - ---@param lvl string|integer - notify = function(msg, lvl) - local l - if lvl == "error" or lvl == 4 then - l = vim.log.levels.ERROR - elseif lvl == "info" or lvl == 2 then - l = vim.log.levels.INFO - elseif lvl == "debug" or lvl == 1 then - l = vim.log.levels.DEBUG - end - - vim.defer_fn(function() - vim.notify(msg, l) - end, 0) - end, - - ---safe require - ---@param name string module name - sreq = function(name) - local ok, m = pcall(require, name) - assert(ok, string.format("gopher.nvim dependency error: %s not installed", name)) - return m - end, -} +return utils diff --git a/lua/gopher/_utils/log.lua b/lua/gopher/_utils/log.lua new file mode 100644 index 0000000..c7dccd7 --- /dev/null +++ b/lua/gopher/_utils/log.lua @@ -0,0 +1,170 @@ +-- thanks https://github.com/tjdevries/vlog.nvim +-- and https://github.com/williamboman/mason.nvim +-- for the code i have stolen(or have inspected by idk) +local c = require "gopher.config" + +---@class Gopher.Logger +---@field get_outfile fun():string +---@field trace fun(...) +---@field fmt_trace fun(...) +---@field debug fun(...) +---@field fmt_debug fun(...) +---@field info fun(...) +---@field fmt_info fun(...) +---@field warn fun(...) +---@field fmt_warn fun(...) +---@field error fun(...) +---@field fmt_error fun(...) + +local config = { + -- Name of the plugin. Prepended to log messages + name = c.___plugin_name, + + -- Should print the output to neovim while running + -- values: 'sync','async',false + use_console = vim.env.GOPHER_VERBOSE_LOGS == "1", + + -- Should highlighting be used in console (using echohl) + highlights = true, + + -- Should write to a file + use_file = true, + + -- Level configuration + modes = { + { name = "trace", hl = "Comment", level = vim.log.levels.TRACE }, + { name = "debug", hl = "Comment", level = vim.log.levels.DEBUG }, + { name = "info", hl = "None", level = vim.log.levels.INFO }, + { name = "warn", hl = "WarningMsg", level = vim.log.levels.WARN }, + { name = "error", hl = "ErrorMsg", level = vim.log.levels.ERROR }, + }, + + -- Can limit the number of decimals displayed for floats + float_precision = 0.01, +} + +---@type Gopher.Logger +---@diagnostic disable-next-line: missing-fields +local log = {} + +---@return string +function log.get_outfile() + return table.concat { + (vim.fn.has "nvim-0.8.0" == 1) and vim.fn.stdpath "log" or vim.fn.stdpath "cache", + ("/%s.log"):format(config.name), + } +end + +-- selene: allow(incorrect_standard_library_use) +local unpack = unpack or table.unpack + +do + local round = function(x, increment) + increment = increment or 1 + x = x / increment + return (x > 0 and math.floor(x + 0.5) or math.ceil(x - 0.5)) * increment + end + + local tbl_has_tostring = function(tbl) + local mt = getmetatable(tbl) + return mt and mt.__tostring ~= nil + end + + local make_string = function(...) + local t = {} + for i = 1, select("#", ...) do + local x = select(i, ...) + + if type(x) == "number" and config.float_precision then + x = tostring(round(x, config.float_precision)) + elseif type(x) == "table" and not tbl_has_tostring(x) then + x = vim.inspect(x) + else + x = tostring(x) + end + + t[#t + 1] = x + end + return table.concat(t, " ") + end + + local log_at_level = function(level_config, message_maker, ...) + -- Return early if we're below the current_log_level + -- + -- the log level source get from config directly because otherwise it doesnt work + if level_config.level < c.log_level then + return + end + local nameupper = level_config.name:upper() + + local msg = message_maker(...) + local info = debug.getinfo(2, "Sl") + local lineinfo = info.short_src .. ":" .. info.currentline + + -- Output to console + if config.use_console then + local log_to_console = function() + local console_string = + string.format("[%-6s%s] %s: %s", nameupper, os.date "%H:%M:%S", lineinfo, msg) + + if config.highlights and level_config.hl then + vim.cmd(string.format("echohl %s", level_config.hl)) + end + + local split_console = vim.split(console_string, "\n") + for _, v in ipairs(split_console) do + local formatted_msg = string.format("[%s] %s", config.name, vim.fn.escape(v, [["\]])) + + ---@diagnostic disable-next-line: param-type-mismatch + local ok = pcall(vim.cmd, string.format([[echom "%s"]], formatted_msg)) + if not ok then + vim.api.nvim_out_write(msg .. "\n") + end + end + + if config.highlights and level_config.hl then + vim.cmd "echohl NONE" + end + end + if config.use_console == "sync" and not vim.in_fast_event() then + log_to_console() + else + vim.schedule(log_to_console) + end + end + + -- Output to log file + if config.use_file then + local fp = assert(io.open(log.get_outfile(), "a")) + local str = string.format("[%-6s%s] %s: %s\n", nameupper, os.date(), lineinfo, msg) + fp:write(str) + fp:close() + end + end + + for _, x in ipairs(config.modes) do + -- log.info("these", "are", "separated") + log[x.name] = function(...) ---@diagnostic disable-line: assign-type-mismatch + return log_at_level(x, make_string, ...) + end + + -- log.fmt_info("These are %s strings", "formatted") + log[("fmt_%s"):format(x.name)] = function(...) ---@diagnostic disable-line: assign-type-mismatch + return log_at_level(x, function(...) + local passed = { ... } + local fmt = table.remove(passed, 1) + local inspected = {} + for _, v in ipairs(passed) do + if type(v) == "table" and tbl_has_tostring(v) then + table.insert(inspected, v) + else + table.insert(inspected, vim.inspect(v)) + end + end + return string.format(fmt, unpack(inspected)) + end, ...) + end + end +end + +return log diff --git a/lua/gopher/_utils/runner/gocmd.lua b/lua/gopher/_utils/runner/gocmd.lua new file mode 100644 index 0000000..97323f9 --- /dev/null +++ b/lua/gopher/_utils/runner/gocmd.lua @@ -0,0 +1,53 @@ +local r = require "gopher._utils.runner" +local c = require("gopher.config").commands +local u = require "gopher._utils" +local gocmd = {} + +---@param args string[] +---@return string[] +local function if_get(args) + for i, arg in ipairs(args) do + local m = string.match(arg, "^https://(.*)$") or string.match(arg, "^http://(.*)$") or arg + table.remove(args, i) + table.insert(args, i, m) + end + return args +end + +---@param args unknown[] +---@return string[] +local function if_generate(args) + if #args == 1 and args[1] == "%" then + args[1] = vim.fn.expand "%" + end + return args +end + +---@param subcmd string +---@param args string[] +---@return string[]|nil +function gocmd.run(subcmd, args) + if #args == 0 then + error "please provice any arguments" + end + + if subcmd == "get" then + args = if_get(args) + end + + if subcmd == "generate" then + args = if_generate(args) + end + + return r.sync(c.go, { + args = { subcmd, unpack(args) }, + on_exit = function(data, status) + if status ~= 0 then + error("gocmd failed: " .. data) + end + u.notify(c.go .. " " .. subcmd .. " successful runned") + end, + }) +end + +return gocmd diff --git a/lua/gopher/_utils/runner/init.lua b/lua/gopher/_utils/runner/init.lua new file mode 100644 index 0000000..3c08f7f --- /dev/null +++ b/lua/gopher/_utils/runner/init.lua @@ -0,0 +1,33 @@ +local Job = require "plenary.job" +local runner = {} + +---@class gopher.RunnerOpts +---@field args? string[] +---@field cwd? string? +---@field on_exit? fun(data:string, status:number) + +---@param cmd string +---@param opts gopher.RunnerOpts +---@return string[]|nil +function runner.sync(cmd, opts) + local output + Job:new({ + command = cmd, + args = opts.args, + cwd = opts.cwd, + on_stderr = function(_, data) + vim.print(data) + end, + on_exit = function(data, status) + output = data:result() + vim.schedule(function() + if opts.on_exit then + opts.on_exit(output, status) + end + end) + end, + }):sync(60000 --[[1 min]]) + return output +end + +return runner diff --git a/lua/gopher/_utils/ts/init.lua b/lua/gopher/_utils/ts/init.lua index ee24f49..aba3a94 100644 --- a/lua/gopher/_utils/ts/init.lua +++ b/lua/gopher/_utils/ts/init.lua @@ -1,7 +1,7 @@ ---@diagnostic disable: param-type-mismatch local nodes = require "gopher._utils.ts.nodes" local u = require "gopher._utils" -local M = { +local ts = { querys = { struct_block = [[((type_declaration (type_spec name:(type_identifier) @struct.name type: (struct_type)))@struct.declaration)]], em_struct_block = [[(field_declaration name:(field_identifier)@struct.name type: (struct_type)) @struct.declaration]], @@ -27,14 +27,14 @@ end ---@param bufnr string|nil ---@param do_notify boolean|nil ---@return table|nil -function M.get_struct_node_at_pos(row, col, bufnr, do_notify) +function ts.get_struct_node_at_pos(row, col, bufnr, do_notify) local notify = do_notify or true - local query = M.querys.struct_block .. " " .. M.querys.em_struct_block + local query = ts.querys.struct_block .. " " .. ts.querys.em_struct_block local bufn = bufnr or vim.api.nvim_get_current_buf() local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) if ns == nil then if notify then - u.notify("struct not found", "warn") + u.deferred_notify("struct not found", vim.log.levels.WARN) end else return ns[#ns] @@ -46,14 +46,14 @@ end ---@param bufnr string|nil ---@param do_notify boolean|nil ---@return table|nil -function M.get_func_method_node_at_pos(row, col, bufnr, do_notify) +function ts.get_func_method_node_at_pos(row, col, bufnr, do_notify) local notify = do_notify or true - local query = M.querys.func .. " " .. M.querys.method_name + local query = ts.querys.func .. " " .. ts.querys.method_name local bufn = bufnr or vim.api.nvim_get_current_buf() local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) if ns == nil then if notify then - u.notify("function not found", "warn") + u.deferred_notify("function not found", vim.log.levels.WARN) end else return ns[#ns] @@ -65,16 +65,16 @@ end ---@param bufnr string|nil ---@param do_notify boolean|nil ---@return table|nil -function M.get_package_node_at_pos(row, col, bufnr, do_notify) +function ts.get_package_node_at_pos(row, col, bufnr, do_notify) local notify = do_notify or true -- stylua: ignore if row > 10 then return end - local query = M.querys.package + local query = ts.querys.package local bufn = bufnr or vim.api.nvim_get_current_buf() local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) if ns == nil then if notify then - u.notify("package not found", "warn") + u.deferred_notify("package not found", vim.log.levels.WARN) return nil end else @@ -87,18 +87,18 @@ end ---@param bufnr string|nil ---@param do_notify boolean|nil ---@return table|nil -function M.get_interface_node_at_pos(row, col, bufnr, do_notify) +function ts.get_interface_node_at_pos(row, col, bufnr, do_notify) local notify = do_notify or true - local query = M.querys.interface + local query = ts.querys.interface local bufn = bufnr or vim.api.nvim_get_current_buf() local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) if ns == nil then if notify then - u.notify("interface not found", "warn") + u.deferred_notify("interface not found", vim.log.levels.WARN) end else return ns[#ns] end end -return M +return ts diff --git a/lua/gopher/_utils/ts/nodes.lua b/lua/gopher/_utils/ts/nodes.lua index 87286a2..e9c730d 100644 --- a/lua/gopher/_utils/ts/nodes.lua +++ b/lua/gopher/_utils/ts/nodes.lua @@ -1,3 +1,7 @@ +local ts_query = require "nvim-treesitter.query" +local parsers = require "nvim-treesitter.parsers" +local locals = require "nvim-treesitter.locals" +local u = require "gopher._utils" local M = {} local function intersects(row, col, sRow, sCol, eRow, eCol) @@ -53,10 +57,6 @@ end ---@param pos_row string ---@return string function M.get_all_nodes(query, lang, _, bufnr, pos_row, _) - local ts_query = require "nvim-treesitter.query" - local parsers = require "nvim-treesitter.parsers" - local locals = require "nvim-treesitter.locals" - bufnr = bufnr or 0 pos_row = pos_row or 30000 @@ -113,8 +113,6 @@ end ---@param col string ---@return table function M.nodes_at_cursor(query, default, bufnr, row, col) - local u = require "gopher._utils" - bufnr = bufnr or vim.api.nvim_get_current_buf() local ft = vim.api.nvim_buf_get_option(bufnr, "ft") if row == nil or col == nil then @@ -123,13 +121,19 @@ function M.nodes_at_cursor(query, default, bufnr, row, col) local nodes = M.get_all_nodes(query, ft, default, bufnr, row, col) if nodes == nil then - u.notify("Unable to find any nodes. Place your cursor on a go symbol and try again", "debug") + u.deferred_notify( + "Unable to find any nodes. Place your cursor on a go symbol and try again", + vim.log.levels.DEBUG + ) return nil end nodes = M.sort_nodes(M.intersect_nodes(nodes, row, col)) if nodes == nil or #nodes == 0 then - u.notify("Unable to find any nodes at pos. " .. tostring(row) .. ":" .. tostring(col), "debug") + u.deferred_notify( + "Unable to find any nodes at pos. " .. tostring(row) .. ":" .. tostring(col), + vim.log.levels.DEBUG + ) return nil end diff --git a/lua/gopher/api.lua b/lua/gopher/api.lua deleted file mode 100644 index 6e854af..0000000 --- a/lua/gopher/api.lua +++ /dev/null @@ -1,29 +0,0 @@ -local API = {} -local tags = require "gopher.struct_tags" -local tests = require "gopher.gotests" -local cmd = require "gopher._utils.commands" - -API.install_deps = require "gopher.installer" -API.tags_add = tags.add -API.tags_rm = tags.remove -API.impl = require "gopher.impl" -API.iferr = require "gopher.iferr" -API.comment = require "gopher.comment" -API.test_add = tests.func_test -API.test_exported = tests.all_exported_tests -API.tests_all = tests.all_tests - -API.get = function(...) - cmd("get", ...) -end -API.mod = function(...) - cmd("mod", ...) -end -API.generate = function(...) - cmd("generate", ...) -end -API.work = function(...) - cmd("work", ...) -end - -return API diff --git a/lua/gopher/comment.lua b/lua/gopher/comment.lua index 0a146fb..8754405 100644 --- a/lua/gopher/comment.lua +++ b/lua/gopher/comment.lua @@ -1,3 +1,10 @@ +---@toc_entry Generate comments +---@tag gopher.nvim-comments +---@usage Execute `:GoCmt` to generate a comment for the current function/method/struct/etc on this line. +---@text This module provides a way to generate comments for Go code. + +local log = require "gopher._utils.log" + local function generate(row, col) local ts_utils = require "gopher._utils.ts" local comment, ns = nil, nil @@ -33,6 +40,8 @@ return function() local row, col = unpack(vim.api.nvim_win_get_cursor(0)) local comment, ns = generate(row + 1, col + 1) + log.debug("generated comment: " .. comment) + vim.api.nvim_win_set_cursor(0, { ns.dim.s.r, ns.dim.s.c, diff --git a/lua/gopher/config.lua b/lua/gopher/config.lua index a467f83..9c20843 100644 --- a/lua/gopher/config.lua +++ b/lua/gopher/config.lua @@ -1,33 +1,89 @@ ----@class Config ----@field commands ConfigCommands - ----@class ConfigCommands ----@field go string ----@field gomodifytags string ----@field gotests string ----@field impl string ----@field iferr string ----@field dlv string - -local M = { - ---@type Config - config = { - ---set custom commands for tools - commands = { - go = "go", - gomodifytags = "gomodifytags", - gotests = "gotests", - impl = "impl", - iferr = "iferr", - dlv = "dlv", - }, +---@toc_entry Configuration +---@tag gopher.nvim-config +---@text config it is the place where you can configure the plugin. +--- also this is optional is you're ok with default settings. +--- You can look at default options |gopher.nvim-config-defaults| + +---@type gopher.Config +---@private +local config = {} + +---@tag gopher.nvim-config.ConfigGoTagTransform +---@text Possible values for |gopher.Config|.gotag.transform: +--- +---@private +---@alias gopher.ConfigGoTagTransform +---| "snakecase" "GopherUser" -> "gopher_user" +---| "camelcase" "GopherUser" -> "gopherUser" +---| "lispcase" "GopherUser" -> "gopher-user" +---| "pascalcase" "GopherUser" -> "GopherUser" +---| "titlecase" "GopherUser" -> "Gopher User" +---| "keep" keeps the original field name + +--minidoc_replace_start { + +---@tag gopher.nvim-config-defaults +---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section):gsub(">", ">lua") +--- +---@class gopher.Config +local default_config = { + --minidoc_replace_end + + -- log level, you might consider using DEBUG or TRACE for degugging the plugin + ---@type number + log_level = vim.log.levels.INFO, + + -- user specified paths to binaries + ---@class gopher.ConfigCommand + commands = { + go = "go", + gomodifytags = "gomodifytags", + gotests = "gotests", + impl = "impl", + iferr = "iferr", + dlv = "dlv", + }, + ---@class gopher.ConfigGotests + gotests = { + -- gotests doesn't have template named "default" so this plugin uses "default" to set the default template + template = "default", + -- path to a directory containing custom test code templates + ---@type string|nil + template_dir = nil, + -- switch table tests from using slice to map (with test name for the key) + -- works only with gotests installed from develop branch + named = false, + }, + ---@class gopher.ConfigGoTag + gotag = { + ---@type gopher.ConfigGoTagTransform + transform = "snakecase", }, } +--minidoc_afterlines_end + +---@type gopher.Config +---@private +local _config = default_config ----Plugin setup function ----@param opts Config user config -function M.setup(opts) - M.config = vim.tbl_deep_extend("force", M.config, opts or {}) +-- I am kinda secret so don't tell anyone about me +-- even dont use me +-- +-- if you don't belive me that i am secret see +-- the line below it says @private +---@private +_config.___plugin_name = "gopher.nvim" ---@diagnostic disable-line: inject-field + +---@param user_config? gopher.Config +---@private +function config.setup(user_config) + _config = vim.tbl_deep_extend("force", default_config, user_config or {}) end -return M +setmetatable(config, { + __index = function(_, key) + return _config[key] + end, +}) + +return config diff --git a/lua/gopher/dap.lua b/lua/gopher/dap.lua new file mode 100644 index 0000000..9930318 --- /dev/null +++ b/lua/gopher/dap.lua @@ -0,0 +1,129 @@ +---@toc_entry Setup `nvim-dap` for Go +---@tag gopher.nvim-dap +---@text This module sets up `nvim-dap` for Go. +---@usage just call `require("gopher.dap").setup()`, and you're good to go. + +local c = require "gopher.config" +local dap = {} + +dap.adapter = function(callback, config) + local host = config.host or "127.0.0.1" + local port = config.port or "38697" + local addr = string.format("%s:%s", host, port) + + local handle, pid_or_err + local stdout = assert(vim.loop.new_pipe(false)) + local opts = { + stdio = { nil, stdout }, + args = { "dap", "-l", addr }, + detached = true, + } + + handle, pid_or_err = vim.loop.spawn(c.commands.dlv, opts, function(status) + if not stdout or not handle then + return + end + + stdout:close() + handle:close() + if status ~= 0 then + print("dlv exited with code", status) + end + end) + + assert(handle, "Error running dlv: " .. tostring(pid_or_err)) + if stdout then + stdout:read_start(function(err, chunk) + assert(not err, err) + if chunk then + vim.schedule(function() + require("dap.repl").append(chunk) + end) + end + end) + end + + -- wait for delve to start + vim.defer_fn(function() + callback { type = "server", host = "127.0.0.1", port = port } + end, 100) +end + +local function args_input() + vim.ui.input({ prompt = "Args: " }, function(input) + return vim.split(input or "", " ") + end) +end + +local function get_arguments() + local co = coroutine.running() + if co then + return coroutine.create(function() + local args = args_input() + coroutine.resume(co, args) + end) + else + return args_input() + end +end + +dap.configuration = { + { + type = "go", + name = "Debug", + request = "launch", + program = "${file}", + }, + { + type = "go", + name = "Debug (Arguments)", + request = "launch", + program = "${file}", + args = get_arguments, + }, + { + type = "go", + name = "Debug Package", + request = "launch", + program = "${fileDirname}", + }, + { + type = "go", + name = "Attach", + mode = "local", + request = "attach", + processId = require("dap.utils").pick_process, + }, + { + type = "go", + name = "Debug test", + request = "launch", + mode = "test", + program = "${file}", + }, + { + type = "go", + name = "Debug test (go.mod)", + request = "launch", + mode = "test", + program = "./${relativeFileDirname}", + }, +} + +-- sets ups nvim-dap for Go in one function call. +function dap.setup() + vim.deprecate( + "gopher.dap", + "you might consider setting up `nvim-dap` manually, or using another plugin(https://github.com/leoluz/nvim-dap-go)", + "v0.1.6", + "gopher" + ) + + local ok, d = pcall(require, "dap") + assert(ok, "gopher.nvim dependency error: dap not installed") + + d.adapters.go = dap.adapter + d.configurations.go = dap.configuration +end + +return dap diff --git a/lua/gopher/dap/config.lua b/lua/gopher/dap/config.lua deleted file mode 100644 index 80b2196..0000000 --- a/lua/gopher/dap/config.lua +++ /dev/null @@ -1,98 +0,0 @@ ----@diagnostic disable: param-type-mismatch -local function get_arguments() - local function get() - vim.ui.input({ prompt = "Args: " }, function(input) - return vim.split(input or "", " ") ---@diagnostic disable-line: missing-parameter - end) - end - - local co = coroutine.running() - if co then - return coroutine.create(function() - local args = get() - coroutine.resume(co, args) - end) - else - return get() - end -end - -return { - adapter = function(callback, config) - local handle, pid_or_err - local stdout = vim.loop.new_pipe(false) - local host = config.host or "127.0.0.1" - local port = config.port or "38697" - local addr = string.format("%s:%s", host, port) - local opts = { - stdio = { nil, stdout }, - args = { "dap", "-l", addr }, - detached = true, - } - - handle, pid_or_err = vim.loop.spawn("dlv", opts, function(code) - stdout:close() - handle:close() - if code ~= 0 then - print("dlv exited with code", code) - end - end) - - assert(handle, "Error running dlv: " .. tostring(pid_or_err)) - stdout:read_start(function(err, chunk) - assert(not err, err) - if chunk then - vim.schedule(function() - require("dap.repl").append(chunk) - end) - end - end) - - -- Wait for delve to start - vim.defer_fn(function() - callback { type = "server", host = "127.0.0.1", port = port } - end, 100) - end, - configuration = { - { - type = "go", - name = "Debug", - request = "launch", - program = "${file}", - }, - { - type = "go", - name = "Debug (Arguments)", - request = "launch", - program = "${file}", - args = get_arguments, - }, - { - type = "go", - name = "Debug Package", - request = "launch", - program = "${fileDirname}", - }, - { - type = "go", - name = "Attach", - mode = "local", - request = "attach", - processId = require("dap.utils").pick_process, - }, - { - type = "go", - name = "Debug test", - request = "launch", - mode = "test", - program = "${file}", - }, - { - type = "go", - name = "Debug test (go.mod)", - request = "launch", - mode = "test", - program = "./${relativeFileDirname}", - }, - }, -} diff --git a/lua/gopher/dap/init.lua b/lua/gopher/dap/init.lua deleted file mode 100644 index 9b0b626..0000000 --- a/lua/gopher/dap/init.lua +++ /dev/null @@ -1,13 +0,0 @@ -local M = {} - ----setup nvim-dap for golang using -function M.setup() - local cfg = require "gopher.dap.config" - local u = require "gopher._utils" - - local dap = u.sreq "dap" - dap.adapters.go = cfg.adapter - dap.configurations.go = cfg.configuration -end - -return M diff --git a/lua/gopher/gotests.lua b/lua/gopher/gotests.lua index 724971c..da4753d 100644 --- a/lua/gopher/gotests.lua +++ b/lua/gopher/gotests.lua @@ -1,73 +1,104 @@ -local u = require "gopher._utils" -local M = {} - ----@param cmd_args table -local function run(cmd_args) - local Job = require "plenary.job" - local c = require("gopher.config").config.commands +---@toc_entry Generating unit tests boilerplate +---@tag gopher.nvim-gotests +---@text gotests is utilizing the `gotests` tool to generate unit tests boilerplate. +---@usage +--- - generate unit test for spesisfic function/method +--- - to specift the function/method put your cursor on it +--- - run `:GoTestAdd` +--- +--- - generate unit tests for all functions/methods in current file +--- - run `:GoTestsAll` +--- +--- - generate unit tests only for exported(public) functions/methods +--- - run `:GoTestsExp` +--- +--- you can also specify the template to use for generating the tests. see |gopher.nvim-config| +--- more details about templates can be found at: https://github.com/cweill/gotests +--- - Job:new({ - command = c.gotests, - args = cmd_args, - on_exit = function(_, retval) - if retval ~= 0 then - u.notify("command 'go " .. unpack(cmd_args) .. "' exited with code " .. retval, "error") - return - end +---@tag gopher.nvim-gotests-named +---@text +--- if you prefare using named tests, you can enable it in the config. +--- but you would need to install `gotests@develop` because stable version doesn't support this feature. +--- you can do it with: +--- >lua +--- -- simply run go get in your shell: +--- go install github.com/cweill/gotests/...@develop +--- +--- -- if you want to install it within neovim, you can use one of this: +--- +--- vim.fn.jobstart("go install github.com/cweill/gotests/...@develop") +--- +--- -- or if you want to use mason: +--- require("mason-tool-installer").setup { +--- ensure_installed = { +--- { "gotests", version = "develop" }, +--- } +--- } +--- < +--- +--- if you choose to install `gotests` within neovim, i recommend adding it to your `build` section in your |lazy.nvim| - u.notify("unit test(s) generated", "info") - end, - }):start() -end +local c = require "gopher.config" +local ts_utils = require "gopher._utils.ts" +local r = require "gopher._utils.runner" +local u = require "gopher._utils" +local log = require "gopher._utils.log" +local gotests = {} ---@param args table +---@private local function add_test(args) - local fpath = vim.fn.expand "%" ---@diagnostic disable-line: missing-parameter - table.insert(args, "-w") - table.insert(args, fpath) - run(args) -end - ----generate unit test for one function ----@param parallel boolean -function M.func_test(parallel) - local ts_utils = require "gopher._utils.ts" + if c.gotests.named then + table.insert(args, "-named") + end - local ns = ts_utils.get_func_method_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0))) - if ns == nil or ns.name == nil then - u.notify("cursor on func/method and execute the command again", "info") - return + if c.gotests.template_dir then + table.insert(args, "-template_dir") + table.insert(args, c.gotests.template_dir) end - local cmd_args = { "-only", ns.name } - if parallel then - table.insert(cmd_args, "-parallel") + if c.gotests.template ~= "default" then + table.insert(args, "-template") + table.insert(args, c.gotests.template) end - add_test(cmd_args) + table.insert(args, "-w") + table.insert(args, vim.fn.expand "%") + + log.debug("generating tests with args: ", args) + + return r.sync(c.commands.gotests, { + args = args, + on_exit = function(data, status) + if not status == 0 then + error("gotests failed: " .. data) + end + + u.notify "unit test(s) generated" + end, + }) end ----generate unit tests for all functions in current file ----@param parallel boolean -function M.all_tests(parallel) - local cmd_args = { "-all" } - if parallel then - table.insert(cmd_args, "-parallel") +-- generate unit test for one function +function gotests.func_test() + local ns = ts_utils.get_func_method_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0))) + if ns == nil or ns.name == nil then + u.notify("cursor on func/method and execute the command again", vim.log.levels.WARN) + return end - add_test(cmd_args) + add_test { "-only", ns.name } end ----generate unit tests for all exported functions ----@param parallel boolean -function M.all_exported_tests(parallel) - local cmd_args = {} - if parallel then - table.insert(cmd_args, "-parallel") - end +-- generate unit tests for all functions in current file +function gotests.all_tests() + add_test { "-all" } +end - table.insert(cmd_args, "-exported") - add_test(cmd_args) +-- generate unit tests for all exported functions +function gotests.all_exported_tests() + add_test { "-exported" } end -return M +return gotests diff --git a/lua/gopher/health.lua b/lua/gopher/health.lua index 17d76ec..633a184 100644 --- a/lua/gopher/health.lua +++ b/lua/gopher/health.lua @@ -1,44 +1,70 @@ -local c = require("gopher.config").config.commands +local health = {} +local cmd = require("gopher.config").commands +local u = require "gopher._utils.health_util" -local requried_for_work_msg = "Gopher.nvim will not work without it!" -local M = { - _required = { - plugins = { - { lib = "plenary", help = requried_for_work_msg }, - { lib = "nvim-treesitter", help = requried_for_work_msg }, - { lib = "dap", help = "Required for set upping debugger" }, +local deps = { + plugin = { + { lib = "dap", msg = "required for `gopher.dap`", optional = true }, + { lib = "plenary", msg = "required for everyting in gopher.nvim", optional = false }, + { lib = "nvim-treesitter", msg = "required for everyting in gopher.nvim", optional = false }, + }, + bin = { + { + bin = cmd.go, + msg = "required for `:GoGet`, `:GoMod`, `:GoGenerate`, `:GoWork`, `:GoInstallDeps`", + optional = false, }, - binarys = { - { bin = c.go, help = "required for GoMod, GoGet, GoGenerate command" }, - { bin = c.gomodifytags, help = "required for modify struct tags" }, - { bin = c.impl, help = "required for interface implementing" }, - { bin = c.gotests, help = "required for test(s) generation" }, - { bin = c.dlv, help = "required for debugger(nvim-dap)" }, + { bin = cmd.gomodifytags, msg = "required for `:GoTagAdd`, `:GoTagRm`", optional = false }, + { bin = cmd.impl, msg = "required for `:GoImpl`", optional = false }, + { bin = cmd.iferr, msg = "required for `:GoIfErr`", optional = false }, + { + bin = cmd.gotests, + msg = "required for `:GoTestAdd`, `:GoTestsAll`, `:GoTestsExp`", + optional = false, }, + { bin = cmd.dlv, msg = "required for debugging, (`nvim-dap`, `gopher.dap`)", optional = true }, + }, + treesitter = { + { parser = "go", msg = "required for `gopher.nvim`", optional = false }, }, } -function M.check() - local health = vim.health or require "health" - local u = require "gopher._utils._health" +function health.check() + u.start "required plugins" + for _, plugin in ipairs(deps.plugin) do + if u.is_lualib_found(plugin.lib) then + u.ok(plugin.lib .. " installed") + else + if plugin.optional then + u.warn(plugin.lib .. " not found, " .. plugin.msg) + else + u.error(plugin.lib .. " not found, " .. plugin.msg) + end + end + end - health.report_start "Required plugins" - for _, plugin in ipairs(M._required.plugins) do - if u.lualib_is_found(plugin.lib) then - health.report_ok(plugin.lib .. " installed.") + u.start "required binaries" + u.info "all those binaries can be installed by `:GoInstallDeps`" + for _, bin in ipairs(deps.bin) do + if u.is_binary_found(bin.bin) then + u.ok(bin.bin .. " installed") else - health.report_error(plugin.lib .. " not found. " .. plugin.help) + if bin.optional then + u.warn(bin.bin .. " not found, " .. bin.msg) + else + u.error(bin.bin .. " not found, " .. bin.msg) + end end end - health.report_start "Required go tools" - for _, binary in ipairs(M._required.binarys) do - if u.binary_is_found(binary.bin) then - health.report_ok(binary.bin .. " installed") + u.start "required treesitter parsers" + for _, parser in ipairs(deps.treesitter) do + if u.is_treesitter_parser_available(parser.parser) then + u.ok(parser.parser .. " parser installed") else - health.report_warn(binary.bin .. " is not installed but " .. binary.help) + u.error(parser.parser .. " parser not found, " .. parser.msg) end end end -return M +return health diff --git a/lua/gopher/iferr.lua b/lua/gopher/iferr.lua index 7f43318..a1cdf9f 100644 --- a/lua/gopher/iferr.lua +++ b/lua/gopher/iferr.lua @@ -1,21 +1,26 @@ ----Add iferr declaration ----That's Lua of vimscript implementation of: ----github.com/koron/iferr -return function() - local c = require("gopher.config").config.commands - local u = require "gopher._utils" +---@toc_entry Iferr +---@tag gopher.nvim-iferr +---@text if you're using `iferr` tool, this module provides a way to automatically insert `if err != nil` check. +---@usage execute `:GoIfErr` near any err variable to insert the check +local c = require "gopher.config" +local log = require "gopher._utils.log" +local iferr = {} + +-- That's Lua implementation: github.com/koron/iferr +function iferr.iferr() local boff = vim.fn.wordcount().cursor_bytes - local cmd = (c.iferr .. " -pos " .. boff) - local data = vim.fn.systemlist(cmd, vim.fn.bufnr "%") + local pos = vim.fn.getcurpos()[2] + local data = vim.fn.systemlist((c.commands.iferr .. " -pos " .. boff), vim.fn.bufnr "%") if vim.v.shell_error ~= 0 then - u.notify("command " .. cmd .. " exited with code " .. vim.v.shell_error, "error") - return + error("iferr failed: " .. data) + log.error("failed. output: " .. data) end - local pos = vim.fn.getcurpos()[2] vim.fn.append(pos, data) vim.cmd [[silent normal! j=2j]] vim.fn.setpos(".", pos) end + +return iferr diff --git a/lua/gopher/impl.lua b/lua/gopher/impl.lua index a279889..f461376 100644 --- a/lua/gopher/impl.lua +++ b/lua/gopher/impl.lua @@ -1,12 +1,43 @@ +---@toc_entry Auto implementation of interface methods +---@tag gopher.nvim-impl +---@text impl is utilizing the `impl` tool to generate method stubs for interfaces. +---@usage +--- 1. put your coursor on the struct on which you want implement the interface +--- and run `:GoImpl io.Reader` +--- which will automatically choose the reciver for the methods and +--- implement the `io.Reader` interface +--- 2. same as previous but with custom receiver, so put your coursor on the struct +--- run `:GoImpl w io.Writer` +--- where `w` is the receiver and `io.Writer` is the interface +--- 3. specift receiver, struct, and interface +--- there's no need to put your coursor on the struct if you specify all arguments +--- `:GoImpl r RequestReader io.Reader` +--- where `r` is the receiver, `RequestReader` is the struct and `io.Reader` is the interface +--- +--- simple example: +--- >go +--- type BytesReader struct{} +--- // ^ put your cursor here +--- // run `:GoImpl b io.Reader` +--- +--- // this is what you will get +--- func (b *BytesReader) Read(p []byte) (n int, err error) { +--- panic("not implemented") // TODO: Implement +--- } +--- < + +local c = require("gopher.config").commands +local r = require "gopher._utils.runner" +local ts_utils = require "gopher._utils.ts" local u = require "gopher._utils" +local impl = {} ---@return string +---@private local function get_struct() - local ts_utils = require "gopher._utils.ts" - local ns = ts_utils.get_struct_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0))) if ns == nil then - u.notify("put cursor on a struct or specify a receiver", "info") + u.deferred_notify("put cursor on a struct or specify a receiver", vim.log.levels.INFO) return "" end @@ -18,10 +49,7 @@ local function get_struct() return ns.name end -return function(...) - local c = require("gopher.config").config.commands - local Job = require "plenary.job" - +function impl.impl(...) local args = { ... } local iface, recv_name = "", "" local recv = get_struct() @@ -30,7 +58,7 @@ return function(...) iface = vim.fn.input "impl: generating method stubs for interface: " vim.cmd "redraw!" if iface == "" then - u.notify("usage: GoImpl f *File io.Reader", "info") + u.deferred_notify("usage: GoImpl f *File io.Reader", vim.log.levels.INFO) return end elseif #args == 1 then -- :GoImpl io.Reader @@ -48,28 +76,23 @@ return function(...) recv = string.format("%s %s", recv_name, recv) end - -- stylua: ignore - local cmd_args = { - "-dir", vim.fn.fnameescape(vim.fn.expand "%:p:h"), ---@diagnostic disable-line: missing-parameter - recv, - iface - } - - local res_data - Job:new({ - command = c.impl, - args = cmd_args, - on_exit = function(data, retval) - if retval ~= 0 then - u.notify("command 'impl " .. unpack(cmd_args) .. "' exited with code " .. retval, "error") - return + local output = r.sync(c.impl, { + args = { + "-dir", + vim.fn.fnameescape(vim.fn.expand "%:p:h" --[[@as string]]), + recv, + iface, + }, + on_exit = function(data, status) + if not status == 0 then + error("impl failed: " .. data) end - - res_data = data:result() end, - }):sync() + }) local pos = vim.fn.getcurpos()[2] - table.insert(res_data, 1, "") - vim.fn.append(pos, res_data) + table.insert(output, 1, "") + vim.fn.append(pos, output) end + +return impl diff --git a/lua/gopher/init.lua b/lua/gopher/init.lua index 46b649e..2aa138e 100644 --- a/lua/gopher/init.lua +++ b/lua/gopher/init.lua @@ -1,5 +1,70 @@ -local GOPHER = {} +--- *gopher.nvim* +--- +--- ============================================================================== +--- +--- gopher.nvim is a minimalistic plugin for Go development in Neovim written in Lua. +--- It's not an LSP tool, the main goal of this plugin is add go tooling support in Neovim. -GOPHER.setup = require("gopher.config").setup +--- Table of Contents +---@tag gopher.nvim-table-of-contents +---@toc -return GOPHER +local log = require "gopher._utils.log" +local tags = require "gopher.struct_tags" +local tests = require "gopher.gotests" +local gocmd = require("gopher._utils.runner.gocmd").run +local gopher = {} + +---@toc_entry Setup +---@tag gopher.nvim-setup +---@text Setup function. This method simply merges default configs with opts table. +--- You can read more about configuration at |gopher.nvim-config| +--- Calling this function is optional, if you ok with default settings. Look |gopher.nvim.config-defaults| +--- +---@usage `require("gopher").setup {}` (replace `{}` with your `config` table) +---@param user_config gopher.Config +gopher.setup = function(user_config) + log.debug "setting up config" + require("gopher.config").setup(user_config) + log.debug(vim.inspect(user_config)) +end + +---@toc_entry Install dependencies +---@tag gopher.nvim-install-deps +---@text Gopher.nvim implements most of its features using third-party tools. +--- To install these tools, you can run `:GoInstallDeps` command +--- or call `require("gopher").install_deps()` if you want ues lua api. +gopher.install_deps = require("gopher.installer").install_deps + +gopher.impl = require("gopher.impl").impl +gopher.iferr = require("gopher.iferr").iferr +gopher.comment = require "gopher.comment" + +gopher.tags = { + add = tags.add, + rm = tags.remove, +} + +gopher.test = { + add = tests.func_test, + exported = tests.all_exported_tests, + all = tests.all_tests, +} + +gopher.get = function(...) + gocmd("get", { ... }) +end + +gopher.mod = function(...) + gocmd("mod", { ... }) +end + +gopher.generate = function(...) + gocmd("generate", { ... }) +end + +gopher.work = function(...) + gocmd("work", { ... }) +end + +return gopher diff --git a/lua/gopher/installer.lua b/lua/gopher/installer.lua index fdd6242..2994b8a 100644 --- a/lua/gopher/installer.lua +++ b/lua/gopher/installer.lua @@ -1,3 +1,8 @@ +local c = require("gopher.config").commands +local r = require "gopher._utils.runner" +local u = require "gopher._utils" +local installer = {} + local urls = { gomodifytags = "github.com/fatih/gomodifytags", impl = "github.com/josharian/impl", @@ -8,28 +13,24 @@ local urls = { ---@param pkg string local function install(pkg) - local Job = require "plenary.job" - local u = require "gopher._utils" - local url = urls[pkg] .. "@latest" - - Job:new({ - command = "go", + r.sync(c.go, { args = { "install", url }, - on_exit = function(_, retval) - if retval ~= 0 then - u.notify("command 'go install " .. url .. "' exited with code " .. retval, "error") + on_exit = function(data, status) + if not status == 0 then + error("go install failed: " .. data) return end - - u.notify("install " .. url .. " finished", "info ") + u.notify("installed: " .. url) end, - }):start() + }) end ---Install required go deps -return function() +function installer.install_deps() for pkg, _ in pairs(urls) do install(pkg) end end + +return installer diff --git a/lua/gopher/struct_tags.lua b/lua/gopher/struct_tags.lua index a91901a..4389b62 100644 --- a/lua/gopher/struct_tags.lua +++ b/lua/gopher/struct_tags.lua @@ -1,11 +1,35 @@ -local M = {} +---@toc_entry Modifty struct tags +---@tag gopher.nvim-struct-tags +---@text struct-tags is utilizing the `gomodifytags` tool to add or remove tags to struct fields. +---@usage - put your coursor on the struct +--- - run `:GoTagAdd json` to add json tags to struct fields +--- - run `:GoTagRm json` to remove json tags to struct fields +--- +--- note: if you dont spesify the tag it will use `json` as default +--- +--- simple example: +--- >go +--- // before +--- type User struct { +--- // ^ put your cursor here +--- // run `:GoTagAdd yaml` +--- ID int +--- Name string +--- } +--- +--- // after +--- type User struct { +--- ID int `yaml:id` +--- Name string `yaml:name` +--- } +--- < + +local ts_utils = require "gopher._utils.ts" +local r = require "gopher._utils.runner" +local c = require "gopher.config" +local struct_tags = {} local function modify(...) - local ts_utils = require "gopher._utils.ts" - local Job = require "plenary.job" - local c = require("gopher.config").config.commands - local u = require "gopher._utils" - local fpath = vim.fn.expand "%" ---@diagnostic disable-line: missing-parameter local ns = ts_utils.get_struct_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0))) if ns == nil then @@ -14,6 +38,7 @@ local function modify(...) -- stylua: ignore local cmd_args = { + "-transform", c.gotag.transform, "-format", "json", "-file", fpath, "-w" @@ -40,40 +65,26 @@ local function modify(...) table.insert(cmd_args, "json") end - -- get result of "gomodifytags" works - local res_data - Job:new({ - command = c.gomodifytags, + local output = r.sync(c.commands.gomodifytags, { args = cmd_args, - on_exit = function(data, retval) - if retval ~= 0 then - u.notify( - "command 'gomodifytags " .. unpack(cmd_args) .. "' exited with code " .. retval, - "error" - ) - return + on_exit = function(data, status) + if not status == 0 then + error("gotag failed: " .. data) end - - res_data = data:result() end, - }):sync() + }) -- decode goted value - local tagged = vim.json.decode(table.concat(res_data)) + local tagged = vim.json.decode(table.concat(output)) if tagged.errors ~= nil or tagged.lines == nil or tagged["start"] == nil or tagged["start"] == 0 then - u.notify("failed to set tags " .. vim.inspect(tagged), "error") - end - - for i, v in ipairs(tagged.lines) do - tagged.lines[i] = u.rtrim(v) + error("failed to set tags " .. vim.inspect(tagged)) end - -- write goted tags vim.api.nvim_buf_set_lines( 0, tagged.start - 1, @@ -84,9 +95,8 @@ local function modify(...) vim.cmd "write" end ----add tags to struct under cursor ----@param ... unknown -function M.add(...) +-- add tags to struct under cursor +function struct_tags.add(...) local arg = { ... } if #arg == nil or arg == "" then arg = { "json" } @@ -100,9 +110,8 @@ function M.add(...) modify(unpack(cmd_args)) end ----remove tags to struct under cursor ----@param ... unknown -function M.remove(...) +-- remove tags to struct under cursor +function struct_tags.remove(...) local arg = { ... } if #arg == nil or arg == "" then arg = { "json" } @@ -116,4 +125,4 @@ function M.remove(...) modify(unpack(cmd_args)) end -return M +return struct_tags diff --git a/plugin/gopher.vim b/plugin/gopher.vim index e797966..a219a1c 100644 --- a/plugin/gopher.vim +++ b/plugin/gopher.vim @@ -1,13 +1,14 @@ -command! -nargs=* GoTagAdd :lua require"gopher.api".tags_add() -command! -nargs=* GoTagRm :lua require"gopher.api".tags_rm() -command! -nargs=* GoTestAdd :lua require"gopher.api".test_add() -command! -nargs=* GoTestsAll :lua require"gopher.api".tests_all() -command! -nargs=* GoTestsExp :lua require"gopher.api".test_exported() -command! -nargs=* GoMod :lua require"gopher.api".mod() -command! -nargs=* GoGet :lua require"gopher.api".get() -command! -nargs=* GoWork :lua require"gopher.api".work() -command! -nargs=* GoImpl :lua require"gopher.api".impl() -command! -nargs=* GoGenerate :lua require"gopher.api".generate() -command! GoCmt :lua require"gopher.api".comment() -command! GoIfErr :lua require"gopher.api".iferr() -command! GoInstallDeps :lua require"gopher.api".install_deps() +command! -nargs=* GoTagAdd :lua require"gopher".tags.add() +command! -nargs=* GoTagRm :lua require"gopher".tags.rm() +command! GoTestAdd :lua require"gopher".test.add() +command! GoTestsAll :lua require"gopher".test.all() +command! GoTestsExp :lua require"gopher".test.exported() +command! -nargs=* GoMod :lua require"gopher".mod() +command! -nargs=* GoGet :lua require"gopher".get() +command! -nargs=* GoWork :lua require"gopher".work() +command! -nargs=* GoImpl :lua require"gopher".impl() +command! -nargs=* GoGenerate :lua require"gopher".generate() +command! GoCmt :lua require"gopher".comment() +command! GoIfErr :lua require"gopher".iferr() +command! GoInstallDeps :lua require"gopher".install_deps() +command! GopherLog :lua vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile()) diff --git a/scripts/docgen.lua b/scripts/docgen.lua new file mode 100644 index 0000000..f2deb8b --- /dev/null +++ b/scripts/docgen.lua @@ -0,0 +1,33 @@ +---@diagnostic disable: undefined-global +--# selene: allow(undefined_variable) + +local okay, minidoc = pcall(require, "mini.doc") +if not okay then + error "mini.doc not found, please install it. https://github.com/echasnovski/mini.doc" + return +end + +local files = { + "lua/gopher/init.lua", + "lua/gopher/config.lua", + "lua/gopher/struct_tags.lua", + "lua/gopher/impl.lua", + "lua/gopher/gotests.lua", + "lua/gopher/iferr.lua", + "lua/gopher/comment.lua", + "lua/gopher/dap.lua", +} + +minidoc.setup() + +local hooks = vim.deepcopy(minidoc.default_hooks) +hooks.write_pre = function(lines) + -- Remove first two lines with `======` and `------` delimiters to comply + -- with `:h local-additions` template + table.remove(lines, 1) + table.remove(lines, 1) + + return lines +end + +MiniDoc.generate(files, "doc/gopher.nvim.txt", { hooks = hooks }) diff --git a/scripts/minimal_init.lua b/scripts/minimal_init.lua new file mode 100644 index 0000000..49a606e --- /dev/null +++ b/scripts/minimal_init.lua @@ -0,0 +1,34 @@ +local function root(p) + local f = debug.getinfo(1, "S").source:sub(2) + return vim.fn.fnamemodify(f, ":p:h:h") .. "/" .. (p or "") +end + +local function install_plug(plugin) + local name = plugin:match ".*/(.*)" + local package_root = root ".tests/site/pack/deps/start/" + if not vim.loop.fs_stat(package_root .. name) then + print("Installing " .. plugin) + vim.fn.mkdir(package_root, "p") + vim.fn.system { + "git", + "clone", + "--depth=1", + "https://github.com/" .. plugin .. ".git", + package_root .. "/" .. name, + } + end +end + +vim.cmd [[set runtimepath=$VIMRUNTIME]] +vim.opt.runtimepath:append(root()) +vim.opt.packpath = { root ".tests/site" } +vim.notify = print + +install_plug "nvim-lua/plenary.nvim" +install_plug "nvim-treesitter/nvim-treesitter" +install_plug "echasnovski/mini.doc" -- used for docs generation + +vim.env.XDG_CONFIG_HOME = root ".tests/config" +vim.env.XDG_DATA_HOME = root ".tests/data" +vim.env.XDG_STATE_HOME = root ".tests/state" +vim.env.XDG_CACHE_HOME = root ".tests/cache" diff --git a/selene.toml b/selene.toml index 8117799..9f6f1c4 100644 --- a/selene.toml +++ b/selene.toml @@ -1 +1,2 @@ -std="nvim+lua51" +std = "nvim+lua52" +exclude = [".tests/*"] diff --git a/spec/gopher_config_spec.lua b/spec/gopher_config_spec.lua deleted file mode 100644 index 1d033c0..0000000 --- a/spec/gopher_config_spec.lua +++ /dev/null @@ -1,41 +0,0 @@ -describe("gopher.config", function() - it("can be required", function() - require "gopher.config" - end) - - it(".setup() when gets empty table not edit config", function() - local c = require "gopher.config" - c.setup {} - - assert.are.same(c.config.commands.go, "go") - assert.are.same(c.config.commands.gomodifytags, "gomodifytags") - assert.are.same(c.config.commands.gotests, "gotests") - assert.are.same(c.config.commands.impl, "impl") - end) - - it(".setup() when get one custom value sets that", function() - local c = require "gopher.config" - c.setup { commands = { - go = "custom_go", - } } - - assert.are.same(c.config.commands.go, "custom_go") - end) - - it(".setup() when get all custom values sets it", function() - local c = require "gopher.config" - c.setup { - commands = { - go = "go1.18", - gomodifytags = "user-gomodifytags", - gotests = "gotests", - impl = "goimpl", - }, - } - - assert.are.same(c.config.commands.go, "go1.18") - assert.are.same(c.config.commands.gomodifytags, "user-gomodifytags") - assert.are.same(c.config.commands.gotests, "gotests") - assert.are.same(c.config.commands.impl, "goimpl") - end) -end) diff --git a/spec/gopher_spec.lua b/spec/gopher_spec.lua deleted file mode 100644 index b50b5ea..0000000 --- a/spec/gopher_spec.lua +++ /dev/null @@ -1,5 +0,0 @@ -describe("gopher", function() - it("can be required", function() - require "gopher" - end) -end) diff --git a/spec/gopher_struct_tags_spec.lua b/spec/gopher_struct_tags_spec.lua deleted file mode 100644 index ad8dd6e..0000000 --- a/spec/gopher_struct_tags_spec.lua +++ /dev/null @@ -1,49 +0,0 @@ -local cur_dir = vim.fn.expand "%:p:h" - -describe("gopher.struct_tags", function() - it("can be required", function() - require "gopher.struct_tags" - end) - - it("can add json tag to struct", function() - local tag = require "gopher.struct_tags" - local temp_file = vim.fn.tempname() .. ".go" - local input_file = vim.fn.readfile(cur_dir .. "/spec/fixtures/tags/add_input.go") - local output_file = - vim.fn.join(vim.fn.readfile(cur_dir .. "/spec/fixtures/tags/add_output.go"), "\n") - - vim.fn.writefile(input_file, temp_file) - vim.cmd("silent exe 'e " .. temp_file .. "'") - vim.bo.filetype = "go" - - local bufn = vim.fn.bufnr(0) - vim.fn.setpos(".", { bufn, 3, 6, 0 }) - tag.add() - - vim.wait(100) - assert.are.same(output_file, vim.fn.join(vim.fn.readfile(temp_file), "\n")) - - vim.cmd("bd! " .. temp_file) - end) - - it("can remove json tag from struct", function() - local tag = require "gopher.struct_tags" - local temp_file = vim.fn.tempname() .. ".go" - local input_file = vim.fn.readfile(cur_dir .. "/spec/fixtures/tags/remove_input.go") - local output_file = - vim.fn.join(vim.fn.readfile(cur_dir .. "/spec/fixtures/tags/remove_output.go"), "\n") - - vim.fn.writefile(input_file, temp_file) - vim.cmd("silent exe 'e " .. temp_file .. "'") - vim.bo.filetype = "go" - - local bufn = vim.fn.bufnr() - vim.fn.setpos(".", { bufn, 3, 6, 0 }) - tag.remove() - - vim.wait(100) - assert.are.same(output_file, vim.fn.join(vim.fn.readfile(temp_file), "\n")) - - vim.cmd("bd! " .. temp_file) - end) -end) diff --git a/spec/gopher_utils_spec.lua b/spec/gopher_utils_spec.lua deleted file mode 100644 index f052cff..0000000 --- a/spec/gopher_utils_spec.lua +++ /dev/null @@ -1,19 +0,0 @@ -describe("gopher._utils", function() - it("can be requried", function() - require "gopher._utils" - end) - - it(".empty() with non-empty talbe", function() - local empty = require("gopher._utils").empty - local res = empty { first = "1", second = 2 } - - assert.are.same(res, false) - end) - - it(".empty() with empty talbe", function() - local empty = require("gopher._utils").empty - local res = empty {} - - assert.are.same(res, true) - end) -end) diff --git a/spec/minimal_init.vim b/spec/minimal_init.vim deleted file mode 100644 index e30e21b..0000000 --- a/spec/minimal_init.vim +++ /dev/null @@ -1,4 +0,0 @@ -set rtp+=. -packadd plenary.nvim -packadd nvim-treesitter -packadd nvim-dap diff --git a/spec/units/config_spec.lua b/spec/units/config_spec.lua new file mode 100644 index 0000000..1fd91dd --- /dev/null +++ b/spec/units/config_spec.lua @@ -0,0 +1,29 @@ +describe("gopher.config", function() + it(".setup() should provide default when .setup() is not called", function() + local c = require "gopher.config" + + assert.are.same(c.commands.go, "go") + assert.are.same(c.commands.gomodifytags, "gomodifytags") + assert.are.same(c.commands.gotests, "gotests") + assert.are.same(c.commands.impl, "impl") + assert.are.same(c.commands.iferr, "iferr") + assert.are.same(c.commands.dlv, "dlv") + end) + + it(".setup() should change options on users config", function() + local c = require "gopher.config" + c.setup { + commands = { + go = "go1.420", + gomodifytags = "iDontUseRustBtw", + }, + } + + assert.are.same(c.commands.go, "go1.420") + assert.are.same(c.commands.gomodifytags, "iDontUseRustBtw") + assert.are.same(c.commands.gotests, "gotests") + assert.are.same(c.commands.impl, "impl") + assert.are.same(c.commands.iferr, "iferr") + assert.are.same(c.commands.dlv, "dlv") + end) +end) diff --git a/spec/units/utils_spec.lua b/spec/units/utils_spec.lua new file mode 100644 index 0000000..ea2f30c --- /dev/null +++ b/spec/units/utils_spec.lua @@ -0,0 +1,15 @@ +describe("gopher._utils", function() + local u = require "gopher._utils" + + describe(".sreq()", function() + it("can require existing module", function() + assert.are.same(require "gopher", u.sreq "gopher") + end) + + it("cannot require non-existing module", function() + assert.has.errors(function() + u.sreq "iDontExistBtw" + end) + end) + end) +end)