From 123dbc2463aef2c547212a3a961e3018e722a326 Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Thu, 7 Mar 2024 22:14:22 -0500 Subject: [PATCH] fix(nextls): force a download (#203) --- .github/workflows/ci.yml | 63 +++++++++++++++++---- .gitignore | 5 ++ .luarc.json | 8 ++- README.md | 38 ++++++++++++- busted/fixtures/basic/lib/basic.ex | 5 ++ busted/fixtures/basic/mix.exs | 32 +++++++++++ busted/nextls/install_spec.lua | 88 ++++++++++++++++++++++++++++++ flake.lock | 64 ++++++++++++++++++++++ flake.nix | 43 +++++++++++++++ justfile | 22 ++++++++ lua/elixir/elixirls/compile.lua | 2 +- lua/elixir/elixirls/download.lua | 4 +- lua/elixir/elixirls/init.lua | 12 ++-- lua/elixir/init.lua | 7 ++- lua/elixir/nextls/init.lua | 27 ++++----- lua/elixir/utils.lua | 8 ++- 16 files changed, 385 insertions(+), 43 deletions(-) create mode 100644 busted/fixtures/basic/lib/basic.ex create mode 100644 busted/fixtures/basic/mix.exs create mode 100644 busted/nextls/install_spec.lua create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 justfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cfe8948..477c9529 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,8 +6,22 @@ concurrency: cancel-in-progress: true jobs: - unit_tests: - name: unit tests + stylua: + name: Stylua + runs-on: ubuntu-latest + + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true) + + steps: + - uses: actions/checkout@v4 + - uses: JohnnyMorganz/stylua-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: latest + args: --check . + + legacy_tests: + name: legacy tests runs-on: ${{ matrix.os }} if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true) @@ -41,12 +55,6 @@ jobs: packages: fd elixir steps: - uses: actions/checkout@v4 - - uses: JohnnyMorganz/stylua-action@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - version: latest - args: --check . - - run: date +%F > todays-date - name: Restore from todays cache uses: actions/cache@v4 @@ -54,7 +62,7 @@ jobs: path: _neovim key: ${{ runner.os }}-${{ matrix.url }}-${{ hashFiles('todays-date') }} - - name: Add Repositoy + - name: Add Repository if: matrix.os == 'ubuntu-20.04' run: wget https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb && sudo dpkg -i erlang-solutions_2.0_all.deb @@ -67,7 +75,7 @@ jobs: curl -sL ${{ matrix.url }} | tar xzf - --strip-components=1 -C "${PWD}/_neovim" } - - name: Run tests + - name: Run legacy tests env: BUSTED_TIMEOUT: 600000 run: | @@ -75,3 +83,38 @@ jobs: export VIM="${PWD}/_neovim/share/nvim/runtime" nvim --version bin/test + + tests: + name: tests + runs-on: ${{ matrix.os }} + + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true) + + strategy: + matrix: + include: + - os: ubuntu-20.04 + manager: sudo apt-get + packages: -y fd-find esl-erlang elixir + nvim-version: 0.8.3 + - os: ubuntu-20.04 + manager: sudo apt-get + packages: -y fd-find esl-erlang elixir + nvim-version: 0.9.5 + - os: macos-14 + manager: brew + packages: fd elixir + nvim-version: 0.8.3 + - os: macos-14 + manager: brew + packages: fd elixir + nvim-version: 0.9.5 + steps: + - uses: actions/checkout@v4 + - uses: extractions/setup-just@v1 + - uses: leafo/gh-actions-lua@v10 + with: + luaVersion: "5.1.5" + - uses: leafo/gh-actions-luarocks@v4 + - name: Run nvim-test tests + run: just test ${{ matrix.nvim-version }} diff --git a/.gitignore b/.gitignore index 3efd6307..c3121bab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ tmp node_modules +nvim-test +deps +busted/fixtures/basic/bin +busted/fixtures/basic/data + diff --git a/.luarc.json b/.luarc.json index e1b9d70d..c278f465 100644 --- a/.luarc.json +++ b/.luarc.json @@ -1,4 +1,6 @@ { - "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", - "Lua.workspace.checkThirdParty": false -} \ No newline at end of file + "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", + "runtime": { + "version": "LuaJIT" + } +} diff --git a/README.md b/README.md index fae68327..b8e8c9a6 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,7 @@ elixir.setup { | Command | Subcommand | Description | |---------|------------|------------------------------------------------------------------------------------------------------| | nextls | uninstall | Removes the `nextls` executable from the default location: `~/.cache/elixir-tools/nextls/bin/nextls` | -| nextls | to-pipe | Extracts the first argument to a pipe call | +| nextls | to-pipe | Extracts the first argument to a pipe call | | nextls | from-pipe | Inlines the pipe call to a function call the first argument to a pipe | ## Next LS @@ -364,3 +364,39 @@ You can run any `mix` command in your project, complete with... autocomplete! :Efeature {args} : Create or edit a Wallaby test module. + +## Contributing + +### Setup + +elixir-tools.nvim uses a combination of [Nix]() and [just]() to provide the development tooling, but you can also install all of this manually. + +#### Nix + just + +```bash +# enter a nix shell, provides language deps and just +$ nix develop + +# install test runner and plugin dependencies +$ just init + +# run tests, optionally include the Neovim version to test +$ just test +$ just test 0.8.3 + +# format the code +$ just format +``` + +#### Manually + +Install the following software: + +- [Neovim](https://neovim.io) +- [Lua 5.1](https://sourceforge.net/projects/luabinaries/files/5.1.5/) +- [Luarocks](https://github.com/luarocks/luarocks/wiki/Download) +- [nvim-test](https://github.com/lewis6991/nvim-test) +- [plenary.nvim](https://github.com/nvim-lua/plenary.nvim) (into the folder "deps") +- [stylua](https://github.com/JohnnyMorganz/StyLua) + +To run the tests, you can reference the commands run in the justfile diff --git a/busted/fixtures/basic/lib/basic.ex b/busted/fixtures/basic/lib/basic.ex new file mode 100644 index 00000000..1dd23fe8 --- /dev/null +++ b/busted/fixtures/basic/lib/basic.ex @@ -0,0 +1,5 @@ +defmodule Basic do + def run do + Enum.map([:one, :two], &Function.identity/1) + end +end diff --git a/busted/fixtures/basic/mix.exs b/busted/fixtures/basic/mix.exs new file mode 100644 index 00000000..bede1724 --- /dev/null +++ b/busted/fixtures/basic/mix.exs @@ -0,0 +1,32 @@ +defmodule Basic.MixProject do + use Mix.Project + + @version "0.1.0" + + def project do + [ + app: :basic, + description: "Basic app", + version: @version, + elixir: "~> 1.13", + elixirc_paths: elixirc_paths(Mix.env()), + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger, :crypto] + ] + end + + defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(_), do: ["lib"] + + # Run "mix help deps" to learn about dependencies. + defp deps do + [] + end +end diff --git a/busted/nextls/install_spec.lua b/busted/nextls/install_spec.lua new file mode 100644 index 00000000..59223264 --- /dev/null +++ b/busted/nextls/install_spec.lua @@ -0,0 +1,88 @@ +local helpers = require("nvim-test.helpers") +local Screen = require("nvim-test.screen") +local exec_lua = helpers.exec_lua +local luv = vim.loop +local eq = assert.equal + +describe("install", function() + before_each(function() + helpers.clear() + helpers.fn.delete("./busted/fixtures/basic/bin", "rf") + helpers.fn.delete("./busted/fixtures/basic/data", "rf") + helpers.fn.mkdir("./busted/fixtures/basic/data", "p") + helpers.fn.mkdir("./busted/fixtures/basic/bin", "p") + -- Make plugin available + exec_lua([[vim.opt.rtp:append'.']]) + exec_lua([[vim.opt.rtp:append'./deps/plenary.nvim/']]) + end) + + it("installs nextls when you open an elixir file and nextls isn't downloaded", function() + helpers.fn.writefile({ "" }, "./busted/fixtures/basic/data/.next-ls-force-update-v1") + exec_lua([[ + vim.g.next_ls_cache_dir = "./busted/fixtures/basic/bin" + vim.g.next_ls_data_dir = "./busted/fixtures/basic/data" + vim.g.next_ls_default_bin = "./busted/fixtures/basic/bin/nextls" + require("elixir.nextls").setup({auto_update = true, cmd = "./busted/fixtures/basic/bin/nextls" }) + vim.cmd.edit("./busted/fixtures/basic/lib/basic.ex") + ]]) + + eq(luv.fs_stat("./busted/fixtures/basic/bin/nextls").mode, 33523) + end) + + it("forces an install if the flag is not set", function() + helpers.fn.mkdir("./busted/fixtures/basic/bin", "p") + helpers.fn.writefile({ "foobar" }, "./busted/fixtures/basic/bin/nextls") + exec_lua([[ + vim.g.next_ls_cache_dir = "./busted/fixtures/basic/bin" + vim.g.next_ls_data_dir = "./busted/fixtures/basic/data" + vim.g.next_ls_default_bin = "./busted/fixtures/basic/bin/nextls" + require("elixir.nextls").setup({auto_update = true, cmd = "./busted/fixtures/basic/bin/nextls" }) + vim.cmd.edit("./busted/fixtures/basic/lib/basic.ex") + ]]) + + assert.error(function() + helpers.fn.readfile("./busted/fixtures/basic/bin/nextls", "b") + end) + eq(luv.fs_stat("./busted/fixtures/basic/bin/nextls").mode, 33523) + end) + + it("doesnt force an install if the flag is set", function() + helpers.fn.writefile({ "" }, "./busted/fixtures/basic/data/.next-ls-force-update-v1") + helpers.fn.mkdir("./busted/fixtures/basic/bin", "p") + helpers.fn.writefile({ "foobar" }, "./busted/fixtures/basic/bin/nextls") + local screen = Screen.new() + screen:attach() + exec_lua([[ + vim.g.next_ls_cache_dir = "./busted/fixtures/basic/bin" + vim.g.next_ls_data_dir = "./busted/fixtures/basic/data" + vim.g.next_ls_default_bin = "./busted/fixtures/basic/bin/nextls" + require("elixir.nextls").setup({auto_update = true, cmd = "./busted/fixtures/basic/bin/nextls" }) + vim.cmd.edit("./busted/fixtures/basic/lib/basic.ex") + ]]) + + helpers.feed("") + -- screen:snapshot_util() + eq(helpers.fn.readfile("./busted/fixtures/basic/bin/nextls", "b")[1], "foobar") + screen:expect { + grid = [[ + ^defmodule Basic do | + def run do | + Enum.map([:one, :two], &Function.identity/1) | + end | + end | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], + attr_ids = { + [1] = { bold = true, foreground = Screen.colors.Blue1 }, + }, + } + end) +end) diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..f0eb902a --- /dev/null +++ b/flake.lock @@ -0,0 +1,64 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1709336216, + "narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1709479366, + "narHash": "sha256-n6F0n8UV6lnTZbYPl1A9q1BS0p4hduAv1mGAP17CVd0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b8697e57f10292a6165a20f03d2f42920dfaf973", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1709237383, + "narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..25c06bb1 --- /dev/null +++ b/flake.nix @@ -0,0 +1,43 @@ +{ + description = "Description for the project"; + + inputs = { + flake-parts.url = "github:hercules-ci/flake-parts"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = inputs@{ flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ + # To import a flake module + # 1. Add foo to inputs + # 2. Add foo as a parameter to the outputs function + # 3. Add here: foo.flakeModule + + ]; + systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ]; + perSystem = { config, self', inputs', pkgs, system, ... }: { + # Per-system attributes can be defined here. The self' and inputs' + # module parameters provide easy access to attributes of the same + # system. + + # Equivalent to inputs'.nixpkgs.legacyPackages.hello; + packages.default = pkgs.hello; + devShells.default = pkgs.mkShell { + # The Nix packages provided in the environment + packages = [ + pkgs.luarocks + pkgs.lua5_1 + pkgs.just + pkgs.stylua + ]; + }; + }; + flake = { + # The usual flake attributes can be defined here, including system- + # agnostic ones like nixosModule and system-enumerating ones, although + # those are more easily expressed in perSystem. + + }; + }; +} diff --git a/justfile b/justfile new file mode 100644 index 00000000..7fb9c19d --- /dev/null +++ b/justfile @@ -0,0 +1,22 @@ +deps: + #!/usr/bin/env bash + set -euo pipefail + mkdir -p deps + if [ ! -d "deps/plenary.nvim" ]; then + git -C deps clone https://github.com/nvim-lua/plenary.nvim + fi + if [ ! -d "nvim-test" ]; then + git clone https://github.com/lewis6991/nvim-test + fi + +init: deps + #!/usr/bin/env bash + nvim-test/bin/nvim-test --init + +test nvim_version="0.9.5": init + #!/usr/bin/env bash + nvim-test/bin/nvim-test --target_version {{nvim_version}} busted --lpath="$PWD/lua/?.lua" + +format: + #!/usr/bin/env bash + stylua . diff --git a/lua/elixir/elixirls/compile.lua b/lua/elixir/elixirls/compile.lua index 328867b8..53ff5c91 100644 --- a/lua/elixir/elixirls/compile.lua +++ b/lua/elixir/elixirls/compile.lua @@ -36,7 +36,7 @@ function M.compile(source_path, install_path, opts) args = { install_path }, cwd = source_path, on_start = function() - vim.notify("Compiling ElixirLS...") + vim.notify("[elixir-tools] Compiling ElixirLS...") end, on_stdout = do_sync and printer or vim.schedule_wrap(printer), on_stderr = do_sync and printer or vim.schedule_wrap(printer), diff --git a/lua/elixir/elixirls/download.lua b/lua/elixir/elixirls/download.lua index fd76412a..71a9e7d9 100644 --- a/lua/elixir/elixirls/download.lua +++ b/lua/elixir/elixirls/download.lua @@ -11,7 +11,7 @@ function M.clone(dir, opts) local rr = Utils.safe_path(opts.ref or "HEAD") local dir_identifier = Path:new(r, rr).filename - vim.notify(string.format("Cloning ref %s from repo %s", opts.ref or "default", opts.repo)) + vim.notify(string.format("[elixir-tools] Cloning ref %s from repo %s", opts.ref or "default", opts.repo)) local made_path = Path:new(dir):mkdir { parents = true, mode = 493 } assert(made_path, "failed to make the path") @@ -36,7 +36,7 @@ function M.clone(dir, opts) assert(checkout.code == 0, "Failed to checkout ref " .. opts.ref) end - vim.notify("Downloaded ElixirLS!") + vim.notify("[elixir-tools] Downloaded ElixirLS!") return dir_identifier end diff --git a/lua/elixir/elixirls/init.lua b/lua/elixir/elixirls/init.lua index 6fa0f81c..fe7812f5 100644 --- a/lua/elixir/elixirls/init.lua +++ b/lua/elixir/elixirls/init.lua @@ -141,9 +141,9 @@ local function test(command) if exit_code == 0 then vim.api.nvim_buf_delete(term_buf_id, { force = true }) term_buf_id = nil_buf_id - vim.notify("Success: " .. cmd, vim.log.levels.INFO) + vim.notify("[elixir-tools] Success: " .. cmd, vim.log.levels.INFO) else - vim.notify("Fail: " .. cmd, vim.log.levels.ERROR) + vim.notify("[elixir-tools] Fail: " .. cmd, vim.log.levels.ERROR) end end, }) @@ -228,15 +228,15 @@ local function install_elixir_ls(opts) end) end - vim.notify("Finished compiling ElixirLS!") - vim.notify("Reloading buffer") + vim.notify("[elixir-tools] Finished compiling ElixirLS!") + vim.notify("[elixir-tools] Reloading buffer") vim.api.nvim_command("edit") - vim.notify("Restarting LSP client") + vim.notify("[elixir-tools] Restarting LSP client") vim.api.nvim_command("LspRestart") vim.fn.jobstart({ "rm", "-rf", download_dir:absolute() }, { on_exit = vim.schedule_wrap(function(_, rm_code) if rm_code == 0 then - vim.notify("Cleaned up elixir-tools.nvim download directory") + vim.notify("[elixir-tools] Cleaned up elixir-tools.nvim download directory") else vim.api.nvim_err_writeln("Failed to clean up elixir-tools.nvim download directory") end diff --git a/lua/elixir/init.lua b/lua/elixir/init.lua index 1f12ceed..fcdf2e9c 100644 --- a/lua/elixir/init.lua +++ b/lua/elixir/init.lua @@ -44,7 +44,10 @@ local define_user_command = function() local subcommand = args:next() if "uninstall" == subcommand then vim.fn.delete(nextls.default_bin) - vim.notify(string.format("Uninstalled Next LS from %s", nextls.default_bin), vim.lsp.log_levels.INFO) + vim.notify( + string.format("[elixir-tools] Uninstalled Next LS from %s", nextls.default_bin), + vim.lsp.log_levels.INFO + ) elseif "to-pipe" == subcommand then local row, col = get_cursor_position() local uri = vim.uri_from_bufnr(0) @@ -66,7 +69,7 @@ local define_user_command = function() not_found = true end if not_found then - vim.notify("elixir-tools: unknown command: " .. opts.name .. " " .. opts.args, vim.lsp.log_levels.WARN) + vim.notify("[elixir-tools] Unknown command: " .. opts.name .. " " .. opts.args, vim.lsp.log_levels.WARN) end end, { desc = "elixir-tools main command", diff --git a/lua/elixir/nextls/init.lua b/lua/elixir/nextls/init.lua index d270fd28..535704fb 100644 --- a/lua/elixir/nextls/init.lua +++ b/lua/elixir/nextls/init.lua @@ -5,7 +5,8 @@ if not vim.uv then vim.uv = vim.loop end -M.default_bin = vim.env.HOME .. "/.cache/elixir-tools/nextls/bin/nextls" +M.default_bin = vim.g.next_ls_default_bin or (vim.env.HOME .. "/.cache/elixir-tools/nextls/bin/nextls") +M.default_data = vim.g.next_ls_data_dir or (vim.env.HOME .. "/.data/elixir-tools/nextls") function M.setup(opts) local nextls_group = vim.api.nvim_create_augroup("elixir-tools.nextls", { clear = true }) @@ -92,7 +93,6 @@ function M.setup(opts) else cmd = { opts.cmd, "--stdio" } end - local activate = function() vim.api.nvim_exec_autocmds("User", { pattern = "ElixirToolsNextLSActivate", @@ -105,25 +105,22 @@ function M.setup(opts) }) end + vim.fn.mkdir(M.default_data, "p") + local force_download_file = M.default_data .. "/.next-ls-force-update-v1" + local force_download = not vim.uv.fs_stat(force_download_file) + if - not vim.b.elixir_tools_prompted_nextls_install - and type(opts.port) ~= "number" - and (opts.auto_update and not vim.uv.fs_stat(opts.cmd)) + (force_download and opts.auto_update) + or (type(opts.port) ~= "number" and (opts.auto_update and not vim.uv.fs_stat(opts.cmd))) then - vim.ui.select({ "Yes", "No" }, { prompt = "Install Next LS?" }, function(choice) - if choice == "Yes" then - utils.download_nextls() - activate() - else - vim.b["elixir_tools_prompted_nextls_install"] = true - end - end) - else - vim.schedule_wrap(activate)() + utils.download_nextls() + vim.fn.writefile({ "" }, force_download_file) end + vim.schedule_wrap(activate)() end end, }) + return opts end return M diff --git a/lua/elixir/utils.lua b/lua/elixir/utils.lua index c2834977..5338a547 100644 --- a/lua/elixir/utils.lua +++ b/lua/elixir/utils.lua @@ -36,8 +36,10 @@ local arch = { } function M.download_nextls(opts) + vim.notify("[elixir-tools] Downloading latest version of Next LS") + local default_cache_dir = vim.g.next_ls_cache_dir or vim.env.HOME .. "/.cache/elixir-tools/nextls/bin" opts = opts or {} - local cache_dir = opts.cache_dir or vim.env.HOME .. "/.cache/elixir-tools/nextls/bin" + local cache_dir = opts.cache_dir or default_cache_dir local os_name = string.lower(vim.uv.os_uname().sysname) local current_arch = arch[string.lower(vim.uv.os_uname().machine)] local curl = { @@ -60,7 +62,7 @@ function M.download_nextls(opts) if not vim.v.shell_error == 0 then vim.notify( - "Failed to fetch the latest release of Next LS from GitHub.\n\n" + "[elixir-tools] Failed to fetch the latest release of Next LS from GitHub.\n\n" .. "Using the command `" .. table.concat(curl, " ") .. "`" @@ -105,7 +107,7 @@ function M.latest_release(owner, repo, opts) return vim.fn.readfile(latest_version_file)[1] else vim.notify( - "Failed to fetch the current " + "[elixir-tools] Failed to fetch the current " .. repo .. " version from GitHub or the cache.\n" .. "You most likely do not have an internet connection / exceeded\n"