Skip to content

Commit fe605f9

Browse files
authored
feat: Automatically map ports via scanning (#31)
This works by scanning terminal output for something that looks like a port being logged. Closes #10
1 parent 56a5415 commit fe605f9

2 files changed

Lines changed: 76 additions & 3 deletions

File tree

src/client/main.go

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ var CliClientOpenCommand = cli.Command{
124124

125125
SshPath: sshPath,
126126
Debug: isDebug,
127+
128+
TunneledPorts: make(map[string]bool),
127129
}
128130

129131
if nvrhContext.SshPath == "internal" {
@@ -469,6 +471,12 @@ func BuildClientNvimCmd(nvrhContext *context.NvrhContext) *exec.Cmd {
469471

470472
func prepareRemoteNvim(nvrhContext *context.NvrhContext, nv *nvim.Nvim) error {
471473
nv.RegisterHandler("tunnel-port", func(v *nvim.Nvim, args []string) {
474+
if _, ok := nvrhContext.TunneledPorts[args[0]]; ok {
475+
return
476+
}
477+
478+
nvrhContext.TunneledPorts[args[0]] = true
479+
472480
go nvrhContext.SshClient.TunnelSocket(&ssh_tunnel_info.SshTunnelInfo{
473481
Mode: "port",
474482
LocalSocket: args[0],
@@ -499,7 +507,6 @@ vim.api.nvim_create_user_command(
499507
force = true,
500508
}
501509
)
502-
return true
503510
`, nil)
504511

505512
// Add command to open url.
@@ -532,10 +539,74 @@ script_contents = string.format(script_contents, socket_path, channel_id)
532539
533540
vim.fn.writefile(vim.fn.split(script_contents, '\n'), browser_script_path)
534541
os.execute('chmod +x ' .. browser_script_path)
535-
536-
return true
537542
`, nil, nvrhContext.BrowserScriptPath, nvrhContext.RemoteSocketOrPort(), nv.ChannelID())
538543

544+
batch.ExecLua(`
545+
local nvrh_port_scanner = {
546+
active_watchers = {},
547+
548+
mapped_ports = {},
549+
550+
patterns = {
551+
-- "port 3000"
552+
"port%s+(%d+)",
553+
-- "localhost:3000"
554+
"localhost:(%d+)",
555+
-- "0.0.0.0:3000"
556+
"%d+%.%d+%.%d+%.%d+:(%d+)",
557+
-- ":3000" at start of line
558+
"^:(%d+)",
559+
-- ":3000" but avoid eslint errors (error in foo.tsx:3)
560+
"%s+:(%d+)",
561+
-- http://some.domain.com:3000 / https://some.domain.com:3000
562+
"https?://[^/]+:(%d+)",
563+
},
564+
}
565+
566+
function nvrh_port_scanner.attach_port_watcher(bufnr)
567+
if vim.bo[bufnr].buftype ~= "terminal" then
568+
return
569+
end
570+
571+
-- Already watching?
572+
if nvrh_port_scanner.active_watchers[bufnr] then
573+
return
574+
end
575+
576+
local function on_lines(_, _, _, lastline, new_lastline, _)
577+
local lines = vim.api.nvim_buf_get_lines(bufnr, lastline, new_lastline, false)
578+
579+
for _, line in ipairs(lines) do
580+
for _, pattern in ipairs(nvrh_port_scanner.patterns) do
581+
local port = string.match(line, pattern)
582+
if port then
583+
if nvrh_port_scanner.mapped_ports[port] then
584+
-- Already notified about this port
585+
else
586+
nvrh_port_scanner.mapped_ports[port] = true
587+
vim.rpcnotify(tonumber(os.getenv("NVRH_CHANNEL_ID")), "tunnel-port", { port })
588+
end
589+
break
590+
end
591+
end
592+
end
593+
end
594+
595+
vim.api.nvim_buf_attach(bufnr, false, {
596+
on_lines = on_lines,
597+
})
598+
599+
nvrh_port_scanner.active_watchers[bufnr] = true
600+
end
601+
602+
-- Attach watcher on TermOpen
603+
vim.api.nvim_create_autocmd("TermOpen", {
604+
callback = function(args)
605+
nvrh_port_scanner.attach_port_watcher(args.buf)
606+
end,
607+
})
608+
`, nil)
609+
539610
batch.ExecLua(`
540611
local original_open = vim.ui.open
541612
vim.ui.open = function(uri, opts)

src/context/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ type NvrhContext struct {
3030
Debug bool
3131

3232
SshClient nvrh_base_ssh.BaseNvrhSshClient
33+
34+
TunneledPorts map[string]bool
3335
}
3436

3537
func (nc *NvrhContext) LocalSocketOrPort() string {

0 commit comments

Comments
 (0)