Compare commits

...

25 commits
main ... v0.1.5

Author SHA1 Message Date
Smirnov Oleksandr
a5becfc329
Merge branch 'main' into develop 2024-06-15 12:40:53 +03:00
Smirnov Oleksandr
af1d5d166f
add deprecation message (#67) 2024-06-15 12:37:15 +03:00
Smirnov Oleksandr
fbf6441f3d
feat: add logger (#64)
* refactor(health): keep in mind new way of health check (#63)

* feat(log): add logger module

* refactor(utils): remove unused code

* refactor(log, utils): get plugin name from config

* refactor(logger): add some type annotations

* refactor(utils): log notifications

* feat: LOGGER™

* feat(config): TYPES

* refactor(log): dont give a thing about var that is not even declared

* feat(log): add easy way to open log

* refactor(log): some types

* update types

* docs: regen

* fix(log): make setting log level by config work

* feat(iferr): write error to log file if occur

* feat(gotests): add logger
2024-06-15 12:18:26 +03:00
Smirnov Oleksandr
65fa148661 refactor(health): keep in mind new way of health check (#63) 2024-06-07 21:14:35 +03:00
Smirnov Oleksandr
10cec9c6b0
add help file, and docs (#59)
* idk how good this idea is

* this could be working but i still cant figure out how to run it

* ignore tags that mini.doc gens, but why?

* chore(taskfile): force exiting after tests

because i got infinit ci

* chore(ci): add more nvim versions to run on

* chore: update taskfile

* feat: add docs generator

* docs: its only begining

* refactor: update docgen script

* docs: write some more

* docs(config): update

* docs: update readme

* language

* hope it would work

* what about that?

* maybe this would work?

* update md

* upd

* WHY DOESNT IT WORKING

* idk by but 0.9.3 just fails the ci, so i deleted it from suite

* again update, why does markdown not work in embeded html

* maybe it can help?

* upd

* again update

* kinda fix

* fix: formatting

* again some updating

* some readme updating

* fix, this shouldnt be in repo

* i finnaly undertood how to fix this fking skill issue

* fix(struct_tags): typo

* refactor(docs): change the order in generated file

* docs: install deps

* refactor(scripts): rename doc-gen script

* docs(impl): write docs

* docs(dap): add doc

* stylua .

* docs(struct_tags): add doc

* docs(gotests): add docs

* docs(iferr): add docs

* docs(comment): add doc

* update CONTRIBUTING.md

* docs(README): talk about `develop` branch

* docs: update README.md
2024-04-04 17:15:55 +03:00
Smirnov Oleksandr
28e1f5689f refactor(api)!: mave tags and gotests api into their sub tables 2024-02-25 01:18:09 +02:00
Smirnov Oleksandr
ebb10e9b90 reformat .editorconfig config 2024-02-11 17:49:13 +02:00
Alex
cdb1cd05a3
Add support for named tests (#50)
* fix(typo): README.md (#47)

* feat: add support for named tests

* test

* tags in table

* debug installer msg

* test

* hardcoded @develop

* get gotests tag from setup()

* update readme

* store install tag in urls table

* removed gotests tag

* update README.md

* remove urls installer index reference

* remove named arg from add_test()

* .

* update README.md

* update README.md

---------

Co-authored-by: Steve M <gearcog@users.noreply.github.com>
2024-02-11 16:35:39 +02:00
Arne Van Maele
8a6f7748ef
fix: fix iferr config (#56) 2024-02-11 01:44:59 +02:00
Smirnov Oleksandr
bbe89312c5 fix(health): check if bin is installed, and added message about treesitter parser 2024-01-12 09:01:56 +02:00
Smirnov Oleksandr
e0a3e70e48
add ability for setting custom tools options (#44)
* feat(gotests): add custom templates support

* feat(struct_tags): add support for custom `transform` option
2023-08-17 19:13:56 +03:00
Smirnov Oleksandr
2e89cea6f3 refactor: commands runner (#42)
* feat(utils): first impl of own commands runner

* refactor(gotests): uses own runner instead of vendored

* refactor(utils): back to plenary.job

* refactor(gotests): use new runner, clean code

* fix(runner): now it returns output correctly

* refactor(iferr): use vim.system

i have tried to use _utils.runner, but i can't figure out how to make `< file.go` for the command

* refactor(impl): use new runner

* refactor(installer): use new runner

* refactor(struct_tags): use new runner

* refactor: commands such as :GoGet runs with new runner

* refactor: throw errors in more lua way, i think

* refactor(utils): notify now has title

* refactor: use more correct way of notifying

* refactor(runner): write error message on error
2023-08-10 12:11:26 +03:00
Smirnov Oleksandr
011769b99b chore(ci): run tests on many versions of nvim 2023-07-22 03:03:09 +03:00
Smirnov Oleksandr
4af6caed6b fix(dap): now dlv uses cmd to run from config 2023-07-22 03:00:00 +03:00
Smirnov Oleksandr
5f8466d043
run tests independent of user nvim setup (#39)
* chore(lua_ls): now lua_ls knows about testing functions

* spec: change way how tests srtuctured

* test(config): refactor tests

* test: utils

* refactor(utils): remove not used function

* chore(ci): add test runner

* chore(ci): remove taskfile from deps

* fix: now it works
2023-07-21 02:57:46 +03:00
Smirnov Oleksandr
b5327cd2eb feat(config): make it optional to call .setup() 2023-07-20 19:53:15 +03:00
Smirnov Oleksandr
1841aede91 chore: update taskfile, and linter config 2023-07-20 19:51:02 +03:00
Smirnov Oleksandr
3b0888ab10 fix(config): now it works correctly 2023-07-20 19:40:12 +03:00
Smirnov Oleksandr
eac5560200 fix(config): now it not removes .setup() from itself after calling
.setup()
2023-07-20 00:36:16 +03:00
Smirnov Oleksandr
e49f3fadd7 remove editorconfig-checker 2023-07-20 00:21:15 +03:00
Smirnov Oleksandr
3d49d58fd2 feat: run tests independent from user's nvim config 2023-07-20 00:18:13 +03:00
Smirnov Oleksandr
bc3ce343a8
update tooling (#38)
* chore: delete pre-commit

* chore: switch from makefile to taskfile

* chore(ci): update and add one more linter

* chore(editorconfig): add config for Go

* chore(editorconfig): remove max_line_length

* fix: editorconfig-check on README.md
2023-07-20 00:11:59 +03:00
Smirnov Oleksandr
26b41bf68c
refactor of public plugin's api (#37)
* refactor: move all plugin functionality to init.lua

* fix(commands): now it uses correct module paths

* refactor(config): change way how it handles options

* refactor(gotests): use correct config, change way how deps required, fix some typos

* fix(healthchecker): use correct config

* refactor(iferr): change api

* refactor(impl): change api

* refactor(installer): change api

* refactor(struct_tags): change way of importting deps

* refactor(struct_tags): rename M to struct_tags

* run stylua

* refactor(dap): make it all in one file, and make some refactoring

* refactor(_utils): change way how it organizes

* fix: use new _utils api

* refactor(_utils.health): reorganize module

* refactor(_utils.ts): some renameing, moving requires lines

* run stylua
2023-07-19 23:38:23 +03:00
Smirnov Oleksandr
94250bb08a
add editorconfig (#36) 2023-07-17 18:32:24 +03:00
Smirnov Oleksandr
9d6bc761d4
healthcheck: refactoring, remove deprecation wanings (#35)
* refactor(checkhealth): remove deprecation warnings, complete rewrite

* refactor(checkhealth): rename util file

* style(healthchecker): reformat lua in vim file

* refactor(health): move all report function into table
2023-07-17 18:31:21 +03:00
47 changed files with 1594 additions and 737 deletions

16
.editorconfig Normal file
View file

@ -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

View file

@ -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 .

19
.github/workflows/linters.yml vendored Normal file
View file

@ -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: .

44
.github/workflows/tests.yml vendored Normal file
View file

@ -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

3
.gitignore vendored
View file

@ -1 +1,2 @@
playground/ /playground/
/.tests/

10
.luarc.json Normal file
View file

@ -0,0 +1,10 @@
{
"diagnostics.globals": [
"describe",
"it",
"before_each",
"after_each",
"before_all",
"after_all"
]
}

View file

@ -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: ["-"]

View file

@ -1,6 +1,14 @@
# Contributing to `gopher.nvim` # 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 ### Styling and formatting
@ -8,6 +16,40 @@ Code is formatted by [stylua](https://github.com/JohnnyMorganz/StyLua) and linte
You can install these with: You can install these with:
```bash ```bash
cargo install stylua sudo pacman -S selene stylua
cargo install selene # 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
``` ```

View file

@ -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'}"

230
README.md
View file

@ -4,157 +4,230 @@
Minimalistic plugin for Go development in Neovim written in Lua. 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 ```lua
use { {
"olexsmir/gopher.nvim", "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-lua/plenary.nvim",
"nvim-treesitter/nvim-treesitter", "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. ## Configuratoin
## Config > [!IMPORTANT]
>
> If you need more info look `:h gopher.nvim`
By `.setup` function you can configure the plugin. **Take a look at default options**
Note:
- `installer` does not install the tool in user set path
```lua ```lua
require("gopher").setup { require("gopher").setup {
commands = { commands = {
go = "go", go = "go",
gomodifytags = "gomodifytags", gomodifytags = "gomodifytags",
gotests = "~/go/bin/gotests", -- also you can set custom command path gotests = "gotests",
impl = "impl", impl = "impl",
iferr = "iferr", 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 ## Features
1. Installation requires this go tool: <!-- markdownlint-disable -->
<details>
<summary>
<b>Install plugin's go deps</b>
</summary>
```vim ```vim
:GoInstallDeps :GoInstallDeps
``` ```
It will install next tools: This will install the following tools:
- [gomodifytags](https://github.com/fatih/gomodifytags) - [gomodifytags](https://github.com/fatih/gomodifytags)
- [impl](https://github.com/josharian/impl) - [impl](https://github.com/josharian/impl)
- [gotests](https://github.com/cweill/gotests) - [gotests](https://github.com/cweill/gotests)
- [iferr](https://github.com/koron/iferr) - [iferr](https://github.com/koron/iferr)
- [dlv](github.com/go-delve/delve/cmd/dlv)
</details>
<details>
<summary>
<b>Add and remove tags for structs via <a href="https://github.com/fatih/gomodifytags">gomodifytags</a></b>
</summary>
2. Modify struct tags:
By default `json` tag will be added/removed, if not set: By default `json` tag will be added/removed, if not set:
```vim ```vim
:GoTagAdd json " For add json tag " add json tag
:GoTagRm yaml " For remove yaml tag :GoTagAdd json
" remove yaml tag
:GoTagRm yaml
``` ```
3. Run `go mod` command: ```lua
-- or you can use lua api
```vim require("gopher").tags.add "xml"
:GoMod tidy " Runs `go mod tidy` require("gopher").tags.rm "proto"
:GoMod init asdf " Runs `go mod init asdf`
``` ```
</details>
4. Run `go get` command <details>
<summary>
Link can have a `http` or `https` prefix. <b>Generating tests via <a href="https://github.com/cweill/gotests">gotests</a></b>
</summary>
You can provide more than one package url:
```vim
:GoGet github.com/gorilla/mux
```
5. Interface implementation
Command syntax:
```vim
:GoImpl [receiver] [interface]
" Also you can put cursor on the struct and run:
:GoImpl [interface]
```
Example of usage:
```vim
" Example
:GoImpl r Read io.Reader
" or simply put your cursor in the struct and run:
:GoImpl io.Reader
```
6. Generate tests with [gotests](https://github.com/cweill/gotests)
Generate one test for a specific function/method:
```vim ```vim
" Generate one test for a specific function/method(one under cursor)
:GoTestAdd :GoTestAdd
```
Generate all tests for all functions/methods in current file: " Generate all tests for all functions/methods in the current file
```vim
:GoTestsAll :GoTestsAll
```
Generate tests only for exported functions/methods in current file: " Generate tests for only exported functions/methods in the current file
```vim
:GoTestsExp :GoTestsExp
``` ```
7. Run `go generate` command; ```lua
-- or you can use lua api
```vim require("gopher").test.add()
" Run `go generate` in cwd path require("gopher").test.exported()
:GoGenerate require("gopher").test.all()
" Run `go generate` for current file
:GoGenerate %
``` ```
8. Generate doc comment For named tests see `:h gopher.nvim-gotests-named`
</details>
<details>
<summary>
<b>Run commands like <code>go mod/get/etc</code> inside of nvim</b>
</summary>
```vim
:GoGet github.com/gorilla/mux
" Link can have an `http` or `https` prefix.
:GoGet https://github.com/lib/pq
" You can provide more than one package url
:GoGet github.com/jackc/pgx/v5 github.com/google/uuid/
" go mod commands
:GoMod tidy
:GoMod init new-shiny-project
" go work commands
:GoWork sync
" run go generate in cwd
:GoGenerate
" run go generate for the current file
:GoGenerate %
```
</details>
<details>
<summary>
<b>Interface implementation via <a href="https://github.com/josharian/impl">impl<a></b>
</summary>
Syntax of the command:
```vim
:GoImpl [receiver] [interface]
" also you can put a cursor on the struct and run
:GoImpl [interface]
```
Usage examples:
```vim
:GoImpl r Read io.Reader
:GoImpl Write io.Writer
" or you can simply put a cursor on the struct and run
:GoImpl io.Reader
```
</details>
<details>
<summary>
<b>Generate boilerplate for doc comments</b>
</summary>
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 ```vim
:GoCmt :GoCmt
``` ```
</details>
9. Generate `if err`
Set cursor on the line with **err** and execute: <details>
<summary>
<b>Generate <code>if err != nil {</code> via <a href="https://github.com/koron/iferr">iferr</a></b>
</summary>
Set the cursor on the line with `err` and execute
```vim ```vim
:GoIfErr :GoIfErr
``` ```
</details>
10. Setup nvim-dap for go in one line. <details>
<summary>
<b>Setup <a href="https://github.com/mfussenegger/nvim-dap">nvim-dap</a> for go in one line</b>
</summary>
Notice: [nvim-dap](https://github.com/mfussenegger/nvim-dap) is required THIS FEATURE WILL BE REMOVED IN `0.1.6`
note [nvim-dap](https://github.com/mfussenegger/nvim-dap) has to be installed
```lua ```lua
require"gopher.dap".setup() require("gopher.dap").setup()
``` ```
</details>
## Contributing ## Contributing
@ -164,3 +237,4 @@ PRs are always welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md)
- [go.nvim](https://github.com/ray-x/go.nvim) - [go.nvim](https://github.com/ray-x/go.nvim)
- [nvim-dap-go](https://github.com/leoluz/nvim-dap-go) - [nvim-dap-go](https://github.com/leoluz/nvim-dap-go)
- [iferr](https://github.com/koron/iferr)

49
Taskfile.yml Normal file
View file

@ -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!"

View file

@ -1,3 +1,3 @@
function! health#gopher#check() function! health#gopher#check()
lua require"gopher.health".check() lua require("gopher.health").check()
endfunction endfunction

1
doc/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/tags

220
doc/gopher.nvim.txt Normal file
View file

@ -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:

View file

@ -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,
}

View file

@ -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

View file

@ -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

View file

@ -1,48 +1,34 @@
---@diagnostic disable: param-type-mismatch local c = require "gopher.config"
return { local log = require "gopher._utils.log"
---@param t table local utils = {}
---@return boolean
empty = function(t)
if t == nil then
return true
end
return next(t) == nil
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
return s:sub(1, n)
end,
---@param msg string ---@param msg string
---@param lvl string|integer ---@param lvl number
notify = function(msg, lvl) function utils.deferred_notify(msg, lvl)
local l vim.defer_fn(function()
if lvl == "error" or lvl == 4 then vim.notify(msg, lvl, {
l = vim.log.levels.ERROR title = c.___plugin_name,
elseif lvl == "info" or lvl == 2 then })
l = vim.log.levels.INFO log.debug(msg)
elseif lvl == "debug" or lvl == 1 then end, 0)
l = vim.log.levels.DEBUG
end end
vim.defer_fn(function() ---@param msg string
vim.notify(msg, l) ---@param lvl? number
end, 0) function utils.notify(msg, lvl)
end, lvl = lvl or vim.log.levels.INFO
vim.notify(msg, lvl, {
title = c.___plugin_name,
})
log.debug(msg)
end
---safe require -- safe require
---@param name string module name ---@param module string module name
sreq = function(name) function utils.sreq(module)
local ok, m = pcall(require, name) local ok, m = pcall(require, module)
assert(ok, string.format("gopher.nvim dependency error: %s not installed", name)) assert(ok, string.format("gopher.nvim dependency error: %s not installed", module))
return m return m
end, end
}
return utils

170
lua/gopher/_utils/log.lua Normal file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,7 +1,7 @@
---@diagnostic disable: param-type-mismatch ---@diagnostic disable: param-type-mismatch
local nodes = require "gopher._utils.ts.nodes" local nodes = require "gopher._utils.ts.nodes"
local u = require "gopher._utils" local u = require "gopher._utils"
local M = { local ts = {
querys = { querys = {
struct_block = [[((type_declaration (type_spec name:(type_identifier) @struct.name type: (struct_type)))@struct.declaration)]], 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]], 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 bufnr string|nil
---@param do_notify boolean|nil ---@param do_notify boolean|nil
---@return table|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 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 bufn = bufnr or vim.api.nvim_get_current_buf()
local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col)
if ns == nil then if ns == nil then
if notify then if notify then
u.notify("struct not found", "warn") u.deferred_notify("struct not found", vim.log.levels.WARN)
end end
else else
return ns[#ns] return ns[#ns]
@ -46,14 +46,14 @@ end
---@param bufnr string|nil ---@param bufnr string|nil
---@param do_notify boolean|nil ---@param do_notify boolean|nil
---@return table|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 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 bufn = bufnr or vim.api.nvim_get_current_buf()
local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col)
if ns == nil then if ns == nil then
if notify then if notify then
u.notify("function not found", "warn") u.deferred_notify("function not found", vim.log.levels.WARN)
end end
else else
return ns[#ns] return ns[#ns]
@ -65,16 +65,16 @@ end
---@param bufnr string|nil ---@param bufnr string|nil
---@param do_notify boolean|nil ---@param do_notify boolean|nil
---@return table|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 local notify = do_notify or true
-- stylua: ignore -- stylua: ignore
if row > 10 then return end 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 bufn = bufnr or vim.api.nvim_get_current_buf()
local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col)
if ns == nil then if ns == nil then
if notify then if notify then
u.notify("package not found", "warn") u.deferred_notify("package not found", vim.log.levels.WARN)
return nil return nil
end end
else else
@ -87,18 +87,18 @@ end
---@param bufnr string|nil ---@param bufnr string|nil
---@param do_notify boolean|nil ---@param do_notify boolean|nil
---@return table|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 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 bufn = bufnr or vim.api.nvim_get_current_buf()
local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col) local ns = nodes.nodes_at_cursor(query, get_name_defaults(), bufn, row, col)
if ns == nil then if ns == nil then
if notify then if notify then
u.notify("interface not found", "warn") u.deferred_notify("interface not found", vim.log.levels.WARN)
end end
else else
return ns[#ns] return ns[#ns]
end end
end end
return M return ts

View file

@ -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 M = {}
local function intersects(row, col, sRow, sCol, eRow, eCol) local function intersects(row, col, sRow, sCol, eRow, eCol)
@ -53,10 +57,6 @@ end
---@param pos_row string ---@param pos_row string
---@return string ---@return string
function M.get_all_nodes(query, lang, _, bufnr, pos_row, _) 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 bufnr = bufnr or 0
pos_row = pos_row or 30000 pos_row = pos_row or 30000
@ -113,8 +113,6 @@ end
---@param col string ---@param col string
---@return table ---@return table
function M.nodes_at_cursor(query, default, bufnr, row, col) function M.nodes_at_cursor(query, default, bufnr, row, col)
local u = require "gopher._utils"
bufnr = bufnr or vim.api.nvim_get_current_buf() bufnr = bufnr or vim.api.nvim_get_current_buf()
local ft = vim.api.nvim_buf_get_option(bufnr, "ft") local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
if row == nil or col == nil then 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) local nodes = M.get_all_nodes(query, ft, default, bufnr, row, col)
if nodes == nil then 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 return nil
end end
nodes = M.sort_nodes(M.intersect_nodes(nodes, row, col)) nodes = M.sort_nodes(M.intersect_nodes(nodes, row, col))
if nodes == nil or #nodes == 0 then 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 return nil
end end

View file

@ -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

View file

@ -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 function generate(row, col)
local ts_utils = require "gopher._utils.ts" local ts_utils = require "gopher._utils.ts"
local comment, ns = nil, nil local comment, ns = nil, nil
@ -33,6 +40,8 @@ return function()
local row, col = unpack(vim.api.nvim_win_get_cursor(0)) local row, col = unpack(vim.api.nvim_win_get_cursor(0))
local comment, ns = generate(row + 1, col + 1) local comment, ns = generate(row + 1, col + 1)
log.debug("generated comment: " .. comment)
vim.api.nvim_win_set_cursor(0, { vim.api.nvim_win_set_cursor(0, {
ns.dim.s.r, ns.dim.s.r,
ns.dim.s.c, ns.dim.s.c,

View file

@ -1,18 +1,40 @@
---@class Config ---@toc_entry Configuration
---@field commands ConfigCommands ---@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|
---@class ConfigCommands ---@type gopher.Config
---@field go string ---@private
---@field gomodifytags string local config = {}
---@field gotests string
---@field impl string
---@field iferr string
---@field dlv string
local M = { ---@tag gopher.nvim-config.ConfigGoTagTransform
---@type Config ---@text Possible values for |gopher.Config|.gotag.transform:
config = { ---
---set custom commands for tools ---@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 = { commands = {
go = "go", go = "go",
gomodifytags = "gomodifytags", gomodifytags = "gomodifytags",
@ -21,13 +43,47 @@ local M = {
iferr = "iferr", iferr = "iferr",
dlv = "dlv", 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
---Plugin setup function ---@type gopher.Config
---@param opts Config user config ---@private
function M.setup(opts) local _config = default_config
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 end
return M setmetatable(config, {
__index = function(_, key)
return _config[key]
end,
})
return config

129
lua/gopher/dap.lua Normal file
View file

@ -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

View file

@ -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}",
},
},
}

View file

@ -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

View file

@ -1,73 +1,104 @@
---@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
---
---@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|
local c = require "gopher.config"
local ts_utils = require "gopher._utils.ts"
local r = require "gopher._utils.runner"
local u = require "gopher._utils" local u = require "gopher._utils"
local M = {} local log = require "gopher._utils.log"
local gotests = {}
---@param cmd_args table
local function run(cmd_args)
local Job = require "plenary.job"
local c = require("gopher.config").config.commands
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
u.notify("unit test(s) generated", "info")
end,
}):start()
end
---@param args table ---@param args table
---@private
local function add_test(args) local function add_test(args)
local fpath = vim.fn.expand "%" ---@diagnostic disable-line: missing-parameter if c.gotests.named then
table.insert(args, "-w") table.insert(args, "-named")
table.insert(args, fpath)
run(args)
end end
---generate unit test for one function if c.gotests.template_dir then
---@param parallel boolean table.insert(args, "-template_dir")
function M.func_test(parallel) table.insert(args, c.gotests.template_dir)
local ts_utils = require "gopher._utils.ts" end
if c.gotests.template ~= "default" then
table.insert(args, "-template")
table.insert(args, c.gotests.template)
end
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 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))) 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 if ns == nil or ns.name == nil then
u.notify("cursor on func/method and execute the command again", "info") u.notify("cursor on func/method and execute the command again", vim.log.levels.WARN)
return return
end end
local cmd_args = { "-only", ns.name } add_test { "-only", ns.name }
if parallel then
table.insert(cmd_args, "-parallel")
end end
add_test(cmd_args) -- generate unit tests for all functions in current file
function gotests.all_tests()
add_test { "-all" }
end end
---generate unit tests for all functions in current file -- generate unit tests for all exported functions
---@param parallel boolean function gotests.all_exported_tests()
function M.all_tests(parallel) add_test { "-exported" }
local cmd_args = { "-all" }
if parallel then
table.insert(cmd_args, "-parallel")
end end
add_test(cmd_args) return gotests
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
table.insert(cmd_args, "-exported")
add_test(cmd_args)
end
return M

View file

@ -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 deps = {
local M = { plugin = {
_required = { { lib = "dap", msg = "required for `gopher.dap`", optional = true },
plugins = { { lib = "plenary", msg = "required for everyting in gopher.nvim", optional = false },
{ lib = "plenary", help = requried_for_work_msg }, { lib = "nvim-treesitter", msg = "required for everyting in gopher.nvim", optional = false },
{ lib = "nvim-treesitter", help = requried_for_work_msg },
{ lib = "dap", help = "Required for set upping debugger" },
}, },
binarys = { bin = {
{ bin = c.go, help = "required for GoMod, GoGet, GoGenerate command" }, {
{ bin = c.gomodifytags, help = "required for modify struct tags" }, bin = cmd.go,
{ bin = c.impl, help = "required for interface implementing" }, msg = "required for `:GoGet`, `:GoMod`, `:GoGenerate`, `:GoWork`, `:GoInstallDeps`",
{ bin = c.gotests, help = "required for test(s) generation" }, optional = false,
{ 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() function health.check()
local health = vim.health or require "health" u.start "required plugins"
local u = require "gopher._utils._health" for _, plugin in ipairs(deps.plugin) do
if u.is_lualib_found(plugin.lib) then
health.report_start "Required plugins" u.ok(plugin.lib .. " installed")
for _, plugin in ipairs(M._required.plugins) do
if u.lualib_is_found(plugin.lib) then
health.report_ok(plugin.lib .. " installed.")
else else
health.report_error(plugin.lib .. " not found. " .. plugin.help) if plugin.optional then
end u.warn(plugin.lib .. " not found, " .. plugin.msg)
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")
else else
health.report_warn(binary.bin .. " is not installed but " .. binary.help) u.error(plugin.lib .. " not found, " .. plugin.msg)
end end
end end
end end
return M 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
if bin.optional then
u.warn(bin.bin .. " not found, " .. bin.msg)
else
u.error(bin.bin .. " not found, " .. bin.msg)
end
end
end
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
u.error(parser.parser .. " parser not found, " .. parser.msg)
end
end
end
return health

View file

@ -1,21 +1,26 @@
---Add iferr declaration ---@toc_entry Iferr
---That's Lua of vimscript implementation of: ---@tag gopher.nvim-iferr
---github.com/koron/iferr ---@text if you're using `iferr` tool, this module provides a way to automatically insert `if err != nil` check.
return function() ---@usage execute `:GoIfErr` near any err variable to insert the check
local c = require("gopher.config").config.commands
local u = require "gopher._utils"
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 boff = vim.fn.wordcount().cursor_bytes
local cmd = (c.iferr .. " -pos " .. boff) local pos = vim.fn.getcurpos()[2]
local data = vim.fn.systemlist(cmd, vim.fn.bufnr "%")
local data = vim.fn.systemlist((c.commands.iferr .. " -pos " .. boff), vim.fn.bufnr "%")
if vim.v.shell_error ~= 0 then if vim.v.shell_error ~= 0 then
u.notify("command " .. cmd .. " exited with code " .. vim.v.shell_error, "error") error("iferr failed: " .. data)
return log.error("failed. output: " .. data)
end end
local pos = vim.fn.getcurpos()[2]
vim.fn.append(pos, data) vim.fn.append(pos, data)
vim.cmd [[silent normal! j=2j]] vim.cmd [[silent normal! j=2j]]
vim.fn.setpos(".", pos) vim.fn.setpos(".", pos)
end end
return iferr

View file

@ -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 u = require "gopher._utils"
local impl = {}
---@return string ---@return string
---@private
local function get_struct() 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))) local ns = ts_utils.get_struct_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0)))
if ns == nil then 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 "" return ""
end end
@ -18,10 +49,7 @@ local function get_struct()
return ns.name return ns.name
end end
return function(...) function impl.impl(...)
local c = require("gopher.config").config.commands
local Job = require "plenary.job"
local args = { ... } local args = { ... }
local iface, recv_name = "", "" local iface, recv_name = "", ""
local recv = get_struct() local recv = get_struct()
@ -30,7 +58,7 @@ return function(...)
iface = vim.fn.input "impl: generating method stubs for interface: " iface = vim.fn.input "impl: generating method stubs for interface: "
vim.cmd "redraw!" vim.cmd "redraw!"
if iface == "" then 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 return
end end
elseif #args == 1 then -- :GoImpl io.Reader elseif #args == 1 then -- :GoImpl io.Reader
@ -48,28 +76,23 @@ return function(...)
recv = string.format("%s %s", recv_name, recv) recv = string.format("%s %s", recv_name, recv)
end end
-- stylua: ignore local output = r.sync(c.impl, {
local cmd_args = { args = {
"-dir", vim.fn.fnameescape(vim.fn.expand "%:p:h"), ---@diagnostic disable-line: missing-parameter "-dir",
vim.fn.fnameescape(vim.fn.expand "%:p:h" --[[@as string]]),
recv, recv,
iface iface,
} },
on_exit = function(data, status)
local res_data if not status == 0 then
Job:new({ error("impl failed: " .. data)
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
end end
res_data = data:result()
end, end,
}):sync() })
local pos = vim.fn.getcurpos()[2] local pos = vim.fn.getcurpos()[2]
table.insert(res_data, 1, "") table.insert(output, 1, "")
vim.fn.append(pos, res_data) vim.fn.append(pos, output)
end end
return impl

View file

@ -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

View file

@ -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 = { local urls = {
gomodifytags = "github.com/fatih/gomodifytags", gomodifytags = "github.com/fatih/gomodifytags",
impl = "github.com/josharian/impl", impl = "github.com/josharian/impl",
@ -8,28 +13,24 @@ local urls = {
---@param pkg string ---@param pkg string
local function install(pkg) local function install(pkg)
local Job = require "plenary.job"
local u = require "gopher._utils"
local url = urls[pkg] .. "@latest" local url = urls[pkg] .. "@latest"
r.sync(c.go, {
Job:new({
command = "go",
args = { "install", url }, args = { "install", url },
on_exit = function(_, retval) on_exit = function(data, status)
if retval ~= 0 then if not status == 0 then
u.notify("command 'go install " .. url .. "' exited with code " .. retval, "error") error("go install failed: " .. data)
return return
end end
u.notify("installed: " .. url)
u.notify("install " .. url .. " finished", "info ")
end, end,
}):start() })
end end
---Install required go deps ---Install required go deps
return function() function installer.install_deps()
for pkg, _ in pairs(urls) do for pkg, _ in pairs(urls) do
install(pkg) install(pkg)
end end
end end
return installer

View file

@ -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 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 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))) local ns = ts_utils.get_struct_node_at_pos(unpack(vim.api.nvim_win_get_cursor(0)))
if ns == nil then if ns == nil then
@ -14,6 +38,7 @@ local function modify(...)
-- stylua: ignore -- stylua: ignore
local cmd_args = { local cmd_args = {
"-transform", c.gotag.transform,
"-format", "json", "-format", "json",
"-file", fpath, "-file", fpath,
"-w" "-w"
@ -40,40 +65,26 @@ local function modify(...)
table.insert(cmd_args, "json") table.insert(cmd_args, "json")
end end
-- get result of "gomodifytags" works local output = r.sync(c.commands.gomodifytags, {
local res_data
Job:new({
command = c.gomodifytags,
args = cmd_args, args = cmd_args,
on_exit = function(data, retval) on_exit = function(data, status)
if retval ~= 0 then if not status == 0 then
u.notify( error("gotag failed: " .. data)
"command 'gomodifytags " .. unpack(cmd_args) .. "' exited with code " .. retval,
"error"
)
return
end end
res_data = data:result()
end, end,
}):sync() })
-- decode goted value -- decode goted value
local tagged = vim.json.decode(table.concat(res_data)) local tagged = vim.json.decode(table.concat(output))
if if
tagged.errors ~= nil tagged.errors ~= nil
or tagged.lines == nil or tagged.lines == nil
or tagged["start"] == nil or tagged["start"] == nil
or tagged["start"] == 0 or tagged["start"] == 0
then then
u.notify("failed to set tags " .. vim.inspect(tagged), "error") error("failed to set tags " .. vim.inspect(tagged))
end end
for i, v in ipairs(tagged.lines) do
tagged.lines[i] = u.rtrim(v)
end
-- write goted tags
vim.api.nvim_buf_set_lines( vim.api.nvim_buf_set_lines(
0, 0,
tagged.start - 1, tagged.start - 1,
@ -84,9 +95,8 @@ local function modify(...)
vim.cmd "write" vim.cmd "write"
end end
---add tags to struct under cursor -- add tags to struct under cursor
---@param ... unknown function struct_tags.add(...)
function M.add(...)
local arg = { ... } local arg = { ... }
if #arg == nil or arg == "" then if #arg == nil or arg == "" then
arg = { "json" } arg = { "json" }
@ -100,9 +110,8 @@ function M.add(...)
modify(unpack(cmd_args)) modify(unpack(cmd_args))
end end
---remove tags to struct under cursor -- remove tags to struct under cursor
---@param ... unknown function struct_tags.remove(...)
function M.remove(...)
local arg = { ... } local arg = { ... }
if #arg == nil or arg == "" then if #arg == nil or arg == "" then
arg = { "json" } arg = { "json" }
@ -116,4 +125,4 @@ function M.remove(...)
modify(unpack(cmd_args)) modify(unpack(cmd_args))
end end
return M return struct_tags

View file

@ -1,13 +1,14 @@
command! -nargs=* GoTagAdd :lua require"gopher.api".tags_add(<f-args>) command! -nargs=* GoTagAdd :lua require"gopher".tags.add(<f-args>)
command! -nargs=* GoTagRm :lua require"gopher.api".tags_rm(<f-args>) command! -nargs=* GoTagRm :lua require"gopher".tags.rm(<f-args>)
command! -nargs=* GoTestAdd :lua require"gopher.api".test_add(<f-args>) command! GoTestAdd :lua require"gopher".test.add()
command! -nargs=* GoTestsAll :lua require"gopher.api".tests_all(<f-args>) command! GoTestsAll :lua require"gopher".test.all()
command! -nargs=* GoTestsExp :lua require"gopher.api".test_exported(<f-args>) command! GoTestsExp :lua require"gopher".test.exported()
command! -nargs=* GoMod :lua require"gopher.api".mod(<f-args>) command! -nargs=* GoMod :lua require"gopher".mod(<f-args>)
command! -nargs=* GoGet :lua require"gopher.api".get(<f-args>) command! -nargs=* GoGet :lua require"gopher".get(<f-args>)
command! -nargs=* GoWork :lua require"gopher.api".work(<f-args>) command! -nargs=* GoWork :lua require"gopher".work(<f-args>)
command! -nargs=* GoImpl :lua require"gopher.api".impl(<f-args>) command! -nargs=* GoImpl :lua require"gopher".impl(<f-args>)
command! -nargs=* GoGenerate :lua require"gopher.api".generate(<f-args>) command! -nargs=* GoGenerate :lua require"gopher".generate(<f-args>)
command! GoCmt :lua require"gopher.api".comment() command! GoCmt :lua require"gopher".comment()
command! GoIfErr :lua require"gopher.api".iferr() command! GoIfErr :lua require"gopher".iferr()
command! GoInstallDeps :lua require"gopher.api".install_deps() command! GoInstallDeps :lua require"gopher".install_deps()
command! GopherLog :lua vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile())

33
scripts/docgen.lua Normal file
View file

@ -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 })

34
scripts/minimal_init.lua Normal file
View file

@ -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"

View file

@ -1 +1,2 @@
std="nvim+lua51" std = "nvim+lua52"
exclude = [".tests/*"]

View file

@ -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)

View file

@ -1,5 +0,0 @@
describe("gopher", function()
it("can be required", function()
require "gopher"
end)
end)

View file

@ -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)

View file

@ -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)

View file

@ -1,4 +0,0 @@
set rtp+=.
packadd plenary.nvim
packadd nvim-treesitter
packadd nvim-dap

View file

@ -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)

15
spec/units/utils_spec.lua Normal file
View file

@ -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)