diff --git a/doc/gitlab.nvim.txt b/doc/gitlab.nvim.txt index 2db9f633..4f61c03b 100644 --- a/doc/gitlab.nvim.txt +++ b/doc/gitlab.nvim.txt @@ -144,7 +144,10 @@ Here is the default setup function. All of these values are optional, and if you call this function with no values the defaults will be used: >lua require("gitlab").setup({ - port = nil, -- The port of the Go server, which runs in the background, if omitted or `nil` the port will be chosen automatically + server = { + binary = nil, -- The path to the server binary. If omitted or nil, the server will be built + port = nil, -- The port of the Go server, which runs in the background. If omitted or `nil` the port will be chosen automatically + }, log_path = vim.fn.stdpath("cache") .. "/gitlab.nvim.log", -- Log path for the Go server config_path = nil, -- Custom path for `.gitlab.nvim` file, please read the "Connecting to Gitlab" section debug = { @@ -678,7 +681,7 @@ by a |motion|. Either the operator or the motion can be preceded by a count, so that `3sj` is equivalent to `s3j`, and they both create a comment for the current line and three more lines downwards. Similarly, both `2s`|ap| and `s2`|ap| create a suggestion -for two "outer" paragraphs. +for two "outer" paragraphs. The operators force |linewise| visual selection, so they work correctly even if the motion itself works |characterwise| (e.g., |i(| for selecting the inner diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..8f8b8f30 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1770770419, + "narHash": "sha256-iKZMkr6Cm9JzWlRYW/VPoL0A9jVKtZYiU4zSrVeetIs=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6c5e707c6b5339359a9a9e215c5e66d6d802fd7a", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..a414bbfd --- /dev/null +++ b/flake.nix @@ -0,0 +1,47 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, flake-utils, nixpkgs }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; config.allowUnfree = true; }; + gitlab-nvim-server = pkgs.buildGoModule { + pname = "gitlab.nvim-server"; + version = "git"; + src = ./.; + vendorHash = "sha256-OLAKTdzqynBDHqWV5RzIpfc3xZDm6uYyLD4rxbh0DMg="; + postInstall = '' + cp -r ${./cmd/config} $out/bin/config + mv $out/bin/cmd $out/bin/gitlab.nvim + ''; + }; + gitlab-nvim = pkgs.vimUtils.buildVimPlugin { + name = "gitlab.nvim"; + src = ./.; + doCheck = false; + }; + in + rec { + formatter = pkgs.nixpkgs-fmt; + packages.gitlab-nvim-server = gitlab-nvim-server; + packages.gitlab-nvim = gitlab-nvim; + packages.default = packages.gitlab-nvim; + devShell = pkgs.mkShell { + packages = with pkgs; [ + git + go + go-tools + golangci-lint + luajitPackages.busted + luajitPackages.luacheck + luajitPackages.luarocks + neovim + stylua + ]; + }; + } + ); +} diff --git a/lua-test.sh b/lua-test.sh index 6cf83dc9..300530c2 100755 --- a/lua-test.sh +++ b/lua-test.sh @@ -41,4 +41,4 @@ done # Run tests echo "Running tests with Neovim..." -nvim -u NONE -U NONE -N -i NONE -l tests/init.lua "$@" +LC_TIME=en_US.UTF-8 nvim -u NONE -U NONE -N -i NONE -l tests/init.lua "$@" diff --git a/lua/gitlab/init.lua b/lua/gitlab/init.lua index 8c52c73b..b492c563 100644 --- a/lua/gitlab/init.lua +++ b/lua/gitlab/init.lua @@ -44,8 +44,8 @@ local function setup(args) return end - server.build() -- Builds the Go binary if it doesn't exist state.merge_settings(args) -- Merges user settings with default settings + server.build() -- Builds the Go binary if it doesn't exist state.set_global_keymaps() -- Sets keymaps that are not bound to a specific buffer require("gitlab.colors") -- Sets colors reviewer.init() diff --git a/lua/gitlab/job.lua b/lua/gitlab/job.lua index 128591be..380246ec 100644 --- a/lua/gitlab/job.lua +++ b/lua/gitlab/job.lua @@ -6,7 +6,7 @@ local M = {} M.run_job = function(endpoint, method, body, callback) local state = require("gitlab.state") - local args = { "-s", "-X", (method or "POST"), string.format("localhost:%s", state.settings.port) .. endpoint } + local args = { "-s", "-X", (method or "POST"), string.format("localhost:%s", state.settings.server.port) .. endpoint } if body ~= nil then local encoded_body = vim.json.encode(body) diff --git a/lua/gitlab/server.lua b/lua/gitlab/server.lua index d9ff6305..55f04de8 100644 --- a/lua/gitlab/server.lua +++ b/lua/gitlab/server.lua @@ -30,7 +30,7 @@ end -- Starts the Go server and call the callback provided M.start = function(callback) - local port = tonumber(state.settings.port) or 0 + local port = tonumber(state.settings.server.port) or 0 local parsed_port = nil local callback_called = false @@ -51,7 +51,7 @@ M.start = function(callback) settings = settings:gsub('"', '\\"') end - local command = string.format('"%s" "%s"', state.settings.bin, settings) + local command = string.format('"%s" "%s"', state.settings.server.binary, settings) local job_id = vim.fn.jobstart(command, { on_stdout = function(_, data) @@ -61,7 +61,7 @@ M.start = function(callback) port = line:match("Server started on port:%s+(%d+)") if port ~= nil then parsed_port = port - state.settings.port = port + state.settings.server.port = port break end end @@ -105,26 +105,59 @@ end -- Builds the Go binary with the current Git tag. M.build = function(override) local file_path = u.current_file_path() - local parent_dir = vim.fn.fnamemodify(file_path, ":h:h:h:h") + state.settings.root_path = vim.fn.fnamemodify(file_path, ":h:h:h:h") + + -- If the user provided a path to the server, don't build it. + if state.settings.server.binary ~= nil then + local binary_exists = vim.loop.fs_stat(state.settings.server.binary) + if binary_exists == nil then + u.notify( + string.format("The user-provided server path (%s) does not exist.", state.settings.server.binary), + vim.log.levels.ERROR + ) + end + return + end + + -- If the user did not provide a path, we build it and place it in either the data path, or the + -- first writable path we find in the runtime. + local datapath = vim.fn.stdpath("data") + local runtimepath = vim.api.nvim_list_runtime_paths() + table.insert(runtimepath, 1, datapath) + + local bin_folder + for _, path in ipairs(runtimepath) do + local ok, err = vim.loop.fs_access(path, "w") + if err == nil and ok ~= nil and ok then + bin_folder = path .. u.path_separator .. "gitlab.nvim" .. u.path_separator .. "bin" + if vim.fn.mkdir(bin_folder, "p") == 1 then + state.settings.server.binary = bin_folder .. u.path_separator .. "server" + break + end + end + end - local bin_name = u.is_windows() and "bin.exe" or "bin" - state.settings.root_path = parent_dir - state.settings.bin = parent_dir .. u.path_separator .. "cmd" .. u.path_separator .. bin_name + if state.settings.server.binary == nil then + u.notify("Could not find a writable folder in the runtime path to save the server to.", vim.log.levels.ERROR) + return + end if not override then - local binary_exists = vim.loop.fs_stat(state.settings.bin) + local binary_exists = vim.loop.fs_stat(state.settings.server.binary) if binary_exists ~= nil then return end end - local version_output = vim.system({ "git", "describe", "--tags", "--always" }, { cwd = parent_dir }):wait() + local version_output = vim + .system({ "git", "describe", "--tags", "--always" }, { cwd = state.settings.root_path }) + :wait() local version = version_output.code == 0 and vim.trim(version_output.stdout) or "unknown" local ldflags = string.format("-X main.Version=%s", version) local res = vim .system( - { "go", "build", "-ldflags", ldflags, "-o", bin_name }, + { "go", "build", "-buildvcs=false", "-ldflags", ldflags, "-o", state.settings.server.binary }, { cwd = state.settings.root_path .. u.path_separator .. "cmd" } ) :wait() @@ -133,6 +166,12 @@ M.build = function(override) u.notify(string.format("Failed to install with status code %d:\n%s", res.code, res.stderr), vim.log.levels.ERROR) return false end + + local Path = require("plenary.path") + local src = Path:new(state.settings.root_path .. u.path_separator .. "cmd" .. u.path_separator .. "config") + local dest = Path:new(bin_folder .. u.path_separator .. "config") + src:copy({ destination = dest, recursive = true, override = true }) + u.notify("Installed successfully!", vim.log.levels.INFO) return true end @@ -185,7 +224,7 @@ M.get_version = function(callback) local version_output = vim.system({ "git", "describe", "--tags", "--always" }, { cwd = parent_dir }):wait() local plugin_version = version_output.code == 0 and vim.trim(version_output.stdout) or "unknown" - local args = { "-s", "-X", "GET", string.format("localhost:%s/version", state.settings.port) } + local args = { "-s", "-X", "GET", string.format("localhost:%s/version", state.settings.server.port) } -- We call the "/version" endpoint here instead of through the regular jobs pattern because earlier versions of the plugin -- may not have it. We handle a 404 as an "unknown" version error. diff --git a/lua/gitlab/state.lua b/lua/gitlab/state.lua index d67e0c06..940b950a 100644 --- a/lua/gitlab/state.lua +++ b/lua/gitlab/state.lua @@ -45,7 +45,10 @@ end M.settings = { auth_provider = M.default_auth_provider, file_separator = u.path_separator, - port = nil, -- choose random port + server = { + binary = nil, + port = nil, + }, debug = { request = false, response = false,