Add option to choose random server port (#65)
Updates the plugin to automatically choose a random port when started when no port is provided. This will allow multiple Neovim instances to open at the same time in different projects.
This commit is contained in:
23
README.md
23
README.md
@@ -13,7 +13,6 @@ And a lot more!
|
|||||||
|
|
||||||
https://github.com/harrisoncramer/gitlab.nvim/assets/32515581/50f44eaf-5f99-4cb3-93e9-ed66ace0f675
|
https://github.com/harrisoncramer/gitlab.nvim/assets/32515581/50f44eaf-5f99-4cb3-93e9-ed66ace0f675
|
||||||
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- <a href="https://go.dev/">Go</a> >= v1.19
|
- <a href="https://go.dev/">Go</a> >= v1.19
|
||||||
@@ -22,10 +21,10 @@ https://github.com/harrisoncramer/gitlab.nvim/assets/32515581/50f44eaf-5f99-4cb3
|
|||||||
|
|
||||||
1. Install Go
|
1. Install Go
|
||||||
2. Install reviewer: <a href="https://github.com/dandavison/delta">delta</a> or <a href="https://github.com/sindrets/diffview.nvim">diffview</a>
|
2. Install reviewer: <a href="https://github.com/dandavison/delta">delta</a> or <a href="https://github.com/sindrets/diffview.nvim">diffview</a>
|
||||||
2. Add configuration (see Installation section)
|
3. Add configuration (see Installation section)
|
||||||
3. Checkout your feature branch: `git checkout feature-branch`
|
4. Checkout your feature branch: `git checkout feature-branch`
|
||||||
4. Open Neovim
|
5. Open Neovim
|
||||||
5. Run `:lua require("gitlab").review()` to open the reviewer pane
|
6. Run `:lua require("gitlab").review()` to open the reviewer pane
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -85,7 +84,7 @@ Here is the default setup function. All of these values are optional, and if you
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
require("gitlab").setup({
|
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
|
log_path = vim.fn.stdpath("cache") .. "/gitlab.nvim.log", -- Log path for the Go server
|
||||||
reviewer = "delta", -- The reviewer type ("delta" or "diffview")
|
reviewer = "delta", -- The reviewer type ("delta" or "diffview")
|
||||||
attachment_dir = nil, -- The local directory for files (see the "summary" section)
|
attachment_dir = nil, -- The local directory for files (see the "summary" section)
|
||||||
@@ -138,13 +137,13 @@ require("gitlab").setup({
|
|||||||
|
|
||||||
## Usage
|
## 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
|
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
|
### Summary
|
||||||
|
|
||||||
@@ -169,9 +168,9 @@ The reviewer is Delta by default, but you can configure the plugin to use Diffvi
|
|||||||
|
|
||||||
### Discussions and Notes
|
### 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
|
```lua
|
||||||
require("gitlab").toggle_discussions()
|
require("gitlab").toggle_discussions()
|
||||||
@@ -189,7 +188,7 @@ require("gitlab").create_note()
|
|||||||
|
|
||||||
### Uploading Files
|
### 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.
|
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.
|
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:
|
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:
|
||||||
|
|
||||||
|
|||||||
55
cmd/main.go
55
cmd/main.go
@@ -4,10 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -29,6 +31,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m := http.NewServeMux()
|
m := http.NewServeMux()
|
||||||
|
m.Handle("/ping", http.HandlerFunc(PingHandler))
|
||||||
m.Handle("/mr/summary", withGitlabContext(http.HandlerFunc(SummaryHandler), c))
|
m.Handle("/mr/summary", withGitlabContext(http.HandlerFunc(SummaryHandler), c))
|
||||||
m.Handle("/mr/attachment", withGitlabContext(http.HandlerFunc(AttachmentHandler), c))
|
m.Handle("/mr/attachment", withGitlabContext(http.HandlerFunc(AttachmentHandler), c))
|
||||||
m.Handle("/mr/reviewer", withGitlabContext(http.HandlerFunc(ReviewersHandler), 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("/pipeline", withGitlabContext(http.HandlerFunc(PipelineHandler), c))
|
||||||
m.Handle("/job", withGitlabContext(http.HandlerFunc(JobHandler), c))
|
m.Handle("/job", withGitlabContext(http.HandlerFunc(JobHandler), c))
|
||||||
|
|
||||||
port := fmt.Sprintf(":%s", os.Args[3])
|
port := os.Args[3]
|
||||||
server := &http.Server{
|
if port == "" {
|
||||||
Addr: port,
|
// port was not specified
|
||||||
Handler: m,
|
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 {
|
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
|
return strings.TrimSpace(string(output)), nil
|
||||||
}
|
}
|
||||||
|
func PingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintln(w, "pong")
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,45 +2,75 @@
|
|||||||
-- the Golang server. The Go server is responsible for making API calls
|
-- the Golang server. The Go server is responsible for making API calls
|
||||||
-- to Gitlab and returning the data
|
-- to Gitlab and returning the data
|
||||||
local state = require("gitlab.state")
|
local state = require("gitlab.state")
|
||||||
local u = require("gitlab.utils")
|
local u = require("gitlab.utils")
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
-- Starts the Go server and call the callback provided
|
-- 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
|
local command = state.settings.bin
|
||||||
.. " "
|
.. " "
|
||||||
.. state.settings.project_id
|
.. state.settings.project_id
|
||||||
.. " "
|
.. " "
|
||||||
.. state.settings.gitlab_url
|
.. state.settings.gitlab_url
|
||||||
.. " "
|
.. " "
|
||||||
.. state.settings.port
|
.. port
|
||||||
.. " "
|
.. " "
|
||||||
.. state.settings.auth_token
|
.. state.settings.auth_token
|
||||||
.. " "
|
.. " "
|
||||||
.. state.settings.log_path
|
.. state.settings.log_path
|
||||||
|
|
||||||
vim.fn.jobstart(command, {
|
local job_id = vim.fn.jobstart(command, {
|
||||||
on_stdout = function(job_id)
|
on_stdout = function(_, data)
|
||||||
if job_id <= 0 then
|
-- if port was not provided then we need to parse it from output of server
|
||||||
vim.notify("Could not start gitlab.nvim binary", vim.log.levels.ERROR)
|
if parsed_port == nil then
|
||||||
else
|
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()
|
||||||
|
callback_called = true
|
||||||
|
elseif not callback_called then
|
||||||
|
vim.notify("Failed to parse server port", vim.log.levels.ERROR)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
on_stderr = function(_, errors)
|
on_stderr = function(_, errors)
|
||||||
local err_msg = ''
|
local err_msg = ""
|
||||||
for _, err in ipairs(errors) do
|
for _, err in ipairs(errors) do
|
||||||
if err ~= "" and err ~= nil then
|
if err ~= "" and err ~= nil then
|
||||||
err_msg = err_msg .. err .. "\n"
|
err_msg = err_msg .. err .. "\n"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if err_msg ~= '' then vim.notify(err_msg, vim.log.levels.ERROR) end
|
if err_msg ~= "" then
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Builds the Go binary
|
-- Builds the Go binary
|
||||||
M.build = function(override)
|
M.build = function(override)
|
||||||
local file_path = u.current_file_path()
|
local file_path = u.current_file_path()
|
||||||
@@ -50,12 +80,13 @@ M.build = function(override)
|
|||||||
|
|
||||||
if not override then
|
if not override then
|
||||||
local binary_exists = vim.loop.fs_stat(state.settings.bin)
|
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
|
end
|
||||||
|
|
||||||
local cmd = u.is_windows() and
|
local cmd = u.is_windows() and "cd %s\\cmd && go build -o bin.exe && move bin.exe ..\\"
|
||||||
'cd %s\\cmd && go build -o bin.exe && move bin.exe ..\\' or
|
or "cd %s/cmd && go build -o bin && mv bin ../bin"
|
||||||
'cd %s/cmd && go build -o bin && mv bin ../bin'
|
|
||||||
|
|
||||||
local command = string.format(cmd, state.settings.bin_path)
|
local command = string.format(cmd, state.settings.bin_path)
|
||||||
local null = u.is_windows() and " >NUL" or " > /dev/null"
|
local null = u.is_windows() and " >NUL" or " > /dev/null"
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ local u = require("gitlab.utils")
|
|||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
-- These are the default settings for the plugin
|
-- These are the default settings for the plugin
|
||||||
M.settings = {
|
M.settings = {
|
||||||
port = 21036,
|
port = nil, -- choose random port
|
||||||
log_path = (vim.fn.stdpath("cache") .. "/gitlab.nvim.log"),
|
log_path = (vim.fn.stdpath("cache") .. "/gitlab.nvim.log"),
|
||||||
reviewer = "delta",
|
reviewer = "delta",
|
||||||
attachment_dir = '',
|
attachment_dir = "",
|
||||||
popup = {
|
popup = {
|
||||||
exit = "<Esc>",
|
exit = "<Esc>",
|
||||||
perform_action = "<leader>s",
|
perform_action = "<leader>s",
|
||||||
@@ -29,15 +29,15 @@ M.settings = {
|
|||||||
relative = "editor",
|
relative = "editor",
|
||||||
position = "left",
|
position = "left",
|
||||||
size = "20%",
|
size = "20%",
|
||||||
resolved = '✓',
|
resolved = "✓",
|
||||||
unresolved = '',
|
unresolved = "",
|
||||||
},
|
},
|
||||||
review_pane = {
|
review_pane = {
|
||||||
delta = {
|
delta = {
|
||||||
added_file = "",
|
added_file = "",
|
||||||
modified_file = "",
|
modified_file = "",
|
||||||
removed_file = "",
|
removed_file = "",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
pipeline = {
|
pipeline = {
|
||||||
created = "",
|
created = "",
|
||||||
|
|||||||
@@ -126,20 +126,10 @@ M.create_popup_state = function(title, width, height)
|
|||||||
end
|
end
|
||||||
|
|
||||||
M.merge = function(defaults, overrides)
|
M.merge = function(defaults, overrides)
|
||||||
local result = {}
|
|
||||||
if type(defaults) == "table" and M.table_size(defaults) == 0 and type(overrides) == "table" then
|
if type(defaults) == "table" and M.table_size(defaults) == 0 and type(overrides) == "table" then
|
||||||
return overrides
|
return overrides
|
||||||
end
|
end
|
||||||
|
return vim.tbl_deep_extend("force", defaults, overrides)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
M.join = function(tbl, separator)
|
M.join = function(tbl, separator)
|
||||||
|
|||||||
Reference in New Issue
Block a user