@@ -122,6 +122,8 @@ var CliClientOpenCommand = cli.Command{
122122
123123 SshPath : sshPath ,
124124 Debug : isDebug ,
125+
126+ TunneledPorts : make (map [string ]bool ),
125127 }
126128
127129 if nvrhContext .SshPath == "internal" {
@@ -466,6 +468,12 @@ func BuildClientNvimCmd(nvrhContext *context.NvrhContext) *exec.Cmd {
466468
467469func prepareRemoteNvim (nvrhContext * context.NvrhContext , nv * nvim.Nvim ) error {
468470 nv .RegisterHandler ("tunnel-port" , func (v * nvim.Nvim , args []string ) {
471+ if _ , ok := nvrhContext .TunneledPorts [args [0 ]]; ok {
472+ return
473+ }
474+
475+ nvrhContext .TunneledPorts [args [0 ]] = true
476+
469477 go nvrhContext .SshClient .TunnelSocket (& ssh_tunnel_info.SshTunnelInfo {
470478 Mode : "port" ,
471479 LocalSocket : args [0 ],
@@ -533,6 +541,72 @@ os.execute('chmod +x ' .. browser_script_path)
533541return true
534542 ` , nil , nvrhContext .BrowserScriptPath , nvrhContext .RemoteSocketOrPort (), nv .ChannelID ())
535543
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+ },
562+ }
563+
564+ function nvrh_port_scanner.attach_port_watcher(bufnr)
565+ if vim.bo[bufnr].buftype ~= "terminal" then
566+ return
567+ end
568+
569+ -- Already watching?
570+ if nvrh_port_scanner.active_watchers[bufnr] then
571+ return
572+ end
573+
574+ local function on_lines(_, _, _, lastline, new_lastline, _)
575+ local lines = vim.api.nvim_buf_get_lines(bufnr, lastline, new_lastline, false)
576+
577+ for _, line in ipairs(lines) do
578+ for _, pattern in ipairs(nvrh_port_scanner.patterns) do
579+ local port = string.match(line, pattern)
580+ if port then
581+ if nvrh_port_scanner.mapped_ports[port] then
582+ -- Already notified about this port
583+ else
584+ nvrh_port_scanner.mapped_ports[port] = true
585+ vim.rpcnotify(tonumber(os.getenv("NVRH_CHANNEL_ID")), "tunnel-port", { port })
586+ end
587+ break
588+ end
589+ end
590+ end
591+ end
592+
593+ local detach = vim.api.nvim_buf_attach(bufnr, false, {
594+ on_lines = on_lines,
595+ })
596+
597+ nvrh_port_scanner.active_watchers[bufnr] = detach
598+ end
599+
600+ -- Attach watcher on TermOpen
601+ vim.api.nvim_create_autocmd("TermOpen", {
602+ callback = function(args)
603+ nvrh_port_scanner.attach_port_watcher(args.buf)
604+ end,
605+ })
606+
607+ return true
608+ ` , nil , nil )
609+
536610 if err := batch .Execute (); err != nil {
537611 return err
538612 }
0 commit comments