diff --git a/README.md b/README.md
index 7258144..2e47043 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,6 @@ And a lot more!
https://github.com/harrisoncramer/gitlab.nvim/assets/32515581/50f44eaf-5f99-4cb3-93e9-ed66ace0f675
-
## Requirements
- Go >= v1.19
@@ -22,10 +21,10 @@ https://github.com/harrisoncramer/gitlab.nvim/assets/32515581/50f44eaf-5f99-4cb3
1. Install Go
2. Install reviewer: delta or diffview
-2. Add configuration (see Installation section)
-3. Checkout your feature branch: `git checkout feature-branch`
-4. Open Neovim
-5. Run `:lua require("gitlab").review()` to open the reviewer pane
+3. Add configuration (see Installation section)
+4. Checkout your feature branch: `git checkout feature-branch`
+5. Open Neovim
+6. Run `:lua require("gitlab").review()` to open the reviewer pane
## Installation
@@ -85,7 +84,7 @@ Here is the default setup function. All of these values are optional, and if you
```lua
require("gitlab").setup({
- port = 21036, -- The port of the Go server, which runs in the background
+ 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
reviewer = "delta", -- The reviewer type ("delta" or "diffview")
attachment_dir = nil, -- The local directory for files (see the "summary" section)
@@ -138,13 +137,13 @@ require("gitlab").setup({
## Usage
-First, check out the branch that you want to review locally.
+First, check out the branch that you want to review locally.
```
git checkout feature-branch
```
-Then open Neovim. The `project_id` you specify in your configuration file must match the project_id of the Gitlab project your terminal is inside of.
+Then open Neovim. The `project_id` you specify in your configuration file must match the project_id of the Gitlab project your terminal is inside of.
### Summary
@@ -169,9 +168,9 @@ The reviewer is Delta by default, but you can configure the plugin to use Diffvi
### Discussions and Notes
-Gitlab groups threads of comments together into "discussions."
+Gitlab groups threads of comments together into "discussions."
-To display all discussions for the current MR, use the `toggle_discussions` action, which will show the discussions in a split window.
+To display all discussions for the current MR, use the `toggle_discussions` action, which will show the discussions in a split window.
```lua
require("gitlab").toggle_discussions()
@@ -189,7 +188,7 @@ require("gitlab").create_note()
### Uploading Files
-To attach a file to an MR description, reply, comment, and so forth use the `settings.popup.perform_linewise_action` keybinding when the the popup is open. This will open a picker that will look in the directory you specify in the `settings.attachment_dir` folder (this must be an absolute path) for files.
+To attach a file to an MR description, reply, comment, and so forth use the `settings.popup.perform_linewise_action` keybinding when the the popup is open. This will open a picker that will look in the directory you specify in the `settings.attachment_dir` folder (this must be an absolute path) for files.
When you have picked the file, it will be added to the current buffer at the current line.
@@ -277,7 +276,7 @@ This is the API call that is happening from within Neovim when you run the `summ
If you are able to build and start the Go server and hit the endpoint successfully for the action you are trying to run (such as creating a comment or approving a merge request) then something is wrong with the Lua code. In that case, please file a bug report.
-This Go server, in turn, writes logs to the log path that is configured in your setup function. These are written by default to `~/.cache/nvim/gitlab.nvim.log` and will be written each time the server reaeches out to Gitlab.
+This Go server, in turn, writes logs to the log path that is configured in your setup function. These are written by default to `~/.cache/nvim/gitlab.nvim.log` and will be written each time the server reaeches out to Gitlab.
If the Golang server is not starting up correctly, please check your `.gitlab.nvim` file and your setup function. You can, however, try running the Golang server independently of Neovim. For instance, to start it up for a certain project, navigate to your plugin directory, and build the binary (these are instructions for Lazy) and move that binary to your project. You can then try running the binary directly, or even with a debugger like Delve:
diff --git a/cmd/main.go b/cmd/main.go
index 041bce3..76a010f 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -4,10 +4,12 @@ import (
"context"
"fmt"
"log"
+ "net"
"net/http"
"os"
"os/exec"
"strings"
+ "time"
)
func main() {
@@ -29,6 +31,7 @@ func main() {
}
m := http.NewServeMux()
+ m.Handle("/ping", http.HandlerFunc(PingHandler))
m.Handle("/mr/summary", withGitlabContext(http.HandlerFunc(SummaryHandler), c))
m.Handle("/mr/attachment", withGitlabContext(http.HandlerFunc(AttachmentHandler), c))
m.Handle("/mr/reviewer", withGitlabContext(http.HandlerFunc(ReviewersHandler), c))
@@ -44,19 +47,45 @@ func main() {
m.Handle("/pipeline", withGitlabContext(http.HandlerFunc(PipelineHandler), c))
m.Handle("/job", withGitlabContext(http.HandlerFunc(JobHandler), c))
- port := fmt.Sprintf(":%s", os.Args[3])
- server := &http.Server{
- Addr: port,
- Handler: m,
+ port := os.Args[3]
+ if port == "" {
+ // port was not specified
+ port = "0"
+ }
+ addr := fmt.Sprintf("localhost:%s", port)
+ listener, err := net.Listen("tcp", addr)
+ if err != nil {
+ log.Fatal(err)
+ fmt.Fprintf(os.Stderr, "Error starting server: %s\n", err)
+ os.Exit(1)
+ }
+ listner_port := listener.Addr().(*net.TCPAddr).Port
+
+ errCh := make(chan error)
+ go func() {
+ err := http.Serve(listener, m)
+ errCh <- err
+ }()
+
+ go func() {
+ for i := 0; i < 10; i++ {
+ resp, err := http.Get("http://localhost:" + fmt.Sprintf("%d", listner_port) + "/ping")
+ if resp.StatusCode == 200 && err == nil {
+ /* This print is detected by the Lua code and used to fetch project information */
+ fmt.Println("Server started on port: ", listner_port)
+ return
+ }
+ // Wait for healthcheck to pass - at most 1 sec.
+ time.Sleep(100 * time.Microsecond)
+ }
+ errCh <- err
+ }()
+
+ if err := <-errCh; err != nil {
+ fmt.Fprintf(os.Stderr, "Error starting server: %s\n", err)
+ os.Exit(1)
}
- done := make(chan bool)
- go server.ListenAndServe()
-
- /* This print is detected by the Lua code and used to fetch project information */
- fmt.Println("Server started.")
-
- <-done
}
func withGitlabContext(next http.HandlerFunc, c Client) http.Handler {
@@ -77,3 +106,7 @@ func getCurrentBranch() (res string, e error) {
return strings.TrimSpace(string(output)), nil
}
+func PingHandler(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusOK)
+ fmt.Fprintln(w, "pong")
+}
diff --git a/lua/gitlab/server.lua b/lua/gitlab/server.lua
index 9e3e38b..836d90d 100644
--- a/lua/gitlab/server.lua
+++ b/lua/gitlab/server.lua
@@ -2,45 +2,75 @@
-- the Golang server. The Go server is responsible for making API calls
-- to Gitlab and returning the data
local state = require("gitlab.state")
-local u = require("gitlab.utils")
-local M = {}
+local u = require("gitlab.utils")
+local M = {}
-- Starts the Go server and call the callback provided
-M.start = function(callback)
+M.start = function(callback)
+ local empty_port = "''"
+ local port = state.settings.port or empty_port
+ local parsed_port = nil
+ local callback_called = false
local command = state.settings.bin
- .. " "
- .. state.settings.project_id
- .. " "
- .. state.settings.gitlab_url
- .. " "
- .. state.settings.port
- .. " "
- .. state.settings.auth_token
- .. " "
- .. state.settings.log_path
+ .. " "
+ .. state.settings.project_id
+ .. " "
+ .. state.settings.gitlab_url
+ .. " "
+ .. port
+ .. " "
+ .. state.settings.auth_token
+ .. " "
+ .. state.settings.log_path
- vim.fn.jobstart(command, {
- on_stdout = function(job_id)
- if job_id <= 0 then
- vim.notify("Could not start gitlab.nvim binary", vim.log.levels.ERROR)
- else
+ local job_id = vim.fn.jobstart(command, {
+ on_stdout = function(_, data)
+ -- if port was not provided then we need to parse it from output of server
+ if parsed_port == nil then
+ for _, line in ipairs(data) do
+ port = line:match("Server started on port:%s+(%d+)")
+ if port ~= nil then
+ parsed_port = port
+ state.settings.port = port
+ break
+ end
+ end
+ end
+
+ -- This assumes that first output of server will be parsable and port will be correctly set.
+ -- Make sure that this actually check if port was correctly parsed based on server output
+ -- because server outputs port only if it started successfully.
+ if parsed_port ~= nil and not callback_called then
callback()
+ callback_called = true
+ elseif not callback_called then
+ vim.notify("Failed to parse server port", vim.log.levels.ERROR)
end
end,
on_stderr = function(_, errors)
- local err_msg = ''
+ local err_msg = ""
for _, err in ipairs(errors) do
if err ~= "" and err ~= nil then
err_msg = err_msg .. err .. "\n"
end
end
- if err_msg ~= '' then vim.notify(err_msg, vim.log.levels.ERROR) end
- end
+ if err_msg ~= "" then
+ vim.notify(err_msg, vim.log.levels.ERROR)
+ end
+ end,
+ on_exit = function(job_id, exit_code, ...)
+ vim.notify(
+ "Golang gitlab server exited: job_id: " .. job_id .. ", exit_code: " .. exit_code,
+ vim.log.levels.ERROR
+ )
+ end,
})
+ if job_id <= 0 then
+ vim.notify("Could not start gitlab.nvim binary", vim.log.levels.ERROR)
+ end
end
-
-- Builds the Go binary
M.build = function(override)
local file_path = u.current_file_path()
@@ -50,12 +80,13 @@ M.build = function(override)
if not override then
local binary_exists = vim.loop.fs_stat(state.settings.bin)
- if binary_exists ~= nil then return end
+ if binary_exists ~= nil then
+ return
+ end
end
- local cmd = u.is_windows() and
- 'cd %s\\cmd && go build -o bin.exe && move bin.exe ..\\' or
- 'cd %s/cmd && go build -o bin && mv bin ../bin'
+ local cmd = u.is_windows() and "cd %s\\cmd && go build -o bin.exe && move bin.exe ..\\"
+ or "cd %s/cmd && go build -o bin && mv bin ../bin"
local command = string.format(cmd, state.settings.bin_path)
local null = u.is_windows() and " >NUL" or " > /dev/null"
diff --git a/lua/gitlab/state.lua b/lua/gitlab/state.lua
index 57432f8..2811ebc 100644
--- a/lua/gitlab/state.lua
+++ b/lua/gitlab/state.lua
@@ -7,11 +7,11 @@ local u = require("gitlab.utils")
local M = {}
-- These are the default settings for the plugin
-M.settings = {
- port = 21036,
+M.settings = {
+ port = nil, -- choose random port
log_path = (vim.fn.stdpath("cache") .. "/gitlab.nvim.log"),
reviewer = "delta",
- attachment_dir = '',
+ attachment_dir = "",
popup = {
exit = "",
perform_action = "s",
@@ -29,15 +29,15 @@ M.settings = {
relative = "editor",
position = "left",
size = "20%",
- resolved = '✓',
- unresolved = '',
+ resolved = "✓",
+ unresolved = "",
},
review_pane = {
delta = {
added_file = "",
modified_file = "",
removed_file = "",
- }
+ },
},
pipeline = {
created = "",
diff --git a/lua/gitlab/utils/init.lua b/lua/gitlab/utils/init.lua
index 822a3f5..5f36d87 100644
--- a/lua/gitlab/utils/init.lua
+++ b/lua/gitlab/utils/init.lua
@@ -126,20 +126,10 @@ M.create_popup_state = function(title, width, height)
end
M.merge = function(defaults, overrides)
- local result = {}
if type(defaults) == "table" and M.table_size(defaults) == 0 and type(overrides) == "table" then
return overrides
end
-
- for key, value in pairs(defaults) do
- if type(value) == "table" then
- result[key] = M.merge(value, overrides[key] or {})
- else
- result[key] = overrides[key] or value
- end
- end
-
- return result
+ return vim.tbl_deep_extend("force", defaults, overrides)
end
M.join = function(tbl, separator)