diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 0000000..159b445 --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -0,0 +1,72 @@ +name: github pages + +on: + push: + branches: + - main # Set a branch to deploy + workflow_dispatch: + pull_request: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + USE_MKDOCS_MATERIAL_INSIDERS: false + +jobs: + deploy: + runs-on: ubuntu-latest + environment: github-pages + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # for rss + + # for social cards + - run: sudo apt-get install libcairo2-dev libfreetype6-dev libffi-dev libjpeg-dev libpng-dev libz-dev + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version-file: "pyproject.toml" + + - name: Install Mkdocs-Material + run: uv sync + + - name: Install Mkdocs-Material-Insiders + run: | + uv pip uninstall mkdocs-material + uv pip install --upgrade git+https://${{ secrets.MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git + if: ${{ env.USE_MKDOCS_MATERIAL_INSIDERS == 'true' }} + + - name: Build + run: uv run mkdocs build + + - name: Setup Pages + if: github.event_name != 'pull_request' + uses: actions/configure-pages@v5 + + - name: Upload artifact + if: github.event_name != 'pull_request' + uses: actions/upload-pages-artifact@v3 + with: + path: "./site" + + - name: Deploy to GitHub Pages + if: github.event_name != 'pull_request' + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index f0d4bc0..6f41501 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .tests +site diff --git a/.pkgx.yml b/.pkgx.yml index 8291bfc..2759a1b 100644 --- a/.pkgx.yml +++ b/.pkgx.yml @@ -1,2 +1,3 @@ dependencies: - go.dev + - python diff --git a/README.md b/README.md index 443a93a..0ae4232 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Reliable Neotest adapter for running Go tests in Neovim. ![neotest-golang](https://github.com/fredrikaverpil/neotest-golang/assets/994357/afb6e936-b355-4d7b-ab73-65c21ee66ae7) -## ⭐️ Features +## Features - Supports all [Neotest usage](https://github.com/nvim-neotest/neotest#usage). - Supports table tests and nested test functions (based on treesitter AST @@ -22,878 +22,7 @@ Reliable Neotest adapter for running Go tests in Neovim. - Supports [testify](https://github.com/stretchr/testify) suites. - Option to sanitize test output from non-UTF8 characters. -
-Why a second Neotest adapter for Go? 🤔 +--- -While using [neotest-go](https://github.com/nvim-neotest/neotest-go) I stumbled -upon many problems which seemed difficult to solve in that codebase. - -I have full respect for the time and efforts put in by the developer(s) of -neotest-go. I do not aim in any way to diminish their needs or efforts. However, -I wanted to see if I could fix these issues by diving into the 🕳️🐇 of Neotest -and building my own adapter. Below is a list of neotest-go issues which are not -present in neotest-golang (this project): - -| Neotest-go issue | URL | -| ------------------------------------------------------- | --------------------------------------------------------------------- | -| Support for Testify framework | [neotest-go#6](https://github.com/nvim-neotest/neotest-go/issues/6) | -| DAP support | [neotest-go#12](https://github.com/nvim-neotest/neotest-go/issues/12) | -| Test Output in JSON, making it difficult to read | [neotest-go#52](https://github.com/nvim-neotest/neotest-go/issues/52) | -| Support for Nested Subtests | [neotest-go#74](https://github.com/nvim-neotest/neotest-go/issues/74) | -| Diagnostics for table tests on the line of failure | [neotest-go#75](https://github.com/nvim-neotest/neotest-go/issues/75) | -| "Run nearest" runs all tests | [neotest-go#83](https://github.com/nvim-neotest/neotest-go/issues/83) | -| Table tests not recognized when defined inside for-loop | [neotest-go#86](https://github.com/nvim-neotest/neotest-go/issues/86) | -| Running test suite doesn't work | [neotest-go#89](https://github.com/nvim-neotest/neotest-go/issues/89) | - -And here, a comparison in number of GitHub stars between the projects: - -[![Star History Chart](https://api.star-history.com/svg?repos=fredrikaverpil/neotest-golang,nvim-neotest/neotest-go&type=Date)](https://star-history.com/#fredrikaverpil/neotest-golang&nvim-neotest/neotest-go&Date) - -
- -## 🥸 Installation - -> [!NOTE] -> -> Requires Neovim 0.10.0 and above. - -
-💤 Lazy.nvim - -```lua -return { - { - "nvim-neotest/neotest", - dependencies = { - "nvim-neotest/nvim-nio", - "nvim-lua/plenary.nvim", - "antoinemadec/FixCursorHold.nvim", - "nvim-treesitter/nvim-treesitter", - { "fredrikaverpil/neotest-golang", version = "*" }, -- Installation - }, - config = function() - require("neotest").setup({ - adapters = { - require("neotest-golang"), -- Registration - }, - }) - end, - }, -} -``` - -For increased stability and less updating noise, I recommend that you track -official releases by setting `version = "*"`. By omitting this option (or -setting `version = false`), you will get the latest and greatest directly from -the main branch. - -I do not recommend pinning to a specific version or to a major version. But -ultimately it is up to you what you want :smile:. - -See the [Lazy versioning spec](https://lazy.folke.io/spec/versioning) for more -details. - -
- -
-🌒 Rocks.nvim - -The adapter is available via -[luarocks package](https://luarocks.org/modules/fredrikaverpil/neotest-golang): - -```vim -:Rocks install neotest-golang -``` - -[rocks.nvim](https://github.com/nvim-neorocks/rocks.nvim) will automatically -install dependencies if they are not already installed. You will need to call -neotest's `setup` function to register this adapter. If you use -[rocks-config.nvim](https://github.com/nvim-neorocks/rocks-config.nvim), -consider setting up neotest and its adapters in a -[plugin bundle](https://github.com/nvim-neorocks/rocks-config.nvim?tab=readme-ov-file#plugin-bundles). - -> [!NOTE] -> -> Please note that [leoluz/nvim-dap-go](https://github.com/leoluz/nvim-dap-go) -> (required for DAP) is not on luarocks as of writing this. - -
- -
-❄️ Nix & Home manager - -```nix -{ - config, - pkgs, - ... -}: { - home.packages = with pkgs; []; - programs = { - neovim = { - plugins = [ - # neotest and dependencies - pkgs.vimPlugins.neotest - pkgs.vimPlugins.nvim-nio - pkgs.vimPlugins.plenary-nvim - pkgs.vimPlugins.FixCursorHold-nvim - pkgs.vimPlugins.nvim-treesitter - (pkgs.vimPlugins.nvim-treesitter.withPlugins (plugins: [plugins.go])) - pkgs.vimPlugins.neotest-golang - - ## debugging - pkgs.vimPlugins.nvim-dap - pkgs.vimPlugins.nvim-dap-ui - pkgs.vimPlugins.nvim-nio - pkgs.vimPlugins.nvim-dap-virtual-text - pkgs.vimPlugins.nvim-dap-go - ]; - enable = true; - extraConfig = '' - lua << EOF - require("neotest").setup({ - adapters = { - require("neotest-golang") - }, - }) - EOF - ''; - }; - }; -} -``` - -
- -## ⚙️ Configuration - -| Argument | Default value | Description | -| ------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `runner` | `go` | Defines the test runner. Valid values: `go` or `gotestsum`. | -| `go_test_args` | `{ "-v", "-race", "-count=1" }` | Arguments to pass into `go test`. Notes: [`-tags` usage](https://github.com/fredrikaverpil/neotest-golang#using-build-tags), [pass args as function](https://github.com/fredrikaverpil/neotest-golang#pass-arguments-as-function-instead-of-table). | -| `gotestsum_args` | `{ "--format=standard-verbose" }` | Arguments to pass into `gotestsum`. Notes: [`-tags` usage](https://github.com/fredrikaverpil/neotest-golang#using-build-tags), [pass args as function](https://github.com/fredrikaverpil/neotest-golang#pass-arguments-as-function-instead-of-table). Will only be used if `runner = "gotestsum"`. The `go_test_args` still applies. | -| `go_list_args` | `{}` | Arguments to pass into `go list`. Note: [`-tags` usage](https://github.com/fredrikaverpil/neotest-golang#using-build-tags), [pass args as function](https://github.com/fredrikaverpil/neotest-golang#pass-arguments-as-function-instead-of-table). | -| `dap_mode` | `dap-go` | Set to `manual` for manual configuration. | -| `dap_go_opts` | `{}` | Options to pass into `require("dap-go").setup()`. Note: [`-tags` usage](https://github.com/fredrikaverpil/neotest-golang#using-build-tags), [pass args as function](https://github.com/fredrikaverpil/neotest-golang#pass-arguments-as-function-instead-of-table). | -| `dap_manual_config` | `{}` | The configuration to apply if `dap_mode == "manual"`. | -| `testify_enabled` | `false` | Enable support for [testify](https://github.com/stretchr/testify) suites. See [here](https://github.com/fredrikaverpil/neotest-golang#testify-suites) for more info. | -| `colorize_test_output` | `true` | Enable output color for `SUCCESS`, `FAIL`, and `SKIP` tests. | -| `warn_test_name_dupes` | `true` | Warn about duplicate test names within the same Go package. | -| `warn_test_not_executed` | `true` | Warn if test was not executed. | -| `log_level` | `vim.log.levels.WARN` | Log level. | -| `sanitize_output` | `false` | Filter control characters and non-printable characters from test output. Note: [usage](https://github.com/fredrikaverpil/neotest-golang#example-configuration-sanitize-output). | - -> [!NOTE] -> -> The `-race` flag (in `go_test_args`) requires CGO to be enabled -> (`CGO_ENABLED=1` is the default) and a C compiler (such as GCC) to be -> installed. However, since Go 1.20, this is not a requirement on macOS. I have -> included the `-race` argument as default, as it provides good production -> defaults. See [this issue](https://github.com/golang/go/issues/9918) for more -> details. - -> [!IMPORTANT] -> -> The `gotestsum` runner is recommended for Windows users or if you are using -> Ubuntu snaps. You can read more below on `gotestsum`. - -### Example configuration: custom `go test` arguments - -```lua -local config = { -- Specify configuration - go_test_args = { - "-v", - "-race", - "-count=1", - "-coverprofile=" .. vim.fn.getcwd() .. "/coverage.out", - }, -} -require("neotest").setup({ - adapters = { - require("neotest-golang")(config), -- Apply configuration - }, -}) -``` - -Note that the example above writes a coverage file. You can use -[andythigpen/nvim-coverage](https://github.com/andythigpen/nvim-coverage) to -show the coverage in Neovim. - -See `go help test`, `go help testflag`, `go help build` for possible arguments. - -### Example configuration: debugging - -To debug tests, make sure you depend on -[mfussenegger/nvim-dap](https://github.com/mfussenegger/nvim-dap) and -[rcarriga/nvim-dap-ui](https://github.com/rcarriga/nvim-dap-ui). Then you have -two options: - -
- - Adapter-provided DAP configuration, - leveraging leoluz/nvim-dap-go (recommended). - - -```diff -return { -+ { -+ "rcarriga/nvim-dap-ui", -+ dependencies = { -+ "nvim-neotest/nvim-nio", -+ "mfussenegger/nvim-dap", -+ }, -+ }, -+ - { - "nvim-neotest/neotest", - dependencies = { - "nvim-neotest/nvim-nio", - "nvim-lua/plenary.nvim", - "antoinemadec/FixCursorHold.nvim", - "nvim-treesitter/nvim-treesitter", -- "fredrikaverpil/neotest-golang", -- Installation -+ { -+ "fredrikaverpil/neotest-golang", -- Installation -+ dependencies = { -+ "leoluz/nvim-dap-go", -+ }, -+ }, - }, - config = function() - require("neotest").setup({ - adapters = { - require("neotest-golang"), -- Registration - }, - }) - end, - }, -} -``` - -
- -
-Use your own custom DAP configuration (no additional dependency needed). - -```diff -return { -+ { -+ "rcarriga/nvim-dap-ui", -+ dependencies = { -+ "nvim-neotest/nvim-nio", -+ "mfussenegger/nvim-dap", -+ }, -+ }, -+ - { - "nvim-neotest/neotest", - dependencies = { - "nvim-neotest/nvim-nio", - "nvim-lua/plenary.nvim", - "antoinemadec/FixCursorHold.nvim", - "nvim-treesitter/nvim-treesitter", - "fredrikaverpil/neotest-golang", -- Installation - }, - config = function() -+ local options = { -+ dap_mode = "manual", -+ dap_manual_config = { -+ name = "Debug go tests", -+ type = "go", -- Preconfigured DAP adapter name -+ request = "launch", -+ mode = "test", -+ }, -+ } - require("neotest").setup({ - adapters = { -+ require("neotest-golang")(options) -- Registration - }, - }) - end, - }, -} -``` - -

- -Finally, set a keymap, like: - -```lua -return { - { - "nvim-neotest/neotest", - ... - keys = { - { - "td", - function() - require("neotest").run.run({ suite = false, strategy = "dap" }) - end, - desc = "Debug nearest test", - }, - }, - }, -} -``` - -For a more verbose example, see the "extra everything" example config. - -### Using `gotestsum` as test runner - -To improve reliability, you can choose to set -[`gotestsum`](https://github.com/gotestyourself/gotestsum) as the test runner. -This tool allows the adapter to write test command output directly to a JSON -file, without going through stdout. - -Using `gotestsum` offers the following benefits: - -- When you "attach" to a running test, you'll see clean `go test` output instead - of having to navigate through difficult-to-read JSON. -- On certain platforms (such as Windows) or terminals, there's a risk of ANSI - codes or other characters being seemingly randomly inserted into the JSON test - output. This can corrupt the data and cause problems with test output JSON - decoding. Enabling `gotestsum` eliminates these issues, as the test output is - then written directly to file. - -`gotestsum` calls `go test` behind the scenes, so your `go_test_args` -configuration remains valid and will still apply. - -> [!NOTE] -> -> See -> [this issue comment](https://github.com/fredrikaverpil/neotest-golang/issues/193#issuecomment-2362845806) -> for more details on reported issues on Windows and Ubuntu snaps. - -#### Configure neotest-golang to use `gotestsum` as test runner - -Make the `gotestsum` command availalbe via -[mason.nvim](https://github.com/williamboman/mason.nvim) or by running the -following in your shell: - -```bash -go install gotest.tools/gotestsum@latest -``` - -Then add the required configuration: - -```lua -local config = { -- Specify configuration - runner = "gotestsum" -} -require("neotest").setup({ - adapters = { - require("neotest-golang")(config), -- Apply configuration - }, -}) -``` - -### Example configuration: sanitize output - -When tests write non-printable characters to stdout/stderr, they can cause -various issues like failing to write output to disk or UI rendering problems. -The `sanitize_output` option helps clean up such output by preserving UTF-8 and -replacing control characters with the Unicode replacement character (�). - -This is particularly useful when: - -- Tests write bytes to stdout/stderr. -- Test output contains terminal control sequences. -- Test output includes non-printable characters. - -The sanitization preserves all regular printable characters including tabs, -newlines, and carriage returns. - -```diff -return { - { - "nvim-neotest/neotest", - dependencies = { - "nvim-neotest/nvim-nio", - "nvim-lua/plenary.nvim", - "antoinemadec/FixCursorHold.nvim", - "nvim-treesitter/nvim-treesitter", -- "fredrikaverpil/neotest-golang", -- Installation -+ { -+ "fredrikaverpil/neotest-golang", -- Installation -+ dependencies = { -+ "uga-rosa/utf8.nvim", -- Additional dependency required -+ }, -+ }, - }, - config = function() - require("neotest").setup({ - adapters = { -- require("neotest-golang"), -- Registration -+ require("neotest-golang")({ sanitize_output = true }), -- Registration - }, - }) - end, - }, -} -``` - -### Example configuration: extra everything - -In the below code block, I've provided a pretty hefty configuration example, -which includes the required setup for testing and debugging along with all the -keymaps. This is a merged snapshot of my own config, which I hope you can draw -inspiration from. To view my current config, which is divided up into several -files, see: - -- [plugins/neotest.lua](https://github.com/fredrikaverpil/dotfiles/blob/main/nvim-fredrik/lua/fredrik/plugins/core/neotest.lua) -- [plugins/dap.lua](https://github.com/fredrikaverpil/dotfiles/blob/main/nvim-fredrik/lua/fredrik/plugins/core/dap.lua) -- [lang/go.lua](https://github.com/fredrikaverpil/dotfiles/blob/main/nvim-fredrik/lua/fredrik/plugins/lang/go.lua) - -
-Click to expand - -```lua -return { - - -- Neotest setup - { - "nvim-neotest/neotest", - event = "VeryLazy", - dependencies = { - "nvim-neotest/nvim-nio", - "nvim-lua/plenary.nvim", - "antoinemadec/FixCursorHold.nvim", - "nvim-treesitter/nvim-treesitter", - - "nvim-neotest/neotest-plenary", - "nvim-neotest/neotest-vim-test", - - { - "fredrikaverpil/neotest-golang", - dependencies = { - { - "leoluz/nvim-dap-go", - opts = {}, - }, - }, - branch = "main", - }, - }, - opts = function(_, opts) - opts.adapters = opts.adapters or {} - opts.adapters["neotest-golang"] = { - go_test_args = { - "-v", - "-race", - "-coverprofile=" .. vim.fn.getcwd() .. "/coverage.out", - }, - } - end, - config = function(_, opts) - if opts.adapters then - local adapters = {} - for name, config in pairs(opts.adapters or {}) do - if type(name) == "number" then - if type(config) == "string" then - config = require(config) - end - adapters[#adapters + 1] = config - elseif config ~= false then - local adapter = require(name) - if type(config) == "table" and not vim.tbl_isempty(config) then - local meta = getmetatable(adapter) - if adapter.setup then - adapter.setup(config) - elseif adapter.adapter then - adapter.adapter(config) - adapter = adapter.adapter - elseif meta and meta.__call then - adapter(config) - else - error("Adapter " .. name .. " does not support setup") - end - end - adapters[#adapters + 1] = adapter - end - end - opts.adapters = adapters - end - - require("neotest").setup(opts) - end, - keys = { - { "ta", function() require("neotest").run.attach() end, desc = "[t]est [a]ttach" }, - { "tf", function() require("neotest").run.run(vim.fn.expand("%")) end, desc = "[t]est run [f]ile" }, - { "tA", function() require("neotest").run.run(vim.uv.cwd()) end, desc = "[t]est [A]ll files" }, - { "tS", function() require("neotest").run.run({ suite = true }) end, desc = "[t]est [S]uite" }, - { "tn", function() require("neotest").run.run() end, desc = "[t]est [n]earest" }, - { "tl", function() require("neotest").run.run_last() end, desc = "[t]est [l]ast" }, - { "ts", function() require("neotest").summary.toggle() end, desc = "[t]est [s]ummary" }, - { "to", function() require("neotest").output.open({ enter = true, auto_close = true }) end, desc = "[t]est [o]utput" }, - { "tO", function() require("neotest").output_panel.toggle() end, desc = "[t]est [O]utput panel" }, - { "tt", function() require("neotest").run.stop() end, desc = "[t]est [t]erminate" }, - { "td", function() require("neotest").run.run({ suite = false, strategy = "dap" }) end, desc = "Debug nearest test" }, - { "tD", function() require("neotest").run.run({ vim.fn.expand("%"), strategy = "dap" }) end, desc = "Debug current file" }, - }, - }, - - -- DAP setup - { - "mfussenegger/nvim-dap", - event = "VeryLazy", - keys = { - {"db", function() require("dap").toggle_breakpoint() end, desc = "toggle [d]ebug [b]reakpoint" }, - {"dB", function() require("dap").set_breakpoint(vim.fn.input("Breakpoint condition: ")) end, desc = "[d]ebug [B]reakpoint"}, - {"dc", function() require("dap").continue() end, desc = "[d]ebug [c]ontinue (start here)" }, - {"dC", function() require("dap").run_to_cursor() end, desc = "[d]ebug [C]ursor" }, - {"dg", function() require("dap").goto_() end, desc = "[d]ebug [g]o to line" }, - {"do", function() require("dap").step_over() end, desc = "[d]ebug step [o]ver" }, - {"dO", function() require("dap").step_out() end, desc = "[d]ebug step [O]ut" }, - {"di", function() require("dap").step_into() end, desc = "[d]ebug [i]nto" }, - {"dj", function() require("dap").down() end, desc = "[d]ebug [j]ump down" }, - {"dk", function() require("dap").up() end, desc = "[d]ebug [k]ump up" }, - {"dl", function() require("dap").run_last() end, desc = "[d]ebug [l]ast" }, - {"dp", function() require("dap").pause() end, desc = "[d]ebug [p]ause" }, - {"dr", function() require("dap").repl.toggle() end, desc = "[d]ebug [r]epl" }, - {"dR", function() require("dap").clear_breakpoints() end, desc = "[d]ebug [R]emove breakpoints" }, - {"ds", function() require("dap").session() end, desc ="[d]ebug [s]ession" }, - {"dt", function() require("dap").terminate() end, desc = "[d]ebug [t]erminate" }, - {"dw", function() require("dap.ui.widgets").hover() end, desc = "[d]ebug [w]idgets" }, - }, - }, - - -- DAP UI setup - { - "rcarriga/nvim-dap-ui", - event = "VeryLazy", - dependencies = { - "nvim-neotest/nvim-nio", - "mfussenegger/nvim-dap", - }, - opts = {}, - config = function(_, opts) - -- setup dap config by VsCode launch.json file - -- require("dap.ext.vscode").load_launchjs() - local dap = require("dap") - local dapui = require("dapui") - dapui.setup(opts) - dap.listeners.after.event_initialized["dapui_config"] = function() - dapui.open({}) - end - dap.listeners.before.event_terminated["dapui_config"] = function() - dapui.close({}) - end - dap.listeners.before.event_exited["dapui_config"] = function() - dapui.close({}) - end - end, - keys = { - { "du", function() require("dapui").toggle({}) end, desc = "[d]ap [u]i" }, - { "de", function() require("dapui").eval() end, desc = "[d]ap [e]val" }, - }, - }, - { - "theHamsta/nvim-dap-virtual-text", - opts = {}, - }, -} -``` - -
- -## ⛑️ Tips & troubleshooting - -### Issues with setting up or using the adapter - -> [!TIP] -> -> You can run `:checkhealth neotest-golang` to review common issues. If you need -> configuring neotest-golang help, please open a discussion -> [here](https://github.com/fredrikaverpil/neotest-golang/discussions/new?category=configuration). - -You can also enable logging to further inspect what's going on under the hood. -Neotest-golang piggybacks on the Neotest logger but writes its own file. The -default log level is `WARN` but during troubleshooting you want to increase -this: - -```lua -local config = { - log_level = vim.log.levels.TRACE, -- set log level -} - -require("neotest").setup({ - adapters = { - require("neotest-golang")(config), -- Apply configuration - }, -}) -``` - -The neotest-golang logs can be opened using this convenient vim command: - -```vim -:exe 'edit' stdpath('log').'/neotest-golang.log' -``` - -This usually corresponds to something like -`~/.local/state/nvim/neotest-golang.log`. - -> [!WARNING] -> -> Don't forget to revert back to `WARN` level once you are done troubleshooting, -> as the `TRACE` level can degrade performance. - -### Neotest is slowing down Neovim - -Neotest, out of the box with default settings, can appear very slow in large -projects (here, I'm referring to -[this kind of large](https://github.com/kubernetes/kubernetes)). There are a few -things you can do to speed up the Neotest appearance and experience in such -cases, by tweaking the Neotest settings. - -You can for example limit the AST-parsing (to detect tests) to the currently -opened file, which in my opinion makes Neotest a joy to work with, even in -ginormous projects. Second, you can tweak the concurrency settings, again for -AST-parsing but also for concurrent test execution. Here is a simplistic example -for [lazy.nvim](https://github.com/folke/lazy.nvim) to show what I mean: - -```lua -return { - { - "nvim-neotest/neotest", - opts = { - -- See all config options with :h neotest.Config - discovery = { - -- Drastically improve performance in ginormous projects by - -- only AST-parsing the currently opened buffer. - enabled = false, - -- Number of workers to parse files concurrently. - -- A value of 0 automatically assigns number based on CPU. - -- Set to 1 if experiencing lag. - concurrent = 1, - }, - running = { - -- Run tests concurrently when an adapter provides multiple commands to run. - concurrent = true, - }, - summary = { - -- Enable/disable animation of icons. - animated = false, - }, - }, - }, -} -``` - -See `:h neotest.Config` for more information. - -[Here](https://github.com/fredrikaverpil/dotfiles/blob/main/nvim-fredrik/lua/fredrik/plugins/core/neotest.lua) -is my personal Neotest configuration, for inspiration. Please note that I am -configuring Go and the neotest-golang adapter in a separate file -[here](https://github.com/fredrikaverpil/dotfiles/blob/main/nvim-fredrik/lua/fredrik/plugins/lang/go.lua). - -### Go test execution and parallelism - -You can set the optional `go_test_args` to control the number of test binaries -and number of tests to run in parallel using the `-p` and `-parallel` flags, -respectively. Execute `go help test`, `go help testflag`, `go help build` for -more information on this. There's also an excellent article written by -[@roblaszczak](https://github.com/roblaszczak) posted -[here](https://threedots.tech/post/go-test-parallelism/) that touches on this -subject further. - -### Testify suites - -> [!WARNING] -> -> This feature comes with some caveats and nuances, which is why it is not -> enabled by default. I advise you to only enable this if you need it. - -There are some real shenaningans going on behind the scenes to make this work. -😅 First, an in-memory lookup of "receiver type-to-suite test function" will be -created of all Go test files in your project. Then, the generated Neotest node -tree is modified by mutating private attributes and merging of nodes to avoid -duplicates. I'm personally a bit afraid of the maintenance burden of this -feature... 🙈 - -> [!NOTE] -> -> Right now, nested tests and table tests are not supported. All of this can be -> remedied at any time by extending the treesitter queries. Feel free to dig in -> and open a PR! - -### Using build tags - -If you need to set build tags (like e.g. `-tags debug` or `-tags "tag1 tag2"`), -you need to provide these arguments both in the `go_test_args` and -`go_list_args` adapter options. If you want to be able to debug, you also need -to set `dap_go_opts`. Full example: - -```lua -return { - { - "nvim-neotest/neotest", - config = function() - require("neotest").setup({ - adapters = { - require("neotest-golang")({ - go_test_args = { "-count=1", "-tags=integration" }, - go_list_args = { "-tags=integration" }, - dap_go_opts = { - delve = { - build_flags = { "-tags=integration" }, - }, - }, - }), - }, - }) - end, - }, -} -``` - -> [!TIP] -> -> Depending on how you have Neovim setup, you can define this on a per-project -> basis by placing a `.lazy.lua` with overrides in the project. This requires -> the [lazy.nvim](https://github.com/folke/lazy.nvim) plugin manager. - -### Pass arguments as function instead of table - -Some use cases may require you to pass in dynamically generated arguments during -runtime. To cater for this, you can provide arguments as a function. - -```lua -return { - { - "nvim-neotest/neotest", - config = function() - require("neotest").setup({ - adapters = { - require("neotest-golang")({ - go_test_args = function() - -- provide custom logic here.. - return { "-count=1", "-tags=integration" } - end, - go_list_args = function() - -- provide custom logic here.. - return { "-tags=integration" } - end, - dap_go_opts = function() - -- provide custom logic here.. - return { - delve = { - build_flags = { "-tags=integration" }, - }, - } - end, - }, - }), - }, - }) - end, - }, -} -``` - -## 🙏 PRs are welcome - -Improvement suggestion PRs to this repo are very much welcome, and I encourage -you to begin by reading the below paragraph on the adapter design and engage in -the [discussions](https://github.com/fredrikaverpil/neotest-golang/discussions) -in case the change is not trivial. - -You can run tests, formatting and linting locally with `make all`. Install -dependencies with `make install`. Have a look at the [Makefile](Makefile) for -more details. You can also use the neotest-plenary and neotest-golang adapters -to run the tests of this repo within Neovim. - -### AST and tree-sitter - -To figure out new tree-sitter queries (for detecting tests), the following -commands are available in Neovim to aid you: - -- `:Inspect` to show the highlight groups under the cursor. -- `:InspectTree` to show the parsed syntax tree (formerly known as - "TSPlayground"). -- `:EditQuery` to open the Live Query Editor (Nvim 0.10+). - -For example, open up a Go test file and then execute `:InspectTree`. A new -window will appear which shows what the tree-sitter query syntax representation -looks like for the Go test file. - -Again, from the Go test file, execute `:EditQuery` to open up the query editor -in a separate window. In the editor, you can now start creating your syntax -query and play around. You can paste in queries from -[`query.lua`](https://github.com/fredrikaverpil/neotest-golang/blob/main/lua/neotest-golang/query.lua) -in the editor, to see how the query behaves and highlights parts of your Go test -file. - -## General design of the adapter - -### Treesitter queries detect tests - -Neotest leverages treesitter AST-parsing of source code to detect tests. This -adapter supplies queries so to figure out what is considered a test. - -From the result of these queries, a Neotest "position" tree is built (can be -visualized through the "Neotest summary"). Each position in the tree represents -either a `dir`, `file` or `test` type. Neotest also has a notion of a -`namespace` position type, but this is ignored by default by this adapter (but -leveraged to supply testify support). - -### Generating valid `go test` commands - -The `dir`, `file` and `test` tree position types cannot be directly translated -over to Go so to produce a valid `go test` command. Go primarily cares about a -Go package's import path, test name regexp filters and the current working -directory. - -For example, these are all valid `go test` command: - -```bash -# run all tests, recursing sub-packages, in the current working directory. -go test ./... - -# run all tests in a given package 'x', by specifying the full import path -go test github.com/fredrikaverpil/neotest-golang/x - -# run all tests in a given package 'x', recursing sub-packages -go test github.com/fredrikaverpil/neotest-golang/x/... - -# run _some_ tests in a given package, based on a regexp filter -go test github.com/fredrikaverpil/neotest-golang -run "^(^TestFoo$|^TestBar$)$" -``` - -> [!NOTE] -> -> All the above commands must be run somewhere beneath the location of the -> `go.mod` file specifying the _module_ name, which in this example is -> `github.com/fredrikaverpil/neotest-golang`. - -I figured out that by executing `go list -json ./...` in the `go.mod` root -location, the output provides valuable information about test files/folders and -their corresponding Go package's import path. This data is key to being able to -take the Neotest/treesitter position type and generate a valid `go test` command -for it. In essence, this approach is what makes neotest-golang so robust. - -### Output processing - -Neotest captures the stdout from the test execution command and writes it to -disk as a temporary file. The adapter is responsible for reading the file(s) and -reporting back status and output to the Neotest tree (and specifically the -position in the tree which was executed). It is therefore crucial for outputting -structured data, which in this case is done with `go test -json`. - -One challenge here is that Go build errors are not part of the strucutured JSON -output (although captured in the stdout) and needs to be looked for in other -ways. - -Another challenge is to properly populate statuses and errors into the -corresponding Neotest tree position. This becomes increasingly difficult when -you consider running tests in a recursive manner (e.g. `go test -json ./...`). - -Errors are recorded and populated, per position type, along with its -corresponding buffer's line number. Neotest can then show the errors inline as -diagnostics. - -I've taken an approach with this adapter where I record test outcome for each -Neotest position type and populate it onto each of them, when applicable. - -On some systems and terminals, there are great issues with the `go test` output. -I've therefore made it possible to make the adapter rely on output saved -directly to disk without going through stdout, by leveraging `gotestsum`. +Documentation is available at +[https://fredrikaverpil.github.io/neotest-golang](https://fredrikaverpil.github.io/neotest-golang) diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 0000000..6db2535 --- /dev/null +++ b/docs/config.md @@ -0,0 +1,238 @@ +--- +icon: material/cog +--- + +# Configuration + +## Options + +!!! tip "Recipes" + + See [the recipes](recipes.md) for usage examples of the below options. + +### `runner` + +Default value: `"go"` + +This option defines the test execution runner, which by default is set to `"go"` +and will use `go test` to write test output to stdout. + +!!! warning "Windows, Ubuntu Snaps" + + If you are on Windows or using Ubuntu snaps, you might want to set the runner + to `"gotestsum"` and/or enable the [`sanitize_output`](#sanitize_output) option. See + [this issue comment](https://github.com/fredrikaverpil/neotest-golang/issues/193#issuecomment-2362845806) + for more details and continue reading. + +To improve reliability, you can choose to set +[`gotestsum`](https://github.com/gotestyourself/gotestsum) as the test runner. +This tool allows the adapter to write test command output directly to a JSON +file without having to go through stdout. + +Using `gotestsum` offers the following benefits: + +- On certain platforms (such as Windows) or in certain terminals, there's a risk + of ANSI codes or other characters being seemingly randomly inserted into the + JSON test output. This can corrupt the data and cause problems with test + output JSON decoding. Enabling `gotestsum` eliminates these issues, as the + test output is then written directly to file. +- When you "attach" (in the Neotest summary window) to a running test, you'll + see clean `go test` output instead of having to navigate through + difficult-to-read JSON, as `gotestsum` is configured to _also_ output non-JSON + test execution to stdout. + +Gotestsum calls `go test` behind the scenes, so your `go_test_args` +configuration remains valid and will still apply. + +??? example "Configure neotest-golang to use `gotestsum` as test runner" + + Make the `gotestsum` command available via e.g. + [mason.nvim](https://github.com/williamboman/mason.nvim) or by running the + following in your shell: + + ```bash + go install gotest.tools/gotestsum@latest + ``` + + Then add the required configuration: + + ```lua + local config = { -- Specify configuration + runner = "gotestsum" + } + require("neotest").setup({ + adapters = { + require("neotest-golang")(config), -- Apply configuration + }, + }) + ``` + +### `go_test_args` + +Default value: `{ "-v", "-race", "-count=1" }` + +Arguments to pass into `go test`. See `go help test`, `go help testflag`, +`go help build` for possible arguments. + +The `-json` flag is mandatory and is always appended to `go test` automatically. + +The value can also be passed in as a function. + +!!! warning "CGO" + + The `-race` flag (in `go_test_args`) requires CGO to be enabled + (`CGO_ENABLED=1` is the default) and a C compiler (such as GCC) to be + installed. However, since Go 1.20, this is not a requirement on macOS. I have + included the `-race` argument as default, as it provides good production + defaults. See [this issue](https://github.com/golang/go/issues/9918) for more + details. + +### `gotestsum_args` + +Default value: `{ "--format=standard-verbose" }` + +Arguments to pass into `gotestsum`. Will only be applicable if +`runner = "gotestsum"`. + +The value can also be passed in as a function. + +### `go_list_args` + +Default value: `{}` + +Arguments to pass into `go list`. The main purpose of `go list` is to internally +translate between filepaths and Go package import paths. + +A mandatory query is passed into the `-f` flag and is always appended +automatically, so that the necessary fields can be extracted. + +The value can also be passed in as a function. + +### `dap_mode` + +Default value: `"dap-go"` + +This option toggles between relying on +[leoluz/nvim-dap-go](https://github.com/leoluz/nvim-dap-go) so supply the delve +DAP configuration, or whether you wish to bring your own DAP configuration. + +Set to `"manual"` for manual configuration. + +The value can also be passed in as a function. + +### `dap_go_opts` + +Default value: `{}` + +If using `dap_mode = "dap-go"`, you can supply dap-go with custom options. + +The value can also be passed in as a function. + +### `dap_manual_config` + +Default value: `{}` + +The configuration to apply if `dap_mode == "manual"`. + +The value can also be passed in as a function. + +### `testify_enabled` + +Default value: `false` + +Enable support for [testify](https://github.com/stretchr/testify) suites. + +!!! warning "Not enabled by default" + + This feature comes with some caveats and nuances, which is why it is not enabled + by default. I advise you to only enable this if you need it. + + There are some real shenaningans going on behind the scenes to make this work. + 😅 First, an in-memory lookup of "receiver type-to-suite test function" will be + created of all Go test files in your project. Then, the generated Neotest node + tree is modified by mutating private attributes and merging of nodes to avoid + duplicates. I'm personally a bit afraid of the maintenance burden of this + feature... 🙈 + +!!! note "Subtests not supported" + + Right now, nested tests and table tests are not supported. All of this can be + remedied at any time by extending the treesitter queries. Feel free to dig in + and open a PR! + +### `colorize_test_output` + +Default value: `true` + +Enable output color for `SUCCESS`, `FAIL`, and `SKIP` tests. + +### `warn_test_name_dupes` + +Default value: `true` + +Warn about duplicate test names within the same Go package. + +### `warn_test_not_executed` + +Default value: `true` + +Warn if test was not executed. + +### `log_level` + +Default value: `"vim.log.levels.WARN"` + +The `neotest-golang.log` log-level. + +### `sanitize_output` + +Default value: `false` + +Filter control characters and non-printable characters from test output. +Requires the [uga-rosa/utf8.nvim](https://github.com/uga-rosa/utf8.nvim) +library. + +When tests write non-printable characters to stdout/stderr, they can cause +various issues like failing to write output to disk or UI rendering problems. +The `sanitize_output` option helps clean up such output by preserving UTF-8 and +replacing control characters with the Unicode replacement character (�). + +This is particularly useful when: + +- Tests write bytes to stdout/stderr. +- Test output contains terminal control sequences. +- Test output includes non-printable characters. + +The sanitization preserves all regular printable characters including tabs, +newlines, and carriage returns. + +??? example "Example config" + + ```diff + return { + { + "nvim-neotest/neotest", + dependencies = { + "nvim-neotest/nvim-nio", + "nvim-lua/plenary.nvim", + "antoinemadec/FixCursorHold.nvim", + "nvim-treesitter/nvim-treesitter", + - "fredrikaverpil/neotest-golang", -- Installation + + { + + "fredrikaverpil/neotest-golang", -- Installation + + dependencies = { + + "uga-rosa/utf8.nvim", -- Additional dependency required + + }, + + }, + }, + config = function() + require("neotest").setup({ + adapters = { + - require("neotest-golang"), -- Registration + + require("neotest-golang")({ sanitize_output = true }), -- Registration + }, + }) + end, + }, + } + ``` diff --git a/docs/contrib.md b/docs/contrib.md new file mode 100644 index 0000000..23c7b80 --- /dev/null +++ b/docs/contrib.md @@ -0,0 +1,111 @@ +--- +icon: material/heart-multiple-outline +--- + +# 🙏 PRs are welcome + +Improvement suggestion PRs to this repo are very much welcome, and I encourage +you to begin by reading the below paragraph on the adapter design and engage in +the [discussions](https://github.com/fredrikaverpil/neotest-golang/discussions) +in case the change is not trivial. + +You can run tests, formatting and linting locally with `make all`. Install +dependencies with `make install`. Have a look at the `Makefile` for more +details. You can also use the neotest-plenary and neotest-golang adapters to run +the tests of this repo within Neovim. + +### AST and tree-sitter + +To figure out new tree-sitter queries (for detecting tests), the following +commands are available in Neovim to aid you: + +- `:Inspect` to show the highlight groups under the cursor. +- `:InspectTree` to show the parsed syntax tree (formerly known as + "TSPlayground"). +- `:EditQuery` to open the Live Query Editor (Nvim 0.10+). + +For example, open up a Go test file and then execute `:InspectTree`. A new +window will appear which shows what the tree-sitter query syntax representation +looks like for the Go test file. + +Again, from the Go test file, execute `:EditQuery` to open up the query editor +in a separate window. In the editor, you can now start creating your syntax +query and play around. You can paste in queries from +[`query.lua`](https://github.com/fredrikaverpil/neotest-golang/blob/main/lua/neotest-golang/query.lua) +in the editor, to see how the query behaves and highlights parts of your Go test +file. + +## General design of the adapter + +### Treesitter queries detect tests + +Neotest leverages treesitter AST-parsing of source code to detect tests. This +adapter supplies queries so to figure out what is considered a test. + +From the result of these queries, a Neotest "position" tree is built (can be +visualized through the "Neotest summary"). Each position in the tree represents +either a `dir`, `file` or `test` type. Neotest also has a notion of a +`namespace` position type, but this is ignored by default by this adapter (but +leveraged to supply testify support). + +### Generating valid `go test` commands + +The `dir`, `file` and `test` tree position types cannot be directly translated +over to Go so to produce a valid `go test` command. Go primarily cares about a +Go package's import path, test name regexp filters and the current working +directory. + +For example, these are all valid `go test` command: + +```bash +# run all tests, recursing sub-packages, in the current working directory. +go test ./... + +# run all tests in a given package 'x', by specifying the full import path +go test github.com/fredrikaverpil/neotest-golang/x + +# run all tests in a given package 'x', recursing sub-packages +go test github.com/fredrikaverpil/neotest-golang/x/... + +# run _some_ tests in a given package, based on a regexp filter +go test github.com/fredrikaverpil/neotest-golang -run "^(^TestFoo$|^TestBar$)$" +``` + +> [!NOTE] +> +> All the above commands must be run somewhere beneath the location of the +> `go.mod` file specifying the _module_ name, which in this example is +> `github.com/fredrikaverpil/neotest-golang`. + +I figured out that by executing `go list -json ./...` in the `go.mod` root +location, the output provides valuable information about test files/folders and +their corresponding Go package's import path. This data is key to being able to +take the Neotest/treesitter position type and generate a valid `go test` command +for it. In essence, this approach is what makes neotest-golang so robust. + +### Output processing + +Neotest captures the stdout from the test execution command and writes it to +disk as a temporary file. The adapter is responsible for reading the file(s) and +reporting back status and output to the Neotest tree (and specifically the +position in the tree which was executed). It is therefore crucial for outputting +structured data, which in this case is done with `go test -json`. + +One challenge here is that Go build errors are not part of the strucutured JSON +output (although captured in the stdout) and needs to be looked for in other +ways. + +Another challenge is to properly populate statuses and errors into the +corresponding Neotest tree position. This becomes increasingly difficult when +you consider running tests in a recursive manner (e.g. `go test -json ./...`). + +Errors are recorded and populated, per position type, along with its +corresponding buffer's line number. Neotest can then show the errors inline as +diagnostics. + +I've taken an approach with this adapter where I record test outcome for each +Neotest position type and populate it onto each of them, when applicable. + +On some systems and terminals, there are great issues with the `go test` output. +I've therefore made it possible to make the adapter rely on output saved +directly to disk without going through stdout, by leveraging `gotestsum`. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..343493a --- /dev/null +++ b/docs/index.md @@ -0,0 +1,33 @@ +--- +icon: material/information +--- + +--8<-- "README.md" + +--- + +??? question "Why a second Neotest adapter for Go? 🤔" + + While using [neotest-go](https://github.com/nvim-neotest/neotest-go) I stumbled + upon many problems which seemed difficult to solve in that codebase. + + I have full respect for the time and efforts put in by the developer(s) of + neotest-go. I do not aim in any way to diminish their needs or efforts. However, + I wanted to see if I could fix these issues by diving into the 🕳️🐇 of Neotest + and building my own adapter. Below is a list of neotest-go issues which are not + present in neotest-golang (this project): + + | Neotest-go issue | URL | + | ------------------------------------------------------- | --------------------------------------------------------------------- | + | Support for Testify framework | [neotest-go#6](https://github.com/nvim-neotest/neotest-go/issues/6) | + | DAP support | [neotest-go#12](https://github.com/nvim-neotest/neotest-go/issues/12) | + | Test Output in JSON, making it difficult to read | [neotest-go#52](https://github.com/nvim-neotest/neotest-go/issues/52) | + | Support for Nested Subtests | [neotest-go#74](https://github.com/nvim-neotest/neotest-go/issues/74) | + | Diagnostics for table tests on the line of failure | [neotest-go#75](https://github.com/nvim-neotest/neotest-go/issues/75) | + | "Run nearest" runs all tests | [neotest-go#83](https://github.com/nvim-neotest/neotest-go/issues/83) | + | Table tests not recognized when defined inside for-loop | [neotest-go#86](https://github.com/nvim-neotest/neotest-go/issues/86) | + | Running test suite doesn't work | [neotest-go#89](https://github.com/nvim-neotest/neotest-go/issues/89) | + + A comparison in number of GitHub stars between the projects: + + [![Star History Chart](https://api.star-history.com/svg?repos=fredrikaverpil/neotest-golang,nvim-neotest/neotest-go&type=Date)](https://star-history.com/#fredrikaverpil/neotest-golang&nvim-neotest/neotest-go&Date) diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000..a8bec70 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,109 @@ +--- +icon: material/progress-check +--- + +# Installation + +!!! warning "Minimum Neovim version" + + Neovim v0.10.0 or above is required. + +## 💤 Lazy.nvim + +```lua +return { + { + "nvim-neotest/neotest", + dependencies = { + "nvim-neotest/nvim-nio", + "nvim-lua/plenary.nvim", + "antoinemadec/FixCursorHold.nvim", + "nvim-treesitter/nvim-treesitter", + { "fredrikaverpil/neotest-golang", version = "*" }, -- Installation + }, + config = function() + local neotest_golang_opts = {} -- Specify custom configuration + require("neotest").setup({ + adapters = { + require("neotest-golang")(neotest_golang_opts), -- Registration + }, + }) + end, + }, +} +``` + +For increased stability and less updating noise, I recommend that you track +official releases by setting `version = "*"`. By omitting this option (or +setting `version = false`), you will get the latest and greatest directly from +the main branch. + +I do not recommend pinning to a specific version or to a major version. But +ultimately it is up to you what you want. + +See the [Lazy versioning spec](https://lazy.folke.io/spec/versioning) for more +details. + +## 🌒 Rocks.nvim + +The adapter is available via +[luarocks package](https://luarocks.org/modules/fredrikaverpil/neotest-golang): + +```vim +:Rocks install neotest-golang +``` + +[rocks.nvim](https://github.com/nvim-neorocks/rocks.nvim) will automatically +install dependencies if they are not already installed. You will need to call +neotest's `setup` function to register this adapter. If you use +[rocks-config.nvim](https://github.com/nvim-neorocks/rocks-config.nvim), +consider setting up neotest and its adapters in a +[plugin bundle](https://github.com/nvim-neorocks/rocks-config.nvim?tab=readme-ov-file#plugin-bundles). + +!!! note "Luarocks" + + Please note that [leoluz/nvim-dap-go](https://github.com/leoluz/nvim-dap-go) + (required for DAP) is not on luarocks as of writing this. + +## ❄️ Nix & Home manager + +```nix +{ + config, + pkgs, + ... +}: { + home.packages = with pkgs; []; + programs = { + neovim = { + plugins = [ + # neotest and dependencies + pkgs.vimPlugins.neotest + pkgs.vimPlugins.nvim-nio + pkgs.vimPlugins.plenary-nvim + pkgs.vimPlugins.FixCursorHold-nvim + pkgs.vimPlugins.nvim-treesitter + (pkgs.vimPlugins.nvim-treesitter.withPlugins (plugins: [plugins.go])) + pkgs.vimPlugins.neotest-golang + + ## debugging + pkgs.vimPlugins.nvim-dap + pkgs.vimPlugins.nvim-dap-ui + pkgs.vimPlugins.nvim-nio + pkgs.vimPlugins.nvim-dap-virtual-text + pkgs.vimPlugins.nvim-dap-go + ]; + enable = true; + extraConfig = '' + lua << EOF + require("neotest").setup({ + adapters = { + require("neotest-golang") + }, + }) + EOF + ''; + }; + }; +} +``` diff --git a/docs/recipes.md b/docs/recipes.md new file mode 100644 index 0000000..891d93a --- /dev/null +++ b/docs/recipes.md @@ -0,0 +1,405 @@ +--- +icon: material/food +--- + +# Recipes + +## Debugging + +To debug tests, make sure you depend on +[mfussenegger/nvim-dap](https://github.com/mfussenegger/nvim-dap) and +[rcarriga/nvim-dap-ui](https://github.com/rcarriga/nvim-dap-ui). + +Then you have two options: + +- DAP configuration provided by + [leoluz/nvim-dap-go](https://github.com/leoluz/nvim-dap-go) (recommended) +- Use your own custom DAP configuration (no additional dependency needed) + +??? example "Adapter-provided (recommended)" + + ```diff + return { + + { + + "rcarriga/nvim-dap-ui", + + dependencies = { + + "nvim-neotest/nvim-nio", + + "mfussenegger/nvim-dap", + + }, + + }, + + + { + "nvim-neotest/neotest", + dependencies = { + "nvim-neotest/nvim-nio", + "nvim-lua/plenary.nvim", + "antoinemadec/FixCursorHold.nvim", + "nvim-treesitter/nvim-treesitter", + - "fredrikaverpil/neotest-golang", -- Installation + + { + + "fredrikaverpil/neotest-golang", -- Installation + + dependencies = { + + "leoluz/nvim-dap-go", + + }, + + }, + }, + config = function() + require("neotest").setup({ + adapters = { + require("neotest-golang"), -- Registration + }, + }) + end, + }, + } + ``` + +??? example "Use your own custom DAP configuration" + + ```diff + return { + + { + + "rcarriga/nvim-dap-ui", + + dependencies = { + + "nvim-neotest/nvim-nio", + + "mfussenegger/nvim-dap", + + }, + + }, + + + { + "nvim-neotest/neotest", + dependencies = { + "nvim-neotest/nvim-nio", + "nvim-lua/plenary.nvim", + "antoinemadec/FixCursorHold.nvim", + "nvim-treesitter/nvim-treesitter", + "fredrikaverpil/neotest-golang", -- Installation + }, + config = function() + + local options = { + + dap_mode = "manual", + + dap_manual_config = { + + name = "Debug go tests", + + type = "go", -- Preconfigured DAP adapter name + + request = "launch", + + mode = "test", + + }, + + } + require("neotest").setup({ + adapters = { + + require("neotest-golang")(options) -- Registration + }, + }) + end, + }, + } + ``` + + + +Finally, set keymaps to run Neotest commands. + +!!! example "Keymap for running nearest test" + + ```lua + return { + { + "nvim-neotest/neotest", + -- ... + keys = { + { + "td", + function() + require("neotest").run.run({ suite = false, strategy = "dap" }) + end, + desc = "Debug nearest test", + }, + }, + }, + } + ``` + +## Coverage + +You can use +[andythigpen/nvim-coverage](https://github.com/andythigpen/nvim-coverage) to +show coverage in Neovim. + +!!! example "Coverage" + + ```lua + return { + { + "nvim-neotest/neotest", + dependencies = { + "nvim-neotest/nvim-nio", + "nvim-lua/plenary.nvim", + "antoinemadec/FixCursorHold.nvim", + "nvim-treesitter/nvim-treesitter", + { + "fredrikaverpil/neotest-golang", + version = "*", + dependencies = { + "andythigpen/nvim-coverage", -- Added dependency + }, + }, + }, + config = function() + local neotest_golang_opts = { -- Specify configuration + runner = "go", + go_test_args = { + "-v", + "-race", + "-count=1", + "-coverprofile=" .. vim.fn.getcwd() .. "/coverage.out", + }, + } + require("neotest").setup({ + adapters = { + require("neotest-golang")(neotest_golang_opts), -- Registration + }, + }) + end, + }, + } + ``` + +## Using build tags + +If you need to set build tags (like e.g. `-tags debug` or `-tags "tag1 tag2"`), +you need to provide these arguments both in the `go_test_args` and +`go_list_args` adapter options. If you want to be able to debug, you also need +to set `dap_go_opts`. Full example: + +!!! example "Build tags" + + ```lua + return { + { + "nvim-neotest/neotest", + config = function() + require("neotest").setup({ + adapters = { + require("neotest-golang")({ + go_test_args = { "-count=1", "-tags=integration" }, + go_list_args = { "-tags=integration" }, + dap_go_opts = { + delve = { + build_flags = { "-tags=integration" }, + }, + }, + }), + }, + }) + end, + }, + } + ``` + +## Pass arguments as function instead of table + +Some use cases may require you to pass in dynamically generated arguments during +runtime. To cater for this, you can provide arguments as a function. + +!!! example "Args passed as functions" + + ```lua + return { + { + "nvim-neotest/neotest", + config = function() + require("neotest").setup({ + adapters = { + require("neotest-golang")({ + go_test_args = function() + -- provide custom logic here.. + return { "-count=1", "-tags=integration" } + end, + go_list_args = function() + -- provide custom logic here.. + return { "-tags=integration" } + end, + dap_go_opts = function() + -- provide custom logic here.. + return { + delve = { + build_flags = { "-tags=integration" }, + }, + } + end, + }, + }), + }, + }) + end, + }, + } + ``` + +## Per-project configuration + +Depending on how you have Neovim setup, you can define the neotest-golang config +on a per-project basis by placing a `.lazy.lua` with overrides in the project. +This requires the [lazy.nvim](https://github.com/folke/lazy.nvim) plugin +manager. + +## Example configuration: extra everything + +In the below code block, I've provided a pretty hefty configuration example, +which includes the required setup for testing and debugging along with all the +keymaps. This is a merged snapshot of my own config, which I hope you can draw +inspiration from. To view my current config, which is divided up into several +files, see: + +- [plugins/neotest.lua](https://github.com/fredrikaverpil/dotfiles/blob/main/nvim-fredrik/lua/fredrik/plugins/core/neotest.lua) +- [plugins/dap.lua](https://github.com/fredrikaverpil/dotfiles/blob/main/nvim-fredrik/lua/fredrik/plugins/core/dap.lua) +- [lang/go.lua](https://github.com/fredrikaverpil/dotfiles/blob/main/nvim-fredrik/lua/fredrik/plugins/lang/go.lua) + +!!! tip "Extra everything" + + ```lua + return { + + -- Neotest setup + { + "nvim-neotest/neotest", + event = "VeryLazy", + dependencies = { + "nvim-neotest/nvim-nio", + "nvim-lua/plenary.nvim", + "antoinemadec/FixCursorHold.nvim", + "nvim-treesitter/nvim-treesitter", + + "nvim-neotest/neotest-plenary", + "nvim-neotest/neotest-vim-test", + + { + "fredrikaverpil/neotest-golang", + dependencies = { + { + "leoluz/nvim-dap-go", + opts = {}, + }, + }, + branch = "main", + }, + }, + opts = function(_, opts) + opts.adapters = opts.adapters or {} + opts.adapters["neotest-golang"] = { + go_test_args = { + "-v", + "-race", + "-coverprofile=" .. vim.fn.getcwd() .. "/coverage.out", + }, + } + end, + config = function(_, opts) + if opts.adapters then + local adapters = {} + for name, config in pairs(opts.adapters or {}) do + if type(name) == "number" then + if type(config) == "string" then + config = require(config) + end + adapters[#adapters + 1] = config + elseif config ~= false then + local adapter = require(name) + if type(config) == "table" and not vim.tbl_isempty(config) then + local meta = getmetatable(adapter) + if adapter.setup then + adapter.setup(config) + elseif adapter.adapter then + adapter.adapter(config) + adapter = adapter.adapter + elseif meta and meta.__call then + adapter(config) + else + error("Adapter " .. name .. " does not support setup") + end + end + adapters[#adapters + 1] = adapter + end + end + opts.adapters = adapters + end + + require("neotest").setup(opts) + end, + keys = { + { "ta", function() require("neotest").run.attach() end, desc = "[t]est [a]ttach" }, + { "tf", function() require("neotest").run.run(vim.fn.expand("%")) end, desc = "[t]est run [f]ile" }, + { "tA", function() require("neotest").run.run(vim.uv.cwd()) end, desc = "[t]est [A]ll files" }, + { "tS", function() require("neotest").run.run({ suite = true }) end, desc = "[t]est [S]uite" }, + { "tn", function() require("neotest").run.run() end, desc = "[t]est [n]earest" }, + { "tl", function() require("neotest").run.run_last() end, desc = "[t]est [l]ast" }, + { "ts", function() require("neotest").summary.toggle() end, desc = "[t]est [s]ummary" }, + { "to", function() require("neotest").output.open({ enter = true, auto_close = true }) end, desc = "[t]est [o]utput" }, + { "tO", function() require("neotest").output_panel.toggle() end, desc = "[t]est [O]utput panel" }, + { "tt", function() require("neotest").run.stop() end, desc = "[t]est [t]erminate" }, + { "td", function() require("neotest").run.run({ suite = false, strategy = "dap" }) end, desc = "Debug nearest test" }, + { "tD", function() require("neotest").run.run({ vim.fn.expand("%"), strategy = "dap" }) end, desc = "Debug current file" }, + }, + }, + + -- DAP setup + { + "mfussenegger/nvim-dap", + event = "VeryLazy", + keys = { + {"db", function() require("dap").toggle_breakpoint() end, desc = "toggle [d]ebug [b]reakpoint" }, + {"dB", function() require("dap").set_breakpoint(vim.fn.input("Breakpoint condition: ")) end, desc = "[d]ebug [B]reakpoint"}, + {"dc", function() require("dap").continue() end, desc = "[d]ebug [c]ontinue (start here)" }, + {"dC", function() require("dap").run_to_cursor() end, desc = "[d]ebug [C]ursor" }, + {"dg", function() require("dap").goto_() end, desc = "[d]ebug [g]o to line" }, + {"do", function() require("dap").step_over() end, desc = "[d]ebug step [o]ver" }, + {"dO", function() require("dap").step_out() end, desc = "[d]ebug step [O]ut" }, + {"di", function() require("dap").step_into() end, desc = "[d]ebug [i]nto" }, + {"dj", function() require("dap").down() end, desc = "[d]ebug [j]ump down" }, + {"dk", function() require("dap").up() end, desc = "[d]ebug [k]ump up" }, + {"dl", function() require("dap").run_last() end, desc = "[d]ebug [l]ast" }, + {"dp", function() require("dap").pause() end, desc = "[d]ebug [p]ause" }, + {"dr", function() require("dap").repl.toggle() end, desc = "[d]ebug [r]epl" }, + {"dR", function() require("dap").clear_breakpoints() end, desc = "[d]ebug [R]emove breakpoints" }, + {"ds", function() require("dap").session() end, desc ="[d]ebug [s]ession" }, + {"dt", function() require("dap").terminate() end, desc = "[d]ebug [t]erminate" }, + {"dw", function() require("dap.ui.widgets").hover() end, desc = "[d]ebug [w]idgets" }, + }, + }, + + -- DAP UI setup + { + "rcarriga/nvim-dap-ui", + event = "VeryLazy", + dependencies = { + "nvim-neotest/nvim-nio", + "mfussenegger/nvim-dap", + }, + opts = {}, + config = function(_, opts) + -- setup dap config by VsCode launch.json file + -- require("dap.ext.vscode").load_launchjs() + local dap = require("dap") + local dapui = require("dapui") + dapui.setup(opts) + dap.listeners.after.event_initialized["dapui_config"] = function() + dapui.open({}) + end + dap.listeners.before.event_terminated["dapui_config"] = function() + dapui.close({}) + end + dap.listeners.before.event_exited["dapui_config"] = function() + dapui.close({}) + end + end, + keys = { + { "du", function() require("dapui").toggle({}) end, desc = "[d]ap [u]i" }, + { "de", function() require("dapui").eval() end, desc = "[d]ap [e]val" }, + }, + }, + { + "theHamsta/nvim-dap-virtual-text", + opts = {}, + }, + } + ``` diff --git a/docs/trouble.md b/docs/trouble.md new file mode 100644 index 0000000..50f3655 --- /dev/null +++ b/docs/trouble.md @@ -0,0 +1,103 @@ +--- +icon: material/bug +--- + +# Troubleshooting + +## Issues with setting up or using the adapter + +> [!TIP] +> +> You can run `:checkhealth neotest-golang` to review common issues. If you need +> configuring neotest-golang help, please open a discussion +> [here](https://github.com/fredrikaverpil/neotest-golang/discussions/new?category=configuration). + +You can also enable logging to further inspect what's going on under the hood. +Neotest-golang piggybacks on the Neotest logger but writes its own file. The +default log level is `WARN` but during troubleshooting you want to increase +this: + +```lua +local config = { + log_level = vim.log.levels.TRACE, -- set log level +} + +require("neotest").setup({ + adapters = { + require("neotest-golang")(config), -- Apply configuration + }, +}) +``` + +The neotest-golang logs can be opened using this convenient vim command: + +```vim +:exe 'edit' stdpath('log').'/neotest-golang.log' +``` + +This usually corresponds to something like +`~/.local/state/nvim/neotest-golang.log`. + +> [!WARNING] +> +> Don't forget to revert back to `WARN` level once you are done troubleshooting, +> as the `TRACE` level can degrade performance. + +## Neotest is slowing down Neovim + +Neotest, out of the box with default settings, can appear very slow in large +projects (here, I'm referring to +[this kind of large](https://github.com/kubernetes/kubernetes)). There are a few +things you can do to speed up the Neotest appearance and experience in such +cases, by tweaking the Neotest settings. + +You can for example limit the AST-parsing (to detect tests) to the currently +opened file, which in my opinion makes Neotest a joy to work with, even in +ginormous projects. Second, you can tweak the concurrency settings, again for +AST-parsing but also for concurrent test execution. Here is a simplistic example +for [lazy.nvim](https://github.com/folke/lazy.nvim) to show what I mean: + +```lua +return { + { + "nvim-neotest/neotest", + opts = { + -- See all config options with :h neotest.Config + discovery = { + -- Drastically improve performance in ginormous projects by + -- only AST-parsing the currently opened buffer. + enabled = false, + -- Number of workers to parse files concurrently. + -- A value of 0 automatically assigns number based on CPU. + -- Set to 1 if experiencing lag. + concurrent = 1, + }, + running = { + -- Run tests concurrently when an adapter provides multiple commands to run. + concurrent = true, + }, + summary = { + -- Enable/disable animation of icons. + animated = false, + }, + }, + }, +} +``` + +See `:h neotest.Config` for more information. + +[Here](https://github.com/fredrikaverpil/dotfiles/blob/main/nvim-fredrik/lua/fredrik/plugins/core/neotest.lua) +is my personal Neotest configuration, for inspiration. Please note that I am +configuring Go and the neotest-golang adapter in a separate file +[here](https://github.com/fredrikaverpil/dotfiles/blob/main/nvim-fredrik/lua/fredrik/plugins/lang/go.lua). + +## Go test execution and parallelism + +You can set the optional `go_test_args` to control the number of test binaries +and number of tests to run in parallel using the `-p` and `-parallel` flags, +respectively. Execute `go help test`, `go help testflag`, `go help build` for +more information on this. There's also an excellent article written by +[@roblaszczak](https://github.com/roblaszczak) posted +[here](https://threedots.tech/post/go-test-parallelism/) that touches on this +subject further. diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..8c16ece --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,86 @@ +site_name: neotest-golang +site_url: https://neotest-golang.github.io +repo_url: https://github.com/fredrikaverpil/neotest-golang +docs_dir: docs +watch: + - pyproject.toml + - uv.lock + - mkdocs.yml + - README.md + - docs +theme: + name: material + font: false + icon: + logo: material/test-tube + admonition: + # note: octicons/tag-16 + # abstract: octicons/checklist-16 + # info: octicons/info-16 + # tip: octicons/squirrel-16 + # success: octicons/check-16 + # question: octicons/question-16 + # warning: octicons/alert-16 + # failure: octicons/x-circle-16 + # danger: octicons/zap-16 + # bug: octicons/bug-16 + # example: octicons/beaker-16 + # quote: octicons/quote-16 + edit_uri: edit/main/docs/ + features: + - navigation.tracking + # - navigation.tabs + # - navigation.tabs.sticky + # - navigation.sections + # - navigation.indexes + - content.action.edit + - content.action.view + - content.code.copy + + palette: + # Palette toggle for automatic mode + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-auto + name: Switch to light mode + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: black + toggle: + icon: material/brightness-4 + name: Switch to system preference + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode +plugins: + - search +markdown_extensions: + - tables + - md_in_html + - admonition + - pymdownx.details + - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true + - pymdownx.highlight: + # use_pygments: true + auto_title: true + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.smartsymbols + - pymdownx.snippets: + url_download: true + check_paths: true +nav: + - About this project: index.md + - Installation: install.md + - Configuration: config.md + - Recipes: recipes.md + - Troubleshooting: trouble.md + - Contributing: contrib.md diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5425f91 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "neotest-golang" +version = "0.1.0" +description = "Neotest-golang docs" +readme = "README.md" +requires-python = ">=3.13" +dependencies = ["mkdocs-material", "mkdocs-simple-hooks"] diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..297d87f --- /dev/null +++ b/uv.lock @@ -0,0 +1,414 @@ +version = 1 +requires-python = ">=3.13" + +[[package]] +name = "babel" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, +] + +[[package]] +name = "certifi" +version = "2024.12.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "jinja2" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, +] + +[[package]] +name = "markdown" +version = "3.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354 }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451 }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mergedeep" }, + { name = "platformdirs" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521 }, +] + +[[package]] +name = "mkdocs-material" +version = "9.5.50" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "colorama" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "mkdocs" }, + { name = "mkdocs-material-extensions" }, + { name = "paginate" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/16/c48d5a28bc4a67c49808180b6009d4d1b4c0753739ffee3cc37046ab29d7/mkdocs_material-9.5.50.tar.gz", hash = "sha256:ae5fe16f3d7c9ccd05bb6916a7da7420cf99a9ce5e33debd9d40403a090d5825", size = 3923354 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/b5/1bf29cd744896ae83bd38c72970782c843ba13e0240b1a85277bd3928637/mkdocs_material-9.5.50-py3-none-any.whl", hash = "sha256:f24100f234741f4d423a9d672a909d859668a4f404796be3cf035f10d6050385", size = 8645274 }, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728 }, +] + +[[package]] +name = "mkdocs-simple-hooks" +version = "0.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/93/565f98d6810e3b493e61160aea1cceb8653331576e7fa7f048ac7e7cdf62/mkdocs-simple-hooks-0.1.5.tar.gz", hash = "sha256:dddbdf151a18723c9302a133e5cf79538be8eb9d274e8e07d2ac3ac34890837c", size = 4037 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/e9/7bf0f928f5b6cdd602d4a01d52e5fc1eba8d3ba6d97619a88fc271a625f8/mkdocs_simple_hooks-0.1.5-py3-none-any.whl", hash = "sha256:efeabdbb98b0850a909adee285f3404535117159d5cb3a34f541d6eaa644d50a", size = 4596 }, +] + +[[package]] +name = "neotest-golang" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "mkdocs-material" }, + { name = "mkdocs-simple-hooks" }, +] + +[package.metadata] +requires-dist = [ + { name = "mkdocs-material" }, + { name = "mkdocs-simple-hooks" }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "paginate" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746 }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + +[[package]] +name = "pymdown-extensions" +version = "10.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/96/b4337b778d2e9e77541a8d1cab00989aaeb1d6003c891cdc89221bd25651/pymdown_extensions-10.14.tar.gz", hash = "sha256:741bd7c4ff961ba40b7528d32284c53bc436b8b1645e8e37c3e57770b8700a34", size = 844927 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/ae/55d347eda5a4c57a2a042fe2e7616d14981115f566b9f8f69901aba3c0c6/pymdown_extensions-10.14-py3-none-any.whl", hash = "sha256:202481f716cc8250e4be8fce997781ebf7917701b59652458ee47f2401f818b5", size = 264264 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911 }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 }, + { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 }, + { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 }, + { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 }, + { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 }, + { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 }, + { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 }, + { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 }, + { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 }, + { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 }, + { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 }, + { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 }, + { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, + { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, + { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480 }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451 }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057 }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, +]