diff --git a/go.mod b/go.mod index 5871ecbab2..7c1797dc28 100644 --- a/go.mod +++ b/go.mod @@ -10,18 +10,18 @@ go 1.27.2 require ( charm.land/bubbles/v2 v2.2.1 - charm.land/bubbletea/v2 v2.0.9 + charm.land/bubbletea/v2 v2.1.0 charm.land/lipgloss/v2 v2.0.6 charm.land/ssh v0.4.3 - charm.land/wish/v2 v2.0.3 + charm.land/wish/v2 v2.0.5 filippo.io/mlockexe v1.0.0 - github.com/a-h/templ v0.3.1020 + github.com/a-h/templ v0.3.1070 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be - github.com/beevik/ntp v1.5.0 + github.com/beevik/ntp v1.6.0 github.com/charmbracelet/colorprofile v0.4.3 github.com/cilium/ebpf v0.22.0 github.com/creack/pty v1.1.24 - github.com/gaissmai/bart v0.29.0 + github.com/gaissmai/bart v0.30.0 github.com/gokrazy/tools v0.0.0-20260703063348-3fe400c13246 github.com/gokrazy/updater v0.0.0-20260620140544-0a84d8ab3878 github.com/golangci/golangci-lint/v2 v2.14.0 @@ -29,35 +29,35 @@ require ( github.com/insomniacslk/dhcp v0.0.0-20260719225207-c76316d4aa82 github.com/mdlayher/genetlink v1.4.0 github.com/mdlayher/netlink v1.11.2 - github.com/mdlayher/packet v1.1.2 + github.com/mdlayher/packet v1.2.0 github.com/miekg/dns v1.1.73 github.com/muesli/reflow v0.3.0 github.com/openconfig/gnmi v0.14.1 github.com/openconfig/goyang v1.6.3 github.com/packetcap/go-pcap v0.0.0-20251215121130-f2cf9f991e7c - github.com/prometheus/client_golang v1.24.1 - github.com/prometheus/client_model v0.6.2 - github.com/prometheus/common v0.70.1 + github.com/prometheus/client_golang v1.25.0 + github.com/prometheus/client_model v0.6.3 + github.com/prometheus/common v0.72.0 github.com/prometheus/procfs v0.22.0 github.com/sirupsen/logrus v1.10.2 github.com/sivchari/gomu v0.2.1 github.com/stretchr/testify v1.12.1 github.com/vishvananda/netlink v1.3.1 github.com/vishvananda/netns v0.0.5 - github.com/yuin/goldmark v1.8.5 + github.com/yuin/goldmark v1.8.6 go.fd.io/govpp v0.13.0 - golang.org/x/crypto v0.57.0 + golang.org/x/crypto v0.58.0 golang.org/x/mod v0.41.0 - golang.org/x/net v0.60.0 - golang.org/x/sync v0.23.0 - golang.org/x/sys v0.48.0 - golang.org/x/term v0.46.0 - golang.org/x/text v0.42.0 - golang.org/x/tools v0.50.0 - golang.org/x/tools/gopls v0.0.0-20260827201838-8471f958f05f // first Go 1.27-compatible revision after v0.23.0 - golang.org/x/vuln v1.7.0 + golang.org/x/net v0.61.0 + golang.org/x/sync v0.24.0 + golang.org/x/sys v0.49.0 + golang.org/x/term v0.47.0 + golang.org/x/text v0.43.0 + golang.org/x/tools v0.51.1-0.20261007173519-d3cf2258b6cf + golang.org/x/tools/gopls v0.24.0-pre.2 // first Go 1.27-compatible revision after v0.23.0 + golang.org/x/vuln v1.8.0 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 - google.golang.org/grpc v1.83.2 + google.golang.org/grpc v1.84.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 google.golang.org/protobuf v1.36.12 gopkg.in/yaml.v3 v3.0.1 @@ -67,7 +67,7 @@ require ( require ( 4d63.com/gocheckcompilerdirectives v1.4.0 // indirect 4d63.com/gochecknoglobals v0.2.2 // indirect - charm.land/log/v2 v2.0.0 // indirect + charm.land/log/v2 v2.0.1 // indirect codeberg.org/chavacava/garif v0.2.1 // indirect codeberg.org/polyfloyd/go-errorlint v1.9.0 // indirect dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect @@ -94,7 +94,7 @@ require ( github.com/alfatraining/structtag v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect github.com/alingse/nilnesserr v0.2.0 // indirect - github.com/andybalholm/brotli v1.1.0 // indirect + github.com/andybalholm/brotli v1.2.6 // indirect github.com/antihax/optional v1.0.0 // indirect github.com/ashanbrown/forbidigo/v2 v2.3.1 // indirect github.com/ashanbrown/makezero/v2 v2.2.1 // indirect @@ -114,8 +114,8 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.11 // indirect github.com/charmbracelet/keygen v0.5.4 // indirect - github.com/charmbracelet/ultraviolet v0.0.0-20260811164956-006e29f97886 // indirect - github.com/charmbracelet/x/ansi v0.11.8 // indirect + github.com/charmbracelet/ultraviolet v0.0.0-20261008173134-6b8d4baf91b4 // indirect + github.com/charmbracelet/x/ansi v0.11.9 // indirect github.com/charmbracelet/x/conpty v0.2.0 // indirect github.com/charmbracelet/x/term v0.2.2 // indirect github.com/charmbracelet/x/termios v0.1.1 // indirect @@ -137,7 +137,7 @@ require ( github.com/fatih/gomodifytags v1.17.1-0.20250423142747-f3939df9aa3c // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/firefart/nonamedreturns v1.0.8 // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/ftrvxmtrx/fd v0.0.0-20150925145434-c6d800382fff // indirect github.com/fzipp/gocyclo v0.6.0 // indirect github.com/ghostiam/protogetter v1.0.1 // indirect @@ -190,7 +190,7 @@ require ( github.com/karamaru-alpha/copyloopvar v1.2.2 // indirect github.com/kisielk/errcheck v1.20.0 // indirect github.com/kkHAIKE/contextcheck v1.1.6 // indirect - github.com/klauspost/compress v1.19.1 // indirect + github.com/klauspost/compress v1.20.1 // indirect github.com/kulti/thelper v0.7.1 // indirect github.com/kunwardeep/paralleltest v1.0.15 // indirect github.com/ldez/exptostd v0.4.5 // indirect @@ -210,13 +210,13 @@ require ( github.com/maratori/testpackage v1.1.2 // indirect github.com/matoous/godox v1.1.0 // indirect github.com/mattn/go-colorable v0.1.15 // indirect - github.com/mattn/go-isatty v0.0.22 // indirect - github.com/mattn/go-runewidth v0.0.27 // indirect - github.com/mdlayher/socket v0.6.0 // indirect + github.com/mattn/go-isatty v0.0.24 // indirect + github.com/mattn/go-runewidth v0.0.30 // indirect + github.com/mdlayher/socket v0.7.0 // indirect github.com/mgechev/revive v1.17.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/modelcontextprotocol/go-sdk v1.6.1 // indirect + github.com/modelcontextprotocol/go-sdk v1.8.0 // indirect github.com/moricho/tparallel v0.3.2 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -228,7 +228,7 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.4.3 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect - github.com/pires/go-proxyproto v0.12.0 // indirect + github.com/pires/go-proxyproto v0.15.0 // indirect github.com/quasilyte/go-ruleguard v0.4.5 // indirect github.com/quasilyte/go-ruleguard/dsl v0.3.23 // indirect github.com/quasilyte/gogrep v0.5.0 // indirect @@ -271,7 +271,7 @@ require ( github.com/uudashr/gocognit v1.2.1 // indirect github.com/uudashr/iface v1.5.1 // indirect github.com/xen0n/gosmopolitan v1.3.0 // indirect - github.com/xo/terminfo v1.0.0 // indirect + github.com/xo/terminfo v1.2.0 // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.3.0 // indirect github.com/ykadowak/zerologlint v0.1.5 // indirect @@ -285,10 +285,11 @@ require ( go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto/x509roots/fallback v0.0.0-20260630172432-7626c5025624 // indirect - golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect - golang.org/x/exp/typeparams v0.0.0-20260812173653-3d80eb74bc5b // indirect - golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/telemetry v0.0.0-20260908163034-4bcc4b2ee518 // indirect + golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba // indirect + golang.org/x/exp/typeparams v0.0.0-20260908205506-85c1c2202aba // indirect + golang.org/x/oauth2 v0.37.0 // indirect + golang.org/x/telemetry v0.0.0-20260924152758-ed294f943157 // indirect + golang.org/x/time v0.16.0 // indirect golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 106d1e1641..47dbd2073c 100644 --- a/go.sum +++ b/go.sum @@ -4,16 +4,16 @@ 4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0= charm.land/bubbles/v2 v2.2.1 h1:Fq1+qm5hV6GkvzLQDhCBpXXE5tLgvh1PRriCLwSvIQU= charm.land/bubbles/v2 v2.2.1/go.mod h1:wdMgn+sje1KNXdwFizIWjbf328fIUBxqEmJ/vYPo8yc= -charm.land/bubbletea/v2 v2.0.9 h1:DpJCMWKgzQK8SJv4zbKKFHAI10ymWy/evClPFk0k0f8= -charm.land/bubbletea/v2 v2.0.9/go.mod h1:2SkdgoTXluXJHOUwAoRlRXF/28vklb1rFl6GcgV1/ss= +charm.land/bubbletea/v2 v2.1.0 h1:A9fj9h5LFrvQhQHFNggp8K8rvY6CUfgn9dMbb6p2HIw= +charm.land/bubbletea/v2 v2.1.0/go.mod h1:xWHeyxIX9/wiFU2kgPW04ojOZyTzrWf+8lGjUCaftiU= charm.land/lipgloss/v2 v2.0.6 h1:EaGKeuA8FvF+v2BT5VmZd2LoYLaMZJXA5n34th8nCIQ= charm.land/lipgloss/v2 v2.0.6/go.mod h1:ipDDJNSGa1hlwDtSfW1s2/xR8Vdhbut4PXh2zEKZd0Q= -charm.land/log/v2 v2.0.0 h1:SY3Cey7ipx86/MBXQHwsguOT6X1exT94mmJRdzTNs+s= -charm.land/log/v2 v2.0.0/go.mod h1:c3cZSRqm20qUVVAR1WmS/7ab8bgha3C6G7DjPcaVZz0= +charm.land/log/v2 v2.0.1 h1:uMk+t1hM/fomIqt7vn/Ub0AKo7IHvnDu6NK17nbOVx0= +charm.land/log/v2 v2.0.1/go.mod h1:VXZNB8zoPPEIcvd7bRi2fpQpGCWweCSfWOwczA9dibs= charm.land/ssh v0.4.3 h1:hr5cYmlUYsP+KxyG/ug7ZMzKKN9f6VOD9yPpcGqdzzM= charm.land/ssh v0.4.3/go.mod h1:so/3IECPNlYZSnE7JKn7NFmcUyyxJqIAeM4TJy35qPk= -charm.land/wish/v2 v2.0.3 h1:Xkgw31lEH9AJkPfgXYYvsgrskfDIY9ffHTxFRV4UT+4= -charm.land/wish/v2 v2.0.3/go.mod h1:i8gFfXu+IyMcGpRh6D84Wa+mDGwjYCKWcA86R+IJf0c= +charm.land/wish/v2 v2.0.5 h1:9JSBsbHWxspL5vqc9qJOnIa8T0QRz3un0n3sF/+Vo10= +charm.land/wish/v2 v2.0.5/go.mod h1:o9NmLxh1EnQk1ZBalvXlonBMmXo9iZ+SH08NVjiRmLw= codeberg.org/chavacava/garif v0.2.1 h1:K9oYxSlvlXrHXyW26Z4q61bTpJDo1wbvXcYKar/F/LM= codeberg.org/chavacava/garif v0.2.1/go.mod h1:oHnDSmc0f9K1MeE+MQD/yjkiIB5Xsn5y3S9Dg96Xk84= codeberg.org/polyfloyd/go-errorlint v1.9.0 h1:VkdEEmA1VBpH6ecQoMR4LdphVI3fA4RrCh2an7YmodI= @@ -54,8 +54,8 @@ github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsu github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo= github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo= github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ= -github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw= -github.com/a-h/templ v0.3.1020/go.mod h1:A2DlK61v+K+NRoGnhmYbNYVmtYHcFO5/AisMvBdDxTM= +github.com/a-h/templ v0.3.1070 h1:3iCOXHXqFXa99OQzgkUOLJbNzv/QjFGLwKZkBAg+ADA= +github.com/a-h/templ v0.3.1070/go.mod h1:iIqB3g2RHBe/WlnPwa+Bvhh1TKXHWLRnBD9NWHOLm78= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.27.0 h1:FodwmyOBgJULFYmDqibcp9pvfDLWdtPRh9v/r5BXYZs= @@ -74,8 +74,8 @@ github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQ github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w= github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/andybalholm/brotli v1.2.6 h1:ftYnfj6usCp+UGV5kSJ3+chpMQgU+gJf/AxsUQ52REI= +github.com/andybalholm/brotli v1.2.6/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= @@ -88,8 +88,8 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o= github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w= -github.com/beevik/ntp v1.5.0 h1:y+uj/JjNwlY2JahivxYvtmv4ehfi3h74fAuABB9ZSM4= -github.com/beevik/ntp v1.5.0/go.mod h1:mJEhBrwT76w9D+IfOEGvuzyuudiW9E52U2BaTrMOYow= +github.com/beevik/ntp v1.6.0 h1:bW359Vvw6bicGhKsVi+ZDLMsMYzb2ohUpLaF/Juv0k8= +github.com/beevik/ntp v1.6.0/go.mod h1:VTAbDFySkuc8cQiAYHhXslAHjJdenICebskhYqBWC4k= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w= @@ -122,10 +122,10 @@ github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q= github.com/charmbracelet/keygen v0.5.4 h1:XQYgf6UEaTGgQSSmiPpIQ78WfseNQp4Pz8N/c1OsrdA= github.com/charmbracelet/keygen v0.5.4/go.mod h1:t4oBRr41bvK7FaJsAaAQhhkUuHslzFXVjOBwA55CZNM= -github.com/charmbracelet/ultraviolet v0.0.0-20260811164956-006e29f97886 h1:rdnVWKgJpTVXKuKuJyxDJ+NFJdUaUqGvyGy61OcvlbA= -github.com/charmbracelet/ultraviolet v0.0.0-20260811164956-006e29f97886/go.mod h1:nAw0d9PhFp1qdzi2xhQU5YOu5sVpDIHWlaW2Uz/bCro= -github.com/charmbracelet/x/ansi v0.11.8 h1:JMFwp0CgDC2+jcOB162HH5k7I3FVbgFSMMYg7dSPBQQ= -github.com/charmbracelet/x/ansi v0.11.8/go.mod h1:ZNN+3mXny/516oTQPLMPIBeSINvNJJQ8uQXDgbeJxY0= +github.com/charmbracelet/ultraviolet v0.0.0-20261008173134-6b8d4baf91b4 h1:hISLzUbwcvJbGoNR6A3L/TpguRRnACBmZOee8LcHLJw= +github.com/charmbracelet/ultraviolet v0.0.0-20261008173134-6b8d4baf91b4/go.mod h1:aJDVk+WOVM1K04rdc/71PMPoUGfjxjhvXfP3Pjz1Zio= +github.com/charmbracelet/x/ansi v0.11.9 h1:kIrqLBxtTVbLyyHO2Evoi93EjQo1mylWmpNEsfPxXAM= +github.com/charmbracelet/x/ansi v0.11.9/go.mod h1:DG/hpOnaI2QJ/KMMqLL2h0KVhluuJLazf8973P+Vy5w= github.com/charmbracelet/x/conpty v0.2.0 h1:eKtA2hm34qNfgJCDp/M6Dc0gLy7e07YEK4qAdNGOvVY= github.com/charmbracelet/x/conpty v0.2.0/go.mod h1:fexgUnVrZgw8scD49f6VSi0Ggj9GWYIrpedRthAwW/8= github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA= @@ -185,14 +185,14 @@ github.com/firefart/nonamedreturns v1.0.8 h1:iB32Dl17zJl1zlVEj/WlUWgx0HiRyQ85OUw github.com/firefart/nonamedreturns v1.0.8/go.mod h1:vxFNvm5AfP/8rgAKFzYmnqx0yp1HjrYsErZ9pHPTznA= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= +github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/ftrvxmtrx/fd v0.0.0-20150925145434-c6d800382fff h1:zk1wwii7uXmI0znwU+lqg+wFL9G5+vm5I+9rv2let60= github.com/ftrvxmtrx/fd v0.0.0-20150925145434-c6d800382fff/go.mod h1:yUhRXHewUVJ1k89wHKP68xfzk7kwXUx/DV1nx4EBMbw= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/gaissmai/bart v0.29.0 h1:wO6HGE8g9YE0Wm0bCpYxwRzfQ4+fbJKOhL64e5ACGCI= -github.com/gaissmai/bart v0.29.0/go.mod h1:GREWQfTLRWz/c5FTOsIw+KkscuFkIV5t8Rp7Nd1Td5c= +github.com/gaissmai/bart v0.30.0 h1:K1oTSQ6Nf76bhrgxeTqzZa4hE0M9x4xDfL6n6UG67Sc= +github.com/gaissmai/bart v0.30.0/go.mod h1:GREWQfTLRWz/c5FTOsIw+KkscuFkIV5t8Rp7Nd1Td5c= github.com/ghostiam/protogetter v1.0.1 h1:q4LLTx2KJHJpxrfO3BniUDWoFkoqWD/UlHhClBlUlLM= github.com/ghostiam/protogetter v1.0.1/go.mod h1:50Fh5yZfs6SFGrd1FlmT04NcguyEOxyjNK5zGD6/EvM= github.com/go-critic/go-critic v0.15.0 h1:MDUZIQKytIl+vPEc0Igw51m4uLTdi1ZGz2jatEqNs1g= @@ -201,8 +201,6 @@ github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkv github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-quicktest/qt v1.102.0 h1:HSQxCeh5YZH3EL3W39ixjtyaEhcWSXQHtHnMBzSs474= github.com/go-quicktest/qt v1.102.0/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -294,8 +292,6 @@ github.com/google/renameio/v2 v2.0.2 h1:qKZs+tfn+arruZZhQ7TKC/ergJunuJicWS6gLDt/ github.com/google/renameio/v2 v2.0.2/go.mod h1:OX+G6WHHpHq3NVj7cAOleLOwJfcQ1s3uUJQCrr78SWo= github.com/google/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8= github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopacket/gopacket v1.2.0 h1:eXbzFad7f73P1n2EJHQlsKuvIMJjVXK5tXoSca78I3A= github.com/gopacket/gopacket v1.2.0/go.mod h1:BrAKEy5EOGQ76LSqh7DMAr7z0NNPdczWm2GxCG7+I8M= github.com/gordonklaus/ineffassign v0.2.0 h1:Uths4KnmwxNJNzq87fwQQDDnbNb7De00VOk9Nu0TySs= @@ -351,8 +347,8 @@ github.com/kisielk/errcheck v1.20.0 h1:9rwHBNKzd4wkDWcROy3DvFGNqEPlkxBg305rvk7Ha github.com/kisielk/errcheck v1.20.0/go.mod h1:O+f80MKNwX8Oor2jwgpeQ9An7uJm+hRSgT+h22knRJU= github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE= github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg= -github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk= -github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ= +github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -400,19 +396,19 @@ github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= -github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= +github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI= +github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.27 h1:Feg/Oou5zI/wnpgDF6omIU0OokC9GxLC/WRknhVlIR0= -github.com/mattn/go-runewidth v0.0.27/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8= +github.com/mattn/go-runewidth v0.0.30 h1:+KUuiDA4fF0R1p5FeueHefjDm+GIM+kWfFnDjybOPgk= +github.com/mattn/go-runewidth v0.0.30/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8= github.com/mdlayher/genetlink v1.4.0 h1:f/Xs7Y2T+GyX9b3dbiUhnLE9InGs5F9RxJ2JwBMl71o= github.com/mdlayher/genetlink v1.4.0/go.mod h1:d1hrKr8fwZU2JkcAtQUAzeTrI7nbgQSl+5k1cC0biSA= github.com/mdlayher/netlink v1.11.2 h1:HKh2jqe+omdSWcQ88nrT7INE61B0NXfiSPFdgL4YbNI= github.com/mdlayher/netlink v1.11.2/go.mod h1:uT2Yc/QLaZubzDpZIBi9d4GoeLwtp3x1AMeqSRrK2sA= -github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY= -github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4= -github.com/mdlayher/socket v0.6.0 h1:ScZPaAGyO1icQnbFrhPM8mnXyMu9qukC1K4ZoM2IQKU= -github.com/mdlayher/socket v0.6.0/go.mod h1:q7vozUAnxSqnjHc12Fik5yUKIzfZ8ITCfMkhOtE9z18= +github.com/mdlayher/packet v1.2.0 h1:SX7/oY0+nkKo7p4Sv30+TIc3YuWgvDEf0eucTXRyPqk= +github.com/mdlayher/packet v1.2.0/go.mod h1:UtYVnQt+Zj6UD0F89HfzkqGXaYqys1NwkrS86wlLCv4= +github.com/mdlayher/socket v0.7.0 h1:qVREPVwtUMg17pwvveQxpurq0PVisMxi3FGgpsorMYQ= +github.com/mdlayher/socket v0.7.0/go.mod h1:f7iKql2EK/rfsWYDKofKjk2Ig7I+6HQWdMIdn1tO4A4= github.com/mgechev/revive v1.17.0 h1:hwTTVC3svSv9X2FA2hiYT17Uws+lfxDWhPxzBZZCREg= github.com/mgechev/revive v1.17.0/go.mod h1:uiVNUF8ruVh4WHm/aPs7kKEeuo+m6oO3tuOfniyzP/g= github.com/miekg/dns v1.1.73 h1:uhT8nJxmTrPJYClxVxTCX+CVn6qnzSiybRk72Z6DgrE= @@ -423,8 +419,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modelcontextprotocol/go-sdk v1.6.1 h1:0zOSupjKUxPKSocPT1Wtago+mUHU2/uZ4xSOY0FGReU= -github.com/modelcontextprotocol/go-sdk v1.6.1/go.mod h1:kzm3kzFL1/+AziGOE0nUs3gvPoNxMCvkxokMkuFapXQ= +github.com/modelcontextprotocol/go-sdk v1.8.0 h1:KIvahhYqwtbeniWVPs3TcXEA7b8jEtwfBpOTAI+Urx4= +github.com/modelcontextprotocol/go-sdk v1.8.0/go.mod h1:dL7u98E/zjJTGzEq+j30jQ8K2k1mb6LeAH4inEcSGts= github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= @@ -464,15 +460,15 @@ github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdD github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pires/go-proxyproto v0.12.0 h1:TTCxD66dU898tahivkqc3hoceZp7P44FnorWyo9d5vM= -github.com/pires/go-proxyproto v0.12.0/go.mod h1:qUvfqUMEoX7T8g0q7TQLDnhMjdTrxnG0hvpMn+7ePNI= +github.com/pires/go-proxyproto v0.15.0 h1:dTshmNbFm/D+0+sbrxUuddPOZ5Y0B7c5NhtsBkm6LqI= +github.com/pires/go-proxyproto v0.15.0/go.mod h1:OXsCrKwrK2tXS9YrI5tkHx5xaQlO8FH3lFW76orFh24= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= -github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= -github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= -github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY= -github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc= +github.com/prometheus/client_golang v1.25.0 h1:cDtW1TcFsMi1RJc8khwCKB4VhzwV04aKqbYtNOXuiK0= +github.com/prometheus/client_golang v1.25.0/go.mod h1:j2X/4/kaQHLPnmyEU8zjmkndo1T317VpCIQEml8LTKk= +github.com/prometheus/client_model v0.6.3 h1:O0jaTVAYNxTHYInEPFJt5I3+sN8zqBtVMPTB1qyxiEo= +github.com/prometheus/client_model v0.6.3/go.mod h1:gpN5P9S7Rr6Yr92PiQ+Ixvhf6JZEkF1dnxsYL2aPBEM= +github.com/prometheus/common v0.72.0 h1:tAYsE+sPJxIncDAobm4H5aQjmox9ZxEIIqPbiffa8G4= +github.com/prometheus/common v0.72.0/go.mod h1:77NWqAQ2tXT7BIK40qjJdw5Acrsrg1TlHAnsQi3i6mk= github.com/prometheus/procfs v0.22.0 h1:6q9+/JL9IKAPbCmBrv9n5O5Ty3NKnciV5X7YGw0oics= github.com/prometheus/procfs v0.22.0/go.mod h1:CvmFr/GVhIjIvWJZW3tgkODBQMRIf0EyWMQLHCHab58= github.com/quasilyte/go-ruleguard v0.4.5 h1:AGY0tiOT5hJX9BTdx/xBdoCubQUAE2grkqY2lSwvZcA= @@ -514,8 +510,8 @@ github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0= github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo= github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q= github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= @@ -584,8 +580,10 @@ github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zd github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM= github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4= -github.com/xo/terminfo v1.0.0 h1:2ZpYzqWzyyytjk3TP6aJVDhkMAkc99/1xKQdA3TDTBY= -github.com/xo/terminfo v1.0.0/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/xo/terminfo v1.2.0 h1:d0ZTOCpuGE0lwSAOs0zcJjwz3jWQyqcRt9XbGJpCOl4= +github.com/xo/terminfo v1.2.0/go.mod h1:lGzkSo8Fe7IRh/w+Gqz7n5mDog4FVXCj3gy/DYTBqio= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= github.com/yeya24/promlinter v0.3.0 h1:JVDbMp08lVCP7Y6NP3qHroGAO6z2yGKQtS5JsjqtoFs= @@ -599,8 +597,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA= -github.com/yuin/goldmark v1.8.5/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark v1.8.6 h1:d0VcaP1sx9GkFVkoW+KtggpGi2KZ965i14b0+bDQST4= +github.com/yuin/goldmark v1.8.6/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/ze-software/goyang v1.6.4-0.20261009101552-a3cf525c4f98 h1:XGObGtdheFMN9GHTHdjW2Nb/SANnWusceBUKeBBc3Mc= github.com/ze-software/goyang v1.6.4-0.20261009101552-a3cf525c4f98/go.mod h1:JpWCtsuYMm/WDE6wJM8nkcdoJen098Cy0OP5D2oFHuQ= gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo= @@ -617,18 +615,6 @@ go.augendre.info/fatcontext v0.10.1 h1:HCS/+eob32cM0mEtmZM6NiXdqvS5KiZmsR+fCubTp go.augendre.info/fatcontext v0.10.1/go.mod h1:pqpGvA9GlrXy+aXkp8L2dKz12Zp4g2FhzcAtwToU+2w= go.fd.io/govpp v0.13.0 h1:MnjH9I5K+X0860CeeuBcMSu3uyUKA6X9AenKzdiGpnA= go.fd.io/govpp v0.13.0/go.mod h1:MQw6XdULE9qJiqYzIUXPSVyOGWCwVLMhhFRjcf+9hmM= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU= -go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ= -go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M= -go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s= -go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= -go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= -go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= -go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= -go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= -go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= @@ -648,16 +634,16 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= -golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M= -golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA= +golang.org/x/crypto v0.58.0 h1:COkYLr4k7nDI4r6QtMML2AfzeIswWHAJthLS6K/M/54= +golang.org/x/crypto v0.58.0/go.mod h1:Xh+kl5A+M0gMiWDytH2Hhr5Z7WbNCpv/eB/EFgfSk+w= golang.org/x/crypto/x509roots/fallback v0.0.0-20260630172432-7626c5025624 h1:qtosn20yWXr+4B4x23stHOMEoDZrcSR3KjGBTNKV+74= golang.org/x/crypto/x509roots/fallback v0.0.0-20260630172432-7626c5025624/go.mod h1:+UoQFNBq2p2wO+Q6ddVtYc25GZ6VNdOMyyrd4nrqrKs= -golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= -golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= +golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba h1:Ck8QetSgk912qxWLMCKxd0in+aiyBQyDSMae6e/xmpU= +golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba/go.mod h1:50RgIsmK7OwqzTTeqcSXQW8SswW0o8fRcDxmqGluJ8E= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20260812173653-3d80eb74bc5b h1:F5AefKrjGj4r4Ht9ov1buP2PTyVb65dyf4bmdVKleV0= -golang.org/x/exp/typeparams v0.0.0-20260812173653-3d80eb74bc5b/go.mod h1:PqrXSW65cXDZH0k4IeUbhmg/bcAZDbzNz3byBpKCsXo= +golang.org/x/exp/typeparams v0.0.0-20260908205506-85c1c2202aba h1:ZoZmFkcuQ289xJilBm9Ck3W+/okrhXc4LUpbtKFQE4k= +golang.org/x/exp/typeparams v0.0.0-20260908205506-85c1c2202aba/go.mod h1:qkMnvgP8C7+xkci04txL2SJFc/0PGqnkf6gs5JUC43E= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -684,10 +670,10 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/net v0.60.0 h1:79p50tfZlm0J9YfoDsSi639qSXNGVwEzOPLCxM2FsYU= -golang.org/x/net v0.60.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= -golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= -golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw= +golang.org/x/net v0.61.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI= +golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98= +golang.org/x/oauth2 v0.37.0/go.mod h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -698,8 +684,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= -golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= +golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA= +golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -721,12 +707,12 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= -golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= +golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA= +golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= -golang.org/x/telemetry v0.0.0-20260908163034-4bcc4b2ee518 h1:F5BWKvW126NXR74uxkxuc1jQHhm/rwm/J3rSiFyuRs4= -golang.org/x/telemetry v0.0.0-20260908163034-4bcc4b2ee518/go.mod h1:i+ivNqjDnTF3WTElsdk5g9V5DTSBYgdNo7xTU9SDwYA= +golang.org/x/telemetry v0.0.0-20260924152758-ed294f943157 h1:DftxASHKl6JolFPqoN+2WrFO53JvkHwRXvFqf8gsivA= +golang.org/x/telemetry v0.0.0-20260924152758-ed294f943157/go.mod h1:i+ivNqjDnTF3WTElsdk5g9V5DTSBYgdNo7xTU9SDwYA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -735,8 +721,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= -golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE= -golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc= +golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM= +golang.org/x/term v0.47.0/go.mod h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -748,8 +734,10 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= -golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= +golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc= +golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s= +golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE= +golang.org/x/time v0.16.0/go.mod h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= @@ -762,16 +750,16 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= -golang.org/x/tools v0.50.0 h1:c2ifzfcuY7L90lZ2aKd8S4K2NpASF08SZx9ZuJkHmSU= -golang.org/x/tools v0.50.0/go.mod h1:7ulVMw3831Mwi5EZD6RomGyffr4VFjuNYXf2BbCEAV0= +golang.org/x/tools v0.51.1-0.20261007173519-d3cf2258b6cf h1:mZ+XlY1AQKy9O7ms5Ta0m8tVG+0+YyysmokCRh/qvaM= +golang.org/x/tools v0.51.1-0.20261007173519-d3cf2258b6cf/go.mod h1:9eEncMayCV6zRMGhR5eZEC2iBx98qWcF1HZ9Z7wJOoA= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= -golang.org/x/tools/gopls v0.0.0-20260827201838-8471f958f05f h1:S8P21cqEuoxTVxZRLrNt+NeIgSBtJ7KPH7EplvOmdFc= -golang.org/x/tools/gopls v0.0.0-20260827201838-8471f958f05f/go.mod h1:uEkESENIgVvy7qQxNvYYmnbHzbetLR0ckeqTfQ3VCn4= -golang.org/x/vuln v1.7.0 h1:4MQBuhmXbz2uepNJrf3v+aaZLGDqw1JluwYboegA1qg= -golang.org/x/vuln v1.7.0/go.mod h1:Xw7zvU3e1bsCYYBXu+w4wcn2Kgn27f34WBCTw8LL5Us= +golang.org/x/tools/gopls v0.24.0-pre.2 h1:gWCc8mtQZ/XZsv4BmMRPFHFqPNF3vMAQ7rZDDuYm7TE= +golang.org/x/tools/gopls v0.24.0-pre.2/go.mod h1:iGxpnA0nFs1ImNLtE07y1prviuAZnlZYPh2a/oof41U= +golang.org/x/vuln v1.8.0 h1:clG4qBU6zH5VKjti8n5j8BBuYzoSha392xXMkXS351U= +golang.org/x/vuln v1.8.0/go.mod h1:Fzm4XK3Hbl1ZvZ7JpNTEWb7CJWOZ7m2LX0GLu4Fsrwo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -784,8 +772,8 @@ gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 h1:cYNAzI2sUwhmCcoj9TxvihSrqsxt6uIkj3rDRhSDmW4= google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA= -google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= -google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= +google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0= +google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 h1:rgSNvqscFZ1JgV/4wH5GOsZFSFkR2Eua9As3KIr2LlM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2/go.mod h1:iMEtFwDlAhjDU9L5mY6U1XLwlIId/G3h+QcBHDIvrJ8= google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= diff --git a/vendor/charm.land/bubbles/v2/textinput/textinput.go b/vendor/charm.land/bubbles/v2/textinput/textinput.go index 8088ba0443..c47738c111 100644 --- a/vendor/charm.land/bubbles/v2/textinput/textinput.go +++ b/vendor/charm.land/bubbles/v2/textinput/textinput.go @@ -921,12 +921,7 @@ func (m Model) Cursor() *tea.Cursor { w := lipgloss.Width promptWidth := w(m.promptView()) - // ze patch (charmbracelet/bubbles#1001, not upstream yet): the hardware - // cursor must use the SCROLL-ADJUSTED position, the same one View() draws - // with. m.Position() is absolute, so once the text is longer than the - // input width the cursor is placed past the viewport and every column - // after it is reported one place out. See internal/le/vendorpatch/patches/. - xOffset := max(0, m.pos-m.offset) + + xOffset := m.Position() + promptWidth if m.width > 0 { xOffset = min(xOffset, m.width+promptWidth) diff --git a/vendor/charm.land/bubbletea/v2/UPGRADE_GUIDE_V2.md b/vendor/charm.land/bubbletea/v2/UPGRADE_GUIDE_V2.md index 2ca7b0a08c..1fd4f8e3d7 100644 --- a/vendor/charm.land/bubbletea/v2/UPGRADE_GUIDE_V2.md +++ b/vendor/charm.land/bubbletea/v2/UPGRADE_GUIDE_V2.md @@ -98,6 +98,7 @@ The `tea.View` struct has fields for everything that used to be controlled by op | `ForegroundColor` | Set the terminal foreground color | | `BackgroundColor` | Set the terminal background color | | `ProgressBar` | Show a native terminal progress bar | +| `ProgramStatus` | Report whether the program is working, waiting on the user, done, or failed (OSC 7501) | | `KeyboardEnhancements` | Request keyboard enhancement features | | `OnMouse` | Intercept mouse messages based on view content | diff --git a/vendor/charm.land/bubbletea/v2/clipboard.go b/vendor/charm.land/bubbletea/v2/clipboard.go index 237a589edd..4baa49d764 100644 --- a/vendor/charm.land/bubbletea/v2/clipboard.go +++ b/vendor/charm.land/bubbletea/v2/clipboard.go @@ -38,7 +38,8 @@ func SetClipboard(s string) Cmd { type readClipboardMsg struct{} // ReadClipboard produces a command that reads the system clipboard using OSC52. -// Note that OSC52 is not supported in all terminals. +// Note that OSC52 is not supported in all terminals. Use [WithInput] to enable +// input, since the reply cannot be read otherwise. func ReadClipboard() Msg { return readClipboardMsg{} } @@ -64,7 +65,8 @@ type readPrimaryClipboardMsg struct{} // ReadPrimaryClipboard produces a command that reads the primary clipboard // using OSC52. Primary clipboard selection is a feature present in X11 and // Wayland only. -// Note that OSC52 is not supported in all terminals. +// Note that OSC52 is not supported in all terminals. Use [WithInput] to enable +// input, since the reply cannot be read otherwise. func ReadPrimaryClipboard() Msg { return readPrimaryClipboardMsg{} } diff --git a/vendor/charm.land/bubbletea/v2/color.go b/vendor/charm.land/bubbletea/v2/color.go index ee287de2fa..455b421640 100644 --- a/vendor/charm.land/bubbletea/v2/color.go +++ b/vendor/charm.land/bubbletea/v2/color.go @@ -10,6 +10,7 @@ import ( type backgroundColorMsg struct{} // RequestBackgroundColor is a command that requests the terminal background color. +// Use [WithInput] to enable input, since the reply cannot be read otherwise. func RequestBackgroundColor() Msg { return backgroundColorMsg{} } @@ -18,6 +19,7 @@ func RequestBackgroundColor() Msg { type foregroundColorMsg struct{} // RequestForegroundColor is a command that requests the terminal foreground color. +// Use [WithInput] to enable input, since the reply cannot be read otherwise. func RequestForegroundColor() Msg { return foregroundColorMsg{} } @@ -26,6 +28,7 @@ func RequestForegroundColor() Msg { type cursorColorMsg struct{} // RequestCursorColor is a command that requests the terminal cursor color. +// Use [WithInput] to enable input, since the reply cannot be read otherwise. func RequestCursorColor() Msg { return cursorColorMsg{} } @@ -49,20 +52,21 @@ func (e ForegroundColorMsg) IsDark() bool { // emitted when the program requests the terminal background color with the // [RequestBackgroundColor] Cmd. // -// This is commonly used in [Update.Init] to get the terminal background color +// This is commonly used in [Model.Init] to get the terminal background color // for style definitions. For that you'll want to call // [BackgroundColorMsg.IsDark] to determine if the color is dark or light. For // example: // -// func (m Model) Init() Cmd { -// return RequestBackgroundColor() +// func (m model) Init() Cmd { +// return RequestBackgroundColor // } // -// func (m Model) Update(msg Msg) (Model, Cmd) { +// func (m model) Update(msg Msg) (Model, Cmd) { // switch msg := msg.(type) { // case BackgroundColorMsg: // m.styles = newStyles(msg.IsDark()) // } +// return m, nil // } type BackgroundColorMsg struct{ color.Color } diff --git a/vendor/charm.land/bubbletea/v2/commands.go b/vendor/charm.land/bubbletea/v2/commands.go index 4497c3a388..a2c0e143f4 100644 --- a/vendor/charm.land/bubbletea/v2/commands.go +++ b/vendor/charm.land/bubbletea/v2/commands.go @@ -9,9 +9,9 @@ import ( // // Example: // -// func (m model) Init() (Model, Cmd) { -// return m, tea.Batch(someCommand, someOtherCommand) -// } +// func (m model) Init() tea.Cmd { +// return tea.Batch(someCommand, someOtherCommand) +// } func Batch(cmds ...Cmd) Cmd { return compactCmds[BatchMsg](cmds) } diff --git a/vendor/charm.land/bubbletea/v2/cursed_renderer.go b/vendor/charm.land/bubbletea/v2/cursed_renderer.go index 0ad3e602b2..e7a5b0866f 100644 --- a/vendor/charm.land/bubbletea/v2/cursed_renderer.go +++ b/vendor/charm.land/bubbletea/v2/cursed_renderer.go @@ -156,6 +156,9 @@ func (s *cursedRenderer) start() { if s.lastView.ProgressBar != nil { setProgressBar(s, s.lastView.ProgressBar) } + if s.lastView.ProgramStatus != nil { + setProgramStatus(s, nil, s.lastView.ProgramStatus) + } if !s.noInput { // Enable modifyOtherKeys and Kitty keyboard protocol. // Both can coexist; terminals ignore what they don't support. @@ -245,6 +248,9 @@ func (s *cursedRenderer) close() (err error) { if lv.ProgressBar != nil && lv.ProgressBar.State != ProgressBarNone { _, _ = s.scr.WriteString(ansi.ResetProgressBar) } + // Unlike the progress bar, the program status is left in place: done + // and error reports should outlive the program, and terminals drop + // working and blocked records when the process exits. } if s.cellbuf.Method == ansi.GraphemeWidth { @@ -506,6 +512,15 @@ func (s *cursedRenderer) flush(closing bool) error { setProgressBar(s, view.ProgressBar) } + // Report the program status if it's changed. + var lps *ProgramStatus + if s.lastView != nil { + lps = s.lastView.ProgramStatus + } + if !programStatusEquals(lps, view.ProgramStatus) { + setProgramStatus(s, lps, view.ProgramStatus) + } + // Render and queue changes to the screen buffer. s.scr.Render(s.cellbuf.RenderBuffer) @@ -850,11 +865,33 @@ func setProgressBar(s *cursedRenderer, pb *ProgressBar) { } } +func programStatusEquals(a, b *ProgramStatus) bool { + if a == nil || b == nil { + return a == b + } + return *a == *b +} + +// setProgramStatus reports cur to the terminal. When cur is nil, or addresses +// a different record than prev, the record of prev is cleared first. +func setProgramStatus(s *cursedRenderer, prev, cur *ProgramStatus) { + if prev != nil && (cur == nil || cur.ID != prev.ID) { + _, _ = s.scr.WriteString(ansi.ClearProgramStatusID(prev.ID)) + } + if cur != nil { + _, _ = s.scr.WriteString(ansi.SetProgramStatus(*cur)) + } +} + func viewEquals(a, b *View) bool { if a == nil || b == nil { return false } + if !programStatusEquals(a.ProgramStatus, b.ProgramStatus) { + return false + } + if a.Content != b.Content || a.AltScreen != b.AltScreen || a.DisableBracketedPasteMode != b.DisableBracketedPasteMode || diff --git a/vendor/charm.land/bubbletea/v2/cursor.go b/vendor/charm.land/bubbletea/v2/cursor.go index 3375a9652b..6ec17599e5 100644 --- a/vendor/charm.land/bubbletea/v2/cursor.go +++ b/vendor/charm.land/bubbletea/v2/cursor.go @@ -23,6 +23,7 @@ type requestCursorPosMsg struct{} // RequestCursorPosition is a command that requests the cursor position. // The cursor position will be sent as a [CursorPositionMsg] message. +// Use [WithInput] to enable input, since the reply cannot be read otherwise. func RequestCursorPosition() Msg { return requestCursorPosMsg{} } diff --git a/vendor/charm.land/bubbletea/v2/input.go b/vendor/charm.land/bubbletea/v2/input.go index db274fb0b4..2bca931fe3 100644 --- a/vendor/charm.land/bubbletea/v2/input.go +++ b/vendor/charm.land/bubbletea/v2/input.go @@ -49,6 +49,8 @@ func (p *Program) translateInputEvent(e uv.Event) Msg { return KeyboardEnhancementsMsg(e) case uv.ModeReportEvent: return ModeReportMsg(e) + case uv.ProgramStatusSupportEvent: + return ProgramStatusSupportMsg{} } return e } diff --git a/vendor/charm.land/bubbletea/v2/options.go b/vendor/charm.land/bubbletea/v2/options.go index dfd8cda4f8..06b380569d 100644 --- a/vendor/charm.land/bubbletea/v2/options.go +++ b/vendor/charm.land/bubbletea/v2/options.go @@ -37,6 +37,13 @@ func WithOutput(output io.Writer) ProgramOption { // won't need to use this. To disable input entirely pass nil. // // p := NewProgram(model, WithInput(nil)) +// +// Note that with input disabled, no terminal query is ever sent, since the +// reply could not be read: commands that expect a response, such as +// [RequestBackgroundColor], [RequestCursorPosition], [ReadClipboard], or +// [RequestTerminalVersion], are skipped, as is the startup capability probe. +// A model waiting on the reply message of any of those commands will never +// receive it. func WithInput(input io.Reader) ProgramOption { return func(p *Program) { p.input = input @@ -54,7 +61,7 @@ func WithInput(input io.Reader) ProgramOption { // var sess ssh.Session // ssh.Session is a type from the github.com/charmbracelet/ssh package // pty, _, _ := sess.Pty() // environ := append(sess.Environ(), "TERM="+pty.Term) -// p := tea.NewProgram(model, tea.WithEnvironment(environ) +// p := tea.NewProgram(model, tea.WithEnvironment(environ)) func WithEnvironment(env []string) ProgramOption { return func(p *Program) { p.environ = env diff --git a/vendor/charm.land/bubbletea/v2/programstatus.go b/vendor/charm.land/bubbletea/v2/programstatus.go new file mode 100644 index 0000000000..f73c45a3b7 --- /dev/null +++ b/vendor/charm.land/bubbletea/v2/programstatus.go @@ -0,0 +1,55 @@ +package tea + +import uv "github.com/charmbracelet/ultraviolet" + +// ProgramStatus is a Program Status Protocol (OSC 7501) report. It tells the +// terminal whether the program is idle, working, waiting on the user, done, +// or failed, and why. Set it on [View.ProgramStatus]. +// +// Support depends on the terminal. Terminals that don't support it ignore it. +// +// See: https://www.superlogical.com/rex/docs/build/program-status +type ProgramStatus = uv.ProgramStatus + +// ProgramState is the state of a [ProgramStatus]. +type ProgramState = uv.ProgramState + +// ProgramStatusKind says what a blocked program waits for. +type ProgramStatusKind = uv.ProgramStatusKind + +// Program states. +const ( + ProgramStateIdle = uv.ProgramStateIdle + ProgramStateWorking = uv.ProgramStateWorking + ProgramStateDone = uv.ProgramStateDone + ProgramStateBlocked = uv.ProgramStateBlocked + ProgramStateError = uv.ProgramStateError + ProgramStateClear = uv.ProgramStateClear +) + +// Program status kinds. +const ( + ProgramStatusKindPermission = uv.ProgramStatusKindPermission + ProgramStatusKindQuestion = uv.ProgramStatusKindQuestion + ProgramStatusKindAuth = uv.ProgramStatusKindAuth +) + +// ProgramStatusSupportMsg is sent when the terminal replies to +// [RequestProgramStatusSupport], meaning it supports the Program Status +// Protocol. +type ProgramStatusSupportMsg struct{} + +// programStatusSupportMsg is an internal message that queries the terminal +// for Program Status Protocol support. +type programStatusSupportMsg struct{} + +// RequestProgramStatusSupport is a command that asks the terminal whether it +// supports the Program Status Protocol (OSC 7501). A supporting terminal +// replies with a [ProgramStatusSupportMsg]. Terminals that don't support it +// never reply, so pick your own timeout. Use [WithInput] to enable input, +// since the reply cannot be read otherwise. +// +// Detection is optional: reports can be sent without it. +func RequestProgramStatusSupport() Msg { + return programStatusSupportMsg{} +} diff --git a/vendor/charm.land/bubbletea/v2/tea.go b/vendor/charm.land/bubbletea/v2/tea.go index f5dac71235..95ce4a8af0 100644 --- a/vendor/charm.land/bubbletea/v2/tea.go +++ b/vendor/charm.land/bubbletea/v2/tea.go @@ -146,6 +146,20 @@ type View struct { // progress bar section. Support depends on the terminal. ProgressBar *ProgressBar + // ProgramStatus when not nil, reports the program status to the terminal + // using the Program Status Protocol (OSC 7501): whether the program is + // idle, working, waiting on the user, done, or failed. Support depends on + // the terminal. + // + // The status usually addresses the root record (empty ID). Changing it + // sends a new report, and setting it back to nil clears the record. + // + // The status is not cleared when the program exits, so done and error + // reports stay visible to the user. Report [ProgramStateDone], + // [ProgramStateError], or [ProgramStateIdle] before quitting rather than + // leaving a working or blocked status behind. + ProgramStatus *ProgramStatus + // AltScreen puts the program in the alternate screen buffer // (i.e. the program goes into full window mode). Note that the altscreen will // be automatically exited when the program quits. @@ -674,11 +688,13 @@ func (p *Program) handleSignals() chan struct{} { case s := <-sig: if atomic.LoadUint32(&p.ignoreSignals) == 0 { - switch s { - case syscall.SIGINT: - p.msgs <- InterruptMsg{} - default: - p.msgs <- QuitMsg{} + var msg Msg = QuitMsg{} + if s == syscall.SIGINT { + msg = InterruptMsg{} + } + select { + case p.msgs <- msg: + case <-p.ctx.Done(): } return } @@ -816,35 +832,38 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) { } case readClipboardMsg: - p.execute(ansi.RequestSystemClipboard) + p.executeQuery(ansi.RequestSystemClipboard) case setClipboardMsg: p.execute(ansi.SetSystemClipboard(string(msg))) case readPrimaryClipboardMsg: - p.execute(ansi.RequestPrimaryClipboard) + p.executeQuery(ansi.RequestPrimaryClipboard) case setPrimaryClipboardMsg: p.execute(ansi.SetPrimaryClipboard(string(msg))) case backgroundColorMsg: - p.execute(ansi.RequestBackgroundColor) + p.executeQuery(ansi.RequestBackgroundColor) case foregroundColorMsg: - p.execute(ansi.RequestForegroundColor) + p.executeQuery(ansi.RequestForegroundColor) case cursorColorMsg: - p.execute(ansi.RequestCursorColor) + p.executeQuery(ansi.RequestCursorColor) + + case programStatusSupportMsg: + p.executeQuery(ansi.RequestProgramStatusSupport) case execMsg: // NB: this blocks. p.exec(msg.cmd, msg.fn) case terminalVersion: - p.execute(ansi.RequestNameVersion) + p.executeQuery(ansi.RequestNameVersion) case requestCapabilityMsg: - p.execute(ansi.RequestTermcap(string(msg))) + p.executeQuery(ansi.RequestTermcap(string(msg))) case BatchMsg: go p.execBatchMsg(msg) @@ -861,7 +880,7 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) { go p.checkResize() case requestCursorPosMsg: - p.execute(ansi.RequestCursorPositionReport) + p.executeQuery(ansi.RequestCursorPositionReport) case RawMsg: p.execute(fmt.Sprint(msg.Msg)) @@ -1055,7 +1074,7 @@ func (p *Program) Run() (returnModel Model, returnErr error) { return p.initialModel, fmt.Errorf("bubbletea: error getting terminal size: %w", err) } - width, height = w, h + width, height = p.fallbackDimensions(w, h) } p.width, p.height = width, height @@ -1119,7 +1138,7 @@ func (p *Program) Run() (returnModel Model, returnErr error) { // Query for synchronized updates support (mode 2026) and unicode core // (mode 2027). If the terminal supports it, the renderer will enable // it once we get the response. - p.execute(ansi.RequestModeSynchronizedOutput + + p.executeQuery(ansi.RequestModeSynchronizedOutput + ansi.RequestModeUnicodeCore) } @@ -1226,6 +1245,22 @@ func (p *Program) execute(seq string) { p.mu.Unlock() } +// executeQuery writes a terminal query to the program output. Queries expect a +// reply on p.input, so when input is disabled the query is skipped: the +// program cannot read the response, and the terminal's reply would leak into +// the shell once the program exits (#1590). +// +// This covers every query that expects a reply, not just the startup +// capability probe: background, foreground, and cursor color requests, cursor +// position reports, clipboard reads, and terminal version and termcap +// queries. +func (p *Program) executeQuery(seq string) { + if p.disableInput { + return + } + p.execute(seq) +} + // flush flushes the output buffer to the program output. func (p *Program) flush() error { p.mu.Lock() diff --git a/vendor/charm.land/bubbletea/v2/termcap.go b/vendor/charm.land/bubbletea/v2/termcap.go index c66d95ae5e..b2a6e2567d 100644 --- a/vendor/charm.land/bubbletea/v2/termcap.go +++ b/vendor/charm.land/bubbletea/v2/termcap.go @@ -5,7 +5,8 @@ package tea type requestCapabilityMsg string // RequestCapability is a command that requests the terminal to send its -// Termcap/Terminfo response for the given capability. +// Termcap/Terminfo response for the given capability. Use [WithInput] to +// enable input, since the reply cannot be read otherwise. // // Bubble Tea recognizes the following capabilities and will use them to // upgrade the program's color profile: diff --git a/vendor/charm.land/bubbletea/v2/tty.go b/vendor/charm.land/bubbletea/v2/tty.go index 12b86493b3..e4aaba1309 100644 --- a/vendor/charm.land/bubbletea/v2/tty.go +++ b/vendor/charm.land/bubbletea/v2/tty.go @@ -3,6 +3,7 @@ package tea import ( "fmt" "os" + "strconv" "time" uv "github.com/charmbracelet/ultraviolet" @@ -104,6 +105,43 @@ func (p *Program) waitForReadLoop() { } } +const ( + defaultWidth = 80 + defaultHeight = 24 +) + +// fallbackDimensions returns width and height, falling back to COLUMNS/LINES +// environment variables or 80x24 defaults when dimensions are non-positive. +func (p *Program) fallbackDimensions(width, height int) (int, int) { + if width <= 0 { + var cols string + if p != nil && p.environ != nil { + cols = p.environ.Getenv("COLUMNS") + } else { + cols = os.Getenv("COLUMNS") + } + if c, err := strconv.Atoi(cols); err == nil && c > 0 { + width = c + } else { + width = defaultWidth + } + } + if height <= 0 { + var lines string + if p != nil && p.environ != nil { + lines = p.environ.Getenv("LINES") + } else { + lines = os.Getenv("LINES") + } + if l, err := strconv.Atoi(lines); err == nil && l > 0 { + height = l + } else { + height = defaultHeight + } + } + return width, height +} + // checkResize detects the current size of the output and informs the program // via a WindowSizeMsg. func (p *Program) checkResize() { @@ -122,6 +160,8 @@ func (p *Program) checkResize() { return } + w, h = p.fallbackDimensions(w, h) + p.width, p.height = w, h p.Send(WindowSizeMsg{Width: w, Height: h}) } diff --git a/vendor/charm.land/bubbletea/v2/xterm.go b/vendor/charm.land/bubbletea/v2/xterm.go index 0ffcd08fe4..edc6e990d7 100644 --- a/vendor/charm.land/bubbletea/v2/xterm.go +++ b/vendor/charm.land/bubbletea/v2/xterm.go @@ -16,7 +16,8 @@ type terminalVersion struct{} // RequestTerminalVersion is a command that queries the terminal for its // version using XTVERSION. Note that some terminals may not support this -// command. +// command. Use [WithInput] to enable input, since the reply cannot be read +// otherwise. func RequestTerminalVersion() Msg { return terminalVersion{} } diff --git a/vendor/charm.land/log/v2/.golangci.yml b/vendor/charm.land/log/v2/.golangci.yml index 2654e327e6..c90f031611 100644 --- a/vendor/charm.land/log/v2/.golangci.yml +++ b/vendor/charm.land/log/v2/.golangci.yml @@ -30,9 +30,6 @@ linters: - text: '(slog|log)\.\w+' linters: - noctx - - text: "var-naming" - linters: - - revive generated: lax presets: - common-false-positives diff --git a/vendor/charm.land/log/v2/README.md b/vendor/charm.land/log/v2/README.md index 4558f76995..c5f4ad4608 100644 --- a/vendor/charm.land/log/v2/README.md +++ b/vendor/charm.land/log/v2/README.md @@ -42,13 +42,13 @@ readable logging with batteries included. Use `go get` to download the dependency. ```bash -go get github.com/charmbracelet/log@latest +go get charm.land/log/v2@latest ``` Then, `import` it in your Go files: ```go -import "github.com/charmbracelet/log" +import "charm.land/log/v2" ``` The Charm logger comes with a global package-wise logger with timestamps turned diff --git a/vendor/charm.land/log/v2/logger_no121.go b/vendor/charm.land/log/v2/logger_no121.go index 001798a897..047d4a8711 100644 --- a/vendor/charm.land/log/v2/logger_no121.go +++ b/vendor/charm.land/log/v2/logger_no121.go @@ -10,7 +10,7 @@ import ( "golang.org/x/exp/slog" ) -// type alises for slog. +// type aliases for slog. type ( slogAttr = slog.Attr slogValue = slog.Value diff --git a/vendor/charm.land/log/v2/styles.go b/vendor/charm.land/log/v2/styles.go index 72085c921f..b892754850 100644 --- a/vendor/charm.land/log/v2/styles.go +++ b/vendor/charm.land/log/v2/styles.go @@ -54,27 +54,27 @@ func DefaultStyles() *Styles { DebugLevel: lipgloss.NewStyle(). SetString(strings.ToUpper(DebugLevel.String())). Bold(true). - MaxWidth(4). + Width(5). Foreground(lipgloss.Color("63")), InfoLevel: lipgloss.NewStyle(). SetString(strings.ToUpper(InfoLevel.String())). Bold(true). - MaxWidth(4). + Width(5). Foreground(lipgloss.Color("86")), WarnLevel: lipgloss.NewStyle(). SetString(strings.ToUpper(WarnLevel.String())). Bold(true). - MaxWidth(4). + Width(5). Foreground(lipgloss.Color("192")), ErrorLevel: lipgloss.NewStyle(). SetString(strings.ToUpper(ErrorLevel.String())). Bold(true). - MaxWidth(4). + Width(5). Foreground(lipgloss.Color("204")), FatalLevel: lipgloss.NewStyle(). SetString(strings.ToUpper(FatalLevel.String())). Bold(true). - MaxWidth(4). + Width(5). Foreground(lipgloss.Color("134")), }, Keys: map[string]lipgloss.Style{}, diff --git a/vendor/charm.land/log/v2/text.go b/vendor/charm.land/log/v2/text.go index d1e3dc8a30..9187c4c911 100644 --- a/vendor/charm.land/log/v2/text.go +++ b/vendor/charm.land/log/v2/text.go @@ -102,8 +102,8 @@ func escapeStringForOutput(str string, escapeQuotes bool) string { switch { case r < ' ': bb.WriteString(`\x`) - bb.WriteByte(lowerhex[byte(r)>>4]) - bb.WriteByte(lowerhex[byte(r)&0xF]) + bb.WriteByte(lowerhex[r>>4&0xF]) + bb.WriteByte(lowerhex[r&0xF]) case !utf8.ValidRune(r): r = 0xFFFD fallthrough diff --git a/vendor/github.com/a-h/templ/.goreleaser.yaml b/vendor/github.com/a-h/templ/.goreleaser.yaml index 456187c226..f4264b4f9f 100644 --- a/vendor/github.com/a-h/templ/.goreleaser.yaml +++ b/vendor/github.com/a-h/templ/.goreleaser.yaml @@ -1,3 +1,5 @@ +version: 2 + builds: - env: - CGO_ENABLED=0 @@ -21,17 +23,17 @@ signs: stdin: '{{ .Env.COSIGN_PASSWORD }}' output: true artifacts: checksum + signature: '${artifact}.sigstore.json' args: - sign-blob - --yes - --key - env://COSIGN_PRIVATE_KEY - - '--output-certificate=${certificate}' - - '--output-signature=${signature}' + - '--bundle=${signature}' - '${artifact}' archives: - - format: tar.gz + - formats: [tar.gz] name_template: >- {{ .ProjectName }}_ {{- title .Os }}_ @@ -40,29 +42,8 @@ archives: {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end }} -kos: - - repository: ghcr.io/a-h/templ - platforms: - - linux/amd64 - - linux/arm64 - tags: - - latest - - '{{.Tag}}' - bare: true - -docker_signs: - - cmd: cosign - artifacts: all - output: true - args: - - sign - - --yes - - --key - - env://COSIGN_PRIVATE_KEY - - '${artifact}' - snapshot: - name_template: "{{ incpatch .Version }}-next" + version_template: "{{ incpatch .Version }}-next" changelog: sort: asc diff --git a/vendor/github.com/a-h/templ/.templignore_fmt b/vendor/github.com/a-h/templ/.templignore_fmt new file mode 100644 index 0000000000..f9f96f1b1f --- /dev/null +++ b/vendor/github.com/a-h/templ/.templignore_fmt @@ -0,0 +1,2 @@ +# Do not format generator test fixtures. +generator/test-* diff --git a/vendor/github.com/a-h/templ/.version b/vendor/github.com/a-h/templ/.version index 8560c05152..bb7bd0440e 100644 --- a/vendor/github.com/a-h/templ/.version +++ b/vendor/github.com/a-h/templ/.version @@ -1 +1 @@ -0.3.1020 \ No newline at end of file +0.3.1070 \ No newline at end of file diff --git a/vendor/github.com/a-h/templ/.version-template b/vendor/github.com/a-h/templ/.version-template new file mode 100644 index 0000000000..a995963f03 --- /dev/null +++ b/vendor/github.com/a-h/templ/.version-template @@ -0,0 +1 @@ +0.3.%d diff --git a/vendor/github.com/a-h/templ/README.md b/vendor/github.com/a-h/templ/README.md index 9bb25e612d..1d45b3ca5d 100644 --- a/vendor/github.com/a-h/templ/README.md +++ b/vendor/github.com/a-h/templ/README.md @@ -23,7 +23,7 @@ See user documentation at https://templ.guide Set the version of templ to the current version. ```sh -version set --template="0.3.%d" +version set ``` ### build @@ -31,7 +31,7 @@ version set --template="0.3.%d" Build a local version. ```sh -version set --template="0.3.%d" +version set cd cmd/templ go build ``` @@ -47,7 +47,7 @@ rm -f ~/bin/templ # Clear LSP logs. rm -f cmd/templ/lspcmd/*.txt # Update version. -version set --template="0.3.%d" +version set # Install to $GOPATH/bin or $HOME/go/bin cd cmd/templ && go install ``` @@ -73,7 +73,7 @@ go run ./cmd/templ generate -include-version=false Run Go tests. ```sh -version set --template="0.3.%d" +version set go run ./cmd/templ generate -include-version=false go test ./... ``` @@ -83,7 +83,7 @@ go test ./... Run Go tests. ```sh -version set --template="0.3.%d" +version set go run ./cmd/templ generate -include-version=false go test ./... -short ``` @@ -169,7 +169,7 @@ git diff --exit-code Push a semantic version number to GitHub to trigger the release process. ```sh -version push --template="0.3.%d" --prefix="v" +version push --prefix="v" ``` ### docs-run diff --git a/vendor/github.com/a-h/templ/cmd/templ/fmtcmd/main.go b/vendor/github.com/a-h/templ/cmd/templ/fmtcmd/main.go index 0fa5c19dc3..cb1e6af0cb 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/fmtcmd/main.go +++ b/vendor/github.com/a-h/templ/cmd/templ/fmtcmd/main.go @@ -12,6 +12,7 @@ import ( "github.com/a-h/templ/cmd/templ/processor" "github.com/a-h/templ/internal/format" + "github.com/a-h/templ/internal/ignorefile" "github.com/natefinch/atomic" ) @@ -70,7 +71,11 @@ func Run(log *slog.Logger, stdin io.Reader, stdout io.Writer, args Arguments) (e return nil, true } dir := args.Files[0] - return NewFormatter(log, dir, process, args.WorkerCount, args.FailIfChanged).Run() + shouldSkip, err := ignorefile.ShouldSkipFunc(dir, ".templignore_fmt") + if err != nil { + return fmt.Errorf("failed to parse .templignore_fmt: %w", err) + } + return NewFormatter(log, dir, process, args.WorkerCount, args.FailIfChanged, shouldSkip).Run() } type Formatter struct { @@ -79,15 +84,17 @@ type Formatter struct { Process func(fileName string) (error, bool) WorkerCount int FailIfChange bool + ShouldSkip func(string) bool } -func NewFormatter(log *slog.Logger, dir string, process func(fileName string) (error, bool), workerCount int, failIfChange bool) *Formatter { +func NewFormatter(log *slog.Logger, dir string, process func(fileName string) (error, bool), workerCount int, failIfChange bool, shouldSkip func(string) bool) *Formatter { f := &Formatter{ Log: log, Dir: dir, Process: process, WorkerCount: workerCount, FailIfChange: failIfChange, + ShouldSkip: shouldSkip, } if f.WorkerCount == 0 { f.WorkerCount = runtime.NumCPU() @@ -101,7 +108,7 @@ func (f *Formatter) Run() (err error) { start := time.Now() results := make(chan processor.Result) f.Log.Debug("Walking directory", slog.String("path", f.Dir)) - go processor.Process(f.Dir, f.Process, f.WorkerCount, results) + go processor.Process(f.Dir, f.Process, f.WorkerCount, f.ShouldSkip, results) var successCount, errorCount int for r := range results { if r.ChangesMade { diff --git a/vendor/github.com/a-h/templ/cmd/templ/generatecmd/cmd.go b/vendor/github.com/a-h/templ/cmd/templ/generatecmd/cmd.go index 58d897f856..8758bc5f56 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/generatecmd/cmd.go +++ b/vendor/github.com/a-h/templ/cmd/templ/generatecmd/cmd.go @@ -28,6 +28,7 @@ import ( "github.com/a-h/templ/cmd/templ/generatecmd/run" "github.com/a-h/templ/cmd/templ/generatecmd/watcher" "github.com/a-h/templ/generator" + "github.com/a-h/templ/internal/ignorefile" "github.com/a-h/templ/internal/skipdir" templruntime "github.com/a-h/templ/runtime" ) @@ -41,8 +42,9 @@ func NewGenerate(log *slog.Logger, args Arguments) (g *Generate, err error) { } type Generate struct { - Log *slog.Logger - Args Arguments + Log *slog.Logger + Args Arguments + ShouldSkip func(string) bool } type GenerationEvent struct { @@ -71,6 +73,12 @@ func (cmd Generate) Run(ctx context.Context) (err error) { } } + // Load ignore patterns. + cmd.ShouldSkip, err = ignorefile.ShouldSkipFunc(cmd.Args.Path, ".templignore_generate") + if err != nil { + return fmt.Errorf("failed to parse .templignore_generate: %w", err) + } + // Configure generator. var opts []generator.GenerateOpt if cmd.Args.IncludeVersion { @@ -317,7 +325,7 @@ func (cmd Generate) handleEvents(ctx context.Context, events chan fsnotify.Event func (cmd *Generate) walkAndWatch(ctx context.Context, events chan fsnotify.Event, errs chan error) { cmd.Log.Debug("Walking directory", slog.String("path", cmd.Args.Path), slog.Bool("devMode", cmd.Args.Watch)) - if err := watcher.WalkFiles(ctx, cmd.Args.Path, cmd.Args.WatchPattern, cmd.Args.IgnorePattern, events); err != nil { + if err := watcher.WalkFiles(ctx, cmd.Args.Path, cmd.Args.WatchPattern, cmd.Args.IgnorePattern, cmd.ShouldSkip, events); err != nil { cmd.Log.Error("WalkFiles failed, exiting", slog.Any("error", err)) errs <- FatalError{Err: fmt.Errorf("failed to walk files: %w", err)} return @@ -327,7 +335,7 @@ func (cmd *Generate) walkAndWatch(ctx context.Context, events chan fsnotify.Even return } cmd.Log.Info("Watching files") - rw, err := watcher.Recursive(ctx, cmd.Args.WatchPattern, cmd.Args.IgnorePattern, events, errs) + rw, err := watcher.Recursive(ctx, cmd.Args.WatchPattern, cmd.Args.IgnorePattern, cmd.ShouldSkip, events, errs) if err != nil { cmd.Log.Error("Recursive watcher setup failed, exiting", slog.Any("error", err)) errs <- FatalError{Err: fmt.Errorf("failed to setup recursive watcher: %w", err)} @@ -359,6 +367,12 @@ func (cmd *Generate) deleteWatchModeTextFiles() error { if info.IsDir() && skipdir.ShouldSkip(absPath) { return filepath.SkipDir } + if cmd.ShouldSkip != nil && cmd.ShouldSkip(path) { + if info.IsDir() { + return filepath.SkipDir + } + return nil + } if !strings.HasSuffix(absPath, "_templ.go") && !strings.HasSuffix(absPath, ".templ") { return nil } diff --git a/vendor/github.com/a-h/templ/cmd/templ/generatecmd/main.go b/vendor/github.com/a-h/templ/cmd/templ/generatecmd/main.go index bd11d81bfd..5499e61a33 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/generatecmd/main.go +++ b/vendor/github.com/a-h/templ/cmd/templ/generatecmd/main.go @@ -39,14 +39,21 @@ Args: Set the regexp pattern of files that will be watched for changes. (default: '(.+\.go$)|(.+\.templ$)|(.+_templ\.txt$)') -ignore-pattern Set the regexp pattern of files to ignore when watching for changes. (default: '') + -open-browser + Set to false to prevent the browser from opening when using the -proxy flag. (default true) -cmd - Set the command to run after generating code. + Set the command to run after generating code. The command is executed via + the system shell ($SHELL on Unix, %COMSPEC% on Windows). -proxy Set the URL to proxy after generating code and executing the command. -proxyport The port the proxy will listen on. (default 7331) -proxybind The address the proxy will listen on. (default 127.0.0.1) + -proxy-tls-crt + Path to a TLS certificate file to serve the proxy over HTTPS. Must be used with -proxy-tls-key and -proxy. + -proxy-tls-key + Path to a TLS key file to serve the proxy over HTTPS. Must be used with -proxy-tls-crt and -proxy. -notify-proxy If present, the command will issue a reload event to the proxy 127.0.0.1:7331, or use proxyport and proxybind to specify a different address. -w diff --git a/vendor/github.com/a-h/templ/cmd/templ/generatecmd/run/run_unix.go b/vendor/github.com/a-h/templ/cmd/templ/generatecmd/run/run_unix.go index a19484aa73..c6e0ccfc1d 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/generatecmd/run/run_unix.go +++ b/vendor/github.com/a-h/templ/cmd/templ/generatecmd/run/run_unix.go @@ -8,7 +8,6 @@ import ( "fmt" "os" "os/exec" - "strings" "sync" "syscall" "time" @@ -33,11 +32,12 @@ func KillAll() (err error) { } func kill(cmd *exec.Cmd) (err error) { + pgid := -cmd.Process.Pid errs := make([]error, 4) - errs[0] = ignoreExited(cmd.Process.Signal(syscall.SIGINT)) - errs[1] = ignoreExited(cmd.Process.Signal(syscall.SIGTERM)) + errs[0] = ignoreExited(syscall.Kill(pgid, syscall.SIGINT)) + errs[1] = ignoreExited(syscall.Kill(pgid, syscall.SIGTERM)) errs[2] = ignoreExited(cmd.Wait()) - errs[3] = ignoreExited(syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)) + errs[3] = ignoreExited(syscall.Kill(pgid, syscall.SIGKILL)) return errors.Join(errs...) } @@ -45,7 +45,9 @@ func ignoreExited(err error) error { if errors.Is(err, syscall.ESRCH) { return nil } - // Ignore *exec.ExitError + if errors.Is(err, syscall.EPERM) { + return nil + } if _, ok := err.(*exec.ExitError); ok { return nil } @@ -63,14 +65,11 @@ func Run(ctx context.Context, workingDir string, input string) (cmd *exec.Cmd, e delete(running, input) } - parts := strings.Fields(input) - executable := parts[0] - args := []string{} - if len(parts) > 1 { - args = append(args, parts[1:]...) + shell := os.Getenv("SHELL") + if shell == "" { + shell = "/bin/sh" } - - cmd = exec.CommandContext(ctx, executable, args...) + cmd = exec.CommandContext(ctx, shell, "-c", input) // Wait for the process to finish gracefully before termination. cmd.WaitDelay = time.Second * 3 cmd.Env = os.Environ() diff --git a/vendor/github.com/a-h/templ/cmd/templ/generatecmd/run/run_windows.go b/vendor/github.com/a-h/templ/cmd/templ/generatecmd/run/run_windows.go index 9a4823dd01..f580c7654f 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/generatecmd/run/run_windows.go +++ b/vendor/github.com/a-h/templ/cmd/templ/generatecmd/run/run_windows.go @@ -7,7 +7,6 @@ import ( "os" "os/exec" "strconv" - "strings" "sync" ) @@ -53,14 +52,11 @@ func Run(ctx context.Context, workingDir string, input string) (cmd *exec.Cmd, e } delete(running, input) } - parts := strings.Fields(input) - executable := parts[0] - args := []string{} - if len(parts) > 1 { - args = append(args, parts[1:]...) + shell := os.Getenv("COMSPEC") + if shell == "" { + shell = "cmd.exe" } - - cmd = exec.Command(executable, args...) + cmd = exec.Command(shell, "/C", input) cmd.Env = os.Environ() cmd.Dir = workingDir cmd.Stdin = os.Stdin diff --git a/vendor/github.com/a-h/templ/cmd/templ/generatecmd/watcher/watch.go b/vendor/github.com/a-h/templ/cmd/templ/generatecmd/watcher/watch.go index 4e10775aa9..760f585f16 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/generatecmd/watcher/watch.go +++ b/vendor/github.com/a-h/templ/cmd/templ/generatecmd/watcher/watch.go @@ -17,6 +17,7 @@ func Recursive( ctx context.Context, watchPattern *regexp.Regexp, ignorePattern *regexp.Regexp, + shouldSkip func(string) bool, out chan fsnotify.Event, errors chan error, ) (w *RecursiveWatcher, err error) { @@ -29,6 +30,7 @@ func Recursive( w: fsnw, WatchPattern: watchPattern, IgnorePattern: ignorePattern, + ShouldSkip: shouldSkip, Events: out, Errors: errors, timers: make(map[timerKey]*time.Timer), @@ -44,7 +46,7 @@ func Recursive( // WalkFiles walks the file tree rooted at path, sending a Create event for each // file it encounters. -func WalkFiles(ctx context.Context, rootPath string, watchPattern, ignorePattern *regexp.Regexp, out chan fsnotify.Event) (err error) { +func WalkFiles(ctx context.Context, rootPath string, watchPattern, ignorePattern *regexp.Regexp, shouldSkip func(string) bool, out chan fsnotify.Event) (err error) { return fs.WalkDir(os.DirFS(rootPath), ".", func(path string, info os.DirEntry, err error) error { if err != nil { return nil @@ -56,6 +58,12 @@ func WalkFiles(ctx context.Context, rootPath string, watchPattern, ignorePattern if info.IsDir() && skipdir.ShouldSkip(absPath) { return filepath.SkipDir } + if shouldSkip != nil && shouldSkip(path) { + if info.IsDir() { + return filepath.SkipDir + } + return nil + } if !watchPattern.MatchString(absPath) { return nil } @@ -75,6 +83,7 @@ type RecursiveWatcher struct { w *fsnotify.Watcher WatchPattern *regexp.Regexp IgnorePattern *regexp.Regexp + ShouldSkip func(string) bool Events chan fsnotify.Event Errors chan error timerMu sync.Mutex @@ -151,16 +160,16 @@ func (w *RecursiveWatcher) loop() { } func (w *RecursiveWatcher) Add(dir string) error { - return filepath.WalkDir(dir, func(dir string, info os.DirEntry, err error) error { + return filepath.WalkDir(dir, func(currentPath string, info os.DirEntry, err error) error { if err != nil { return nil } if !info.IsDir() { return nil } - if skipdir.ShouldSkip(dir) { + if skipdir.ShouldSkip(currentPath) { return filepath.SkipDir } - return w.w.Add(dir) + return w.w.Add(currentPath) }) } diff --git a/vendor/github.com/a-h/templ/cmd/templ/infocmd/main.go b/vendor/github.com/a-h/templ/cmd/templ/infocmd/main.go index b21ea981b7..b340b96f91 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/infocmd/main.go +++ b/vendor/github.com/a-h/templ/cmd/templ/infocmd/main.go @@ -68,13 +68,11 @@ func getGoplsInfo() (d ToolInfo) { d.Message = fmt.Sprintf("failed to find gopls: %v", err) return } - cmd := exec.Command(d.Location, "version") - v, err := cmd.Output() + d.Version, err = pls.GoplsVersion(d.Location) if err != nil { d.Message = fmt.Sprintf("failed to get gopls version: %v", err) return } - d.Version = strings.TrimSpace(string(v)) d.Level = slog.LevelInfo return } @@ -126,9 +124,14 @@ func getPrettierInfo() (d ToolInfo) { d.Level = slog.LevelWarn var err error - d.Location, err = exec.LookPath("prettier") + for _, name := range []string{"prettier", "prettierd"} { + d.Location, err = exec.LookPath(name) + if err == nil { + break + } + } if err != nil { - d.Message = fmt.Sprintf("failed to find prettier: %v", err) + d.Message = fmt.Sprintf("failed to find prettier or prettierd: %v", err) return } cmd := exec.Command(d.Location, "--version") @@ -147,23 +150,18 @@ func getInfo() (d Info) { d.OS.GOARCH = runtime.GOARCH var wg sync.WaitGroup - wg.Add(4) - go func() { - defer wg.Done() + wg.Go(func() { d.Go = getGoInfo() - }() - go func() { - defer wg.Done() + }) + wg.Go(func() { d.Gopls = getGoplsInfo() - }() - go func() { - defer wg.Done() + }) + wg.Go(func() { d.Templ = getTemplInfo() - }() - go func() { - defer wg.Done() + }) + wg.Go(func() { d.Prettier = getPrettierInfo() - }() + }) wg.Wait() return } diff --git a/vendor/github.com/a-h/templ/cmd/templ/lspcmd/main.go b/vendor/github.com/a-h/templ/cmd/templ/lspcmd/main.go index dbe522fa21..956a632b2d 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/lspcmd/main.go +++ b/vendor/github.com/a-h/templ/cmd/templ/lspcmd/main.go @@ -12,6 +12,7 @@ import ( "github.com/a-h/templ/cmd/templ/lspcmd/httpdebug" "github.com/a-h/templ/cmd/templ/lspcmd/pls" "github.com/a-h/templ/cmd/templ/lspcmd/proxy" + "github.com/a-h/templ/internal/format" "github.com/a-h/templ/lsp/jsonrpc2" "github.com/a-h/templ/lsp/protocol" @@ -28,7 +29,8 @@ type Arguments struct { // HTTPDebug sets the HTTP endpoint to listen on. Leave empty for no web debug. HTTPDebug string // NoPreload disables preloading of templ files on server startup (useful for large monorepos) - NoPreload bool + NoPreload bool + FormatConfig format.Config } func Run(stdin io.Reader, stdout, stderr io.Writer, args Arguments) (err error) { @@ -81,7 +83,7 @@ func run(ctx context.Context, log *slog.Logger, templStream jsonrpc2.Stream, arg }() log.Info("lsp: starting gopls...") - rwc, err := pls.NewGopls(ctx, log, pls.Options{ + goplsLocation, rwc, err := pls.NewGopls(ctx, log, pls.Options{ Log: args.GoplsLog, RPCTrace: args.GoplsRPCTrace, Remote: args.GoplsRemote, @@ -90,6 +92,14 @@ func run(ctx context.Context, log *slog.Logger, templStream jsonrpc2.Stream, arg log.Error("failed to start gopls", slog.Any("error", err)) os.Exit(1) } + log.Info("found gopls", slog.String("location", goplsLocation)) + + goplsVersion, err := pls.GoplsVersion(goplsLocation) + if err != nil { + log.Warn("could not determine gopls version", slog.Any("error", err)) + } else { + log.Info("gopls version", slog.String("version", goplsVersion)) + } cache := proxy.NewSourceMapCache() diagnosticCache := proxy.NewDiagnosticCache() @@ -105,7 +115,9 @@ func run(ctx context.Context, log *slog.Logger, templStream jsonrpc2.Stream, arg log.Info("creating proxy") // Create the proxy to sit between. - serverProxy := proxy.NewServer(log, goplsServer, cache, diagnosticCache, args.NoPreload) + serverProxy := proxy.NewServer(log, goplsServer, cache, diagnosticCache, args.NoPreload, args.FormatConfig) + serverProxy.GoplsPath = goplsLocation + serverProxy.GoplsVersion = goplsVersion // Create templ server. log.Info("creating templ server") diff --git a/vendor/github.com/a-h/templ/cmd/templ/lspcmd/pls/main.go b/vendor/github.com/a-h/templ/cmd/templ/lspcmd/pls/main.go index f5b4df521c..8292fae314 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/lspcmd/pls/main.go +++ b/vendor/github.com/a-h/templ/cmd/templ/lspcmd/pls/main.go @@ -10,6 +10,7 @@ import ( "os/exec" "path" "runtime" + "strings" ) // Options for the gopls client. @@ -72,14 +73,31 @@ func FindGopls() (location string, err error) { return "", fmt.Errorf("cannot find gopls on the path (%q), in $HOME/go/bin or $HOME/.local/bin/gopls. You can install gopls with `go install golang.org/x/tools/gopls@latest`", os.Getenv("PATH")) } +// GoplsVersion runs "gopls version" and returns the version string (e.g. "v0.21.1"). +func GoplsVersion(location string) (version string, err error) { + out, err := exec.Command(location, "version").Output() + if err != nil { + return "", fmt.Errorf("failed to run gopls version: %w", err) + } + // Output is like: "golang.org/x/tools/gopls v0.21.1" + for _, line := range strings.Split(string(out), "\n") { + _, v, ok := strings.Cut(line, "golang.org/x/tools/gopls ") + if ok { + return strings.TrimSpace(v), nil + } + } + return "", fmt.Errorf("could not parse gopls version from output: %s", string(out)) +} + // NewGopls starts gopls and opens up a jsonrpc2 connection to it. -func NewGopls(ctx context.Context, log *slog.Logger, opts Options) (rwc io.ReadWriteCloser, err error) { - location, err := FindGopls() +func NewGopls(ctx context.Context, log *slog.Logger, opts Options) (location string, rwc io.ReadWriteCloser, err error) { + location, err = FindGopls() if err != nil { - return nil, err + return "", nil, err } cmd := exec.Command(location, opts.AsArguments()...) - return newProcessReadWriteCloser(log, cmd) + rwc, err = newProcessReadWriteCloser(log, cmd) + return location, rwc, err } // newProcessReadWriteCloser creates a processReadWriteCloser to allow stdin/stdout to be used as diff --git a/vendor/github.com/a-h/templ/cmd/templ/lspcmd/proxy/client.go b/vendor/github.com/a-h/templ/cmd/templ/lspcmd/proxy/client.go index cf1248b0e5..2a5aee81a7 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/lspcmd/proxy/client.go +++ b/vendor/github.com/a-h/templ/cmd/templ/lspcmd/proxy/client.go @@ -59,14 +59,19 @@ func (p Client) PublishDiagnostics(ctx context.Context, params *lsp.PublishDiagn for i, diagnostic := range params.Diagnostics { p.Log.Info(fmt.Sprintf("client <- server: PublishDiagnostics: [%d]", i), slog.Any("diagnostic", diagnostic)) } + // Only convert diagnostics for _templ.go files. Diagnostics for regular + // .go files are dropped because the Go extension publishes those directly. + isTemplGoFile, templURI := convertTemplGoToTemplURI(params.URI) + if !isTemplGoFile { + return nil + } // Get the sourcemap from the cache. - uri := strings.TrimSuffix(string(params.URI), "_templ.go") + ".templ" - sourceMap, ok := p.SourceMapCache.Get(uri) + sourceMap, ok := p.SourceMapCache.Get(string(templURI)) if !ok { - p.Log.Error("unable to complete because the sourcemap for the URI doesn't exist in the cache", slog.String("uri", uri)) - return fmt.Errorf("unable to complete because the sourcemap for %q doesn't exist in the cache, has the didOpen notification been sent yet?", uri) + p.Log.Error("unable to complete because the sourcemap for the URI doesn't exist in the cache", slog.String("uri", string(templURI))) + return fmt.Errorf("unable to complete because the sourcemap for %q doesn't exist in the cache, has the didOpen notification been sent yet?", templURI) } - params.URI = lsp.DocumentURI(uri) + params.URI = templURI // Rewrite the positions. for i, item := range params.Diagnostics { start, ok := sourceMap.SourcePositionFromTarget(item.Range.Start.Line, item.Range.Start.Character) @@ -94,9 +99,8 @@ func (p Client) PublishDiagnostics(ctx context.Context, params *lsp.PublishDiagn params.Diagnostics[i] = item p.Log.Info(fmt.Sprintf("diagnostic [%d] rewritten", i), slog.Any("diagnostic", item)) } - params.Diagnostics = p.DiagnosticCache.AddTemplDiagnostics(uri, params.Diagnostics) - err = p.Target.PublishDiagnostics(ctx, params) - return err + params.Diagnostics = p.DiagnosticCache.AddTemplDiagnostics(string(templURI), params.Diagnostics) + return p.Target.PublishDiagnostics(ctx, params) } func (p Client) ShowMessage(ctx context.Context, params *lsp.ShowMessageParams) (err error) { @@ -132,6 +136,7 @@ func (p Client) UnregisterCapability(ctx context.Context, params *lsp.Unregistra func (p Client) ApplyEdit(ctx context.Context, params *lsp.ApplyWorkspaceEditParams) (result *lsp.ApplyWorkspaceEditResponse, err error) { p.Log.Info("client <- server: ApplyEdit") + convertWorkspaceEdit(p.SourceMapCache, p.Log, ¶ms.Edit) return p.Target.ApplyEdit(ctx, params) } @@ -144,3 +149,17 @@ func (p Client) WorkspaceFolders(ctx context.Context) (result []lsp.WorkspaceFol p.Log.Info("client <- server: WorkspaceFolders") return p.Target.WorkspaceFolders(ctx) } + +func (p Client) ShowDocument(ctx context.Context, params *lsp.ShowDocumentParams) (result *lsp.ShowDocumentResult, err error) { + p.Log.Info("client <- server: ShowDocument", slog.String("uri", string(params.URI))) + isTemplGoFile, templURI := convertTemplGoToTemplURI(params.URI) + if !isTemplGoFile { + return p.Target.ShowDocument(ctx, params) + } + params.URI = templURI + if params.Selection != nil { + r := convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, *params.Selection) + params.Selection = &r + } + return p.Target.ShowDocument(ctx, params) +} diff --git a/vendor/github.com/a-h/templ/cmd/templ/lspcmd/proxy/rewrite.go b/vendor/github.com/a-h/templ/cmd/templ/lspcmd/proxy/rewrite.go index 3afb472df5..32ffaa6bc5 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/lspcmd/proxy/rewrite.go +++ b/vendor/github.com/a-h/templ/cmd/templ/lspcmd/proxy/rewrite.go @@ -1,6 +1,7 @@ package proxy import ( + "log/slog" "path" "strings" @@ -15,6 +16,12 @@ func convertTemplToGoURI(templURI lsp.DocumentURI) (isTemplFile bool, goURI lsp. return true, lsp.DocumentURI(base + (strings.TrimSuffix(fileName, ".templ") + "_templ.go")) } +// isPlainGoFile returns true if the URI refers to a .go file that is not a _templ.go file. +func isPlainGoFile(docURI lsp.DocumentURI) bool { + s := string(docURI) + return strings.HasSuffix(s, ".go") && !strings.HasSuffix(s, "_templ.go") +} + func convertTemplGoToTemplURI(goURI lsp.DocumentURI) (isTemplGoFile bool, templURI lsp.DocumentURI) { base, fileName := path.Split(string(goURI)) if !strings.HasSuffix(fileName, "_templ.go") { @@ -22,3 +29,96 @@ func convertTemplGoToTemplURI(goURI lsp.DocumentURI) (isTemplGoFile bool, templU } return true, lsp.DocumentURI(base + (strings.TrimSuffix(fileName, "_templ.go") + ".templ")) } + +// convertGoRangeToTemplRange converts a Go range to a templ range using the source map cache. +func convertGoRangeToTemplRange(cache *SourceMapCache, log *slog.Logger, templURI lsp.DocumentURI, input lsp.Range) (output lsp.Range) { + output = input + sourceMap, ok := cache.Get(string(templURI)) + if !ok { + log.Warn("go->templ: sourcemap not found in cache", + slog.String("uri", string(templURI)), + slog.Any("cachedURIs", cache.URIs())) + return + } + start, startMapped := sourceMap.SourcePositionFromTarget(input.Start.Line, input.Start.Character) + if startMapped { + output.Start.Line = start.Line + output.Start.Character = start.Col + } + end, endMapped := sourceMap.SourcePositionFromTarget(input.End.Line, input.End.Character) + if endMapped { + output.End.Line = end.Line + output.End.Character = end.Col + } + if !startMapped || !endMapped { + log.Warn("go->templ: range not found in sourcemap", + slog.String("uri", string(templURI)), + slog.Any("range", input), + slog.Bool("startMapped", startMapped), + slog.Bool("endMapped", endMapped)) + } + return +} + +// convertLocationResults converts _templ.go URIs and ranges in location results back to .templ URIs. +func convertLocationResults(cache *SourceMapCache, log *slog.Logger, result []lsp.Location) { + for i, r := range result { + isTemplGoFile, templURI := convertTemplGoToTemplURI(r.URI) + if !isTemplGoFile { + continue + } + log.Info("convertLocationResults: converting", + slog.String("from", string(r.URI)), + slog.String("to", string(templURI)), + slog.Any("goRange", r.Range)) + result[i].URI = templURI + result[i].Range = convertGoRangeToTemplRange(cache, log, templURI, r.Range) + log.Info("convertLocationResults: converted", + slog.Any("templRange", result[i].Range)) + } +} + +// convertCallHierarchyItem converts a _templ.go call hierarchy item back to .templ. +func convertCallHierarchyItem(cache *SourceMapCache, log *slog.Logger, item *lsp.CallHierarchyItem) { + isTemplGoFile, templURI := convertTemplGoToTemplURI(item.URI) + if !isTemplGoFile { + return + } + item.URI = templURI + item.Range = convertGoRangeToTemplRange(cache, log, templURI, item.Range) + item.SelectionRange = convertGoRangeToTemplRange(cache, log, templURI, item.SelectionRange) +} + +// convertWorkspaceEdit converts _templ.go URIs and ranges in a workspace edit back to .templ. +func convertWorkspaceEdit(cache *SourceMapCache, log *slog.Logger, edit *lsp.WorkspaceEdit) { + if edit == nil { + return + } + for i, dc := range edit.DocumentChanges { + isTemplGoFile, templURI := convertTemplGoToTemplURI(dc.TextDocument.URI) + if !isTemplGoFile { + continue + } + for j, e := range dc.Edits { + dc.Edits[j].Range = convertGoRangeToTemplRange(cache, log, templURI, e.Range) + } + dc.TextDocument.URI = templURI + edit.DocumentChanges[i] = dc + } + if edit.Changes == nil { + return + } + converted := make(map[lsp.DocumentURI][]lsp.TextEdit) + for docURI, edits := range edit.Changes { + isTemplGoFile, templURI := convertTemplGoToTemplURI(docURI) + if !isTemplGoFile { + converted[docURI] = edits + continue + } + for i, e := range edits { + edits[i].Range = convertGoRangeToTemplRange(cache, log, templURI, e.Range) + } + converted[templURI] = edits + } + edit.Changes = converted +} diff --git a/vendor/github.com/a-h/templ/cmd/templ/lspcmd/proxy/server.go b/vendor/github.com/a-h/templ/cmd/templ/lspcmd/proxy/server.go index 5307e540d1..6117180827 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/lspcmd/proxy/server.go +++ b/vendor/github.com/a-h/templ/cmd/templ/lspcmd/proxy/server.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/a-h/parse" + "github.com/a-h/templ/internal/format" "github.com/a-h/templ/internal/imports" "github.com/a-h/templ/internal/lazyloader" lsp "github.com/a-h/templ/lsp/protocol" @@ -41,12 +42,15 @@ type Server struct { DiagnosticCache *DiagnosticCache TemplSource *DocumentContents GoSource map[string]string + GoplsPath string + GoplsVersion string NoPreload bool preLoadURIs []*lsp.DidOpenTextDocumentParams templDocLazyLoader lazyloader.TemplDocLazyLoader + formatConf format.Config } -func NewServer(log *slog.Logger, target lsp.Server, cache *SourceMapCache, diagnosticCache *DiagnosticCache, noPreload bool) (s *Server) { +func NewServer(log *slog.Logger, target lsp.Server, cache *SourceMapCache, diagnosticCache *DiagnosticCache, noPreload bool, formatConf format.Config) (s *Server) { return &Server{ Log: log, Target: target, @@ -55,6 +59,7 @@ func NewServer(log *slog.Logger, target lsp.Server, cache *SourceMapCache, diagn TemplSource: newDocumentContents(log), GoSource: make(map[string]string), NoPreload: noPreload, + formatConf: formatConf, } } @@ -106,28 +111,28 @@ func (p *Server) convertTemplRangeToGoRange(templURI lsp.DocumentURI, input lsp. return } -func (p *Server) convertGoRangeToTemplRange(templURI lsp.DocumentURI, input lsp.Range) (output lsp.Range) { - output = input - sourceMap, ok := p.SourceMapCache.Get(string(templURI)) - if !ok { - p.Log.Warn("go->templ: sourcemap not found in cache") - return - } - // Map from the source position to target Go position. - start, startPositionMapped := sourceMap.SourcePositionFromTarget(input.Start.Line, input.Start.Character) - if startPositionMapped { - output.Start.Line = start.Line - output.Start.Character = start.Col +// proxyPositionRequest determines how to forward a position-based request. +// For .templ files, it converts the position to the corresponding Go position. +// For non-templ files (e.g. .go), it returns the position unchanged so the +// request can be forwarded to gopls as-is. +// Returns ok=false only when position mapping fails for a .templ file. +func (p *Server) proxyPositionRequest(templURI lsp.DocumentURI, pos lsp.Position) (isTempl bool, goURI lsp.DocumentURI, goPos lsp.Position, ok bool) { + isTemplFile, goFileURI := convertTemplToGoURI(templURI) + if !isTemplFile { + return false, templURI, pos, true } - end, endPositionMapped := sourceMap.SourcePositionFromTarget(input.End.Line, input.End.Character) - if endPositionMapped { - output.End.Line = end.Line - output.End.Character = end.Col + sourceMap, smOk := p.SourceMapCache.Get(string(templURI)) + if !smOk { + p.Log.Warn("proxyPositionRequest: sourcemap not found in cache", slog.String("uri", string(templURI))) + return true, templURI, pos, false } - if !startPositionMapped || !endPositionMapped { - p.Log.Warn("go->templ: range not found in sourcemap", slog.Any("range", input)) + to, toOk := sourceMap.TargetPositionFromSource(pos.Line, pos.Character) + if !toOk { + p.Log.Info("proxyPositionRequest: position not found in sourcemap", + slog.String("from", fmt.Sprintf("%d:%d", pos.Line, pos.Character))) + return true, templURI, pos, false } - return + return true, goFileURI, lsp.Position{Line: to.Line, Character: to.Col}, true } // parseTemplate parses the templ file content, and notifies the end user via the LSP about how it went. @@ -256,6 +261,13 @@ func (p *Server) Initialize(ctx context.Context, params *lsp.InitializeParams) ( result.ServerInfo.Name = "templ-lsp" result.ServerInfo.Version = templ.Version() + // Advertise Go file support so editors can register for .go files + // and route them through the proxy for cross-file navigation. + result.Capabilities.Experimental = map[string]any{ + "templ": map[string]any{ + "goFileSupport": true, + }, + } return result, err } @@ -327,6 +339,8 @@ func (p *Server) Initialized(ctx context.Context, params *lsp.InitializedParams) defer p.Log.Info("client -> server: Initialized end") goInitErr := p.Target.Initialized(ctx, params) + p.notifyGoplsVersion(ctx) + for i, doParams := range p.preLoadURIs { doErr := p.Target.DidOpen(ctx, doParams) if doErr != nil { @@ -338,6 +352,26 @@ func (p *Server) Initialized(ctx context.Context, params *lsp.InitializedParams) return goInitErr } +func (p *Server) notifyGoplsVersion(ctx context.Context) { + client := lsp.ClientFromContext(ctx) + if client == nil { + return + } + + if p.GoplsVersion == "" { + _ = client.LogMessage(ctx, &lsp.LogMessageParams{ + Type: lsp.MessageTypeWarning, + Message: fmt.Sprintf("templ: could not determine gopls version at %s. If you experience errors, upgrade with: go install golang.org/x/tools/gopls@latest", p.GoplsPath), + }) + return + } + + _ = client.LogMessage(ctx, &lsp.LogMessageParams{ + Type: lsp.MessageTypeInfo, + Message: fmt.Sprintf("templ: using gopls %s (%s)", p.GoplsVersion, p.GoplsPath), + }) +} + func (p *Server) Shutdown(ctx context.Context) (err error) { p.Log.Info("client -> server: Shutdown") defer p.Log.Info("client -> server: Shutdown end") @@ -368,7 +402,9 @@ func (p *Server) SetTrace(ctx context.Context, params *lsp.SetTraceParams) (err return p.Target.SetTrace(ctx, params) } -var supportedCodeActions = map[string]bool{} +var supportedCodeActions = map[string]bool{ + "Organize Imports": true, +} func (p *Server) CodeAction(ctx context.Context, params *lsp.CodeActionParams) (result []lsp.CodeAction, err error) { p.Log.Info("client -> server: CodeAction", slog.Any("params", params)) @@ -386,7 +422,7 @@ func (p *Server) CodeAction(ctx context.Context, params *lsp.CodeActionParams) ( } isTemplFile, goURI := convertTemplToGoURI(templURI) if !isTemplFile { - return p.Target.CodeAction(ctx, params) + return nil, nil } var ok bool if params.Range, ok = p.convertTemplRangeToGoRange(templURI, params.Range); !ok { @@ -408,20 +444,10 @@ func (p *Server) CodeAction(ctx context.Context, params *lsp.CodeActionParams) ( if !supportedCodeActions[r.Title] { continue } - // Rewrite the Diagnostics range field. for di, diag := range r.Diagnostics { - r.Diagnostics[di].Range = p.convertGoRangeToTemplRange(templURI, diag.Range) - } - // Rewrite the DocumentChanges. - if r.Edit != nil { - for dci, dc := range r.Edit.DocumentChanges { - for ei, edit := range dc.Edits { - dc.Edits[ei].Range = p.convertGoRangeToTemplRange(templURI, edit.Range) - } - dc.TextDocument.URI = templURI - r.Edit.DocumentChanges[dci] = dc - } + r.Diagnostics[di].Range = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, diag.Range) } + convertWorkspaceEdit(p.SourceMapCache, p.Log, r.Edit) updatedResults = append(updatedResults, r) } return updatedResults, nil @@ -448,7 +474,7 @@ func (p *Server) CodeLens(ctx context.Context, params *lsp.CodeLensParams) (resu return } for i, cl := range result { - cl.Range = p.convertGoRangeToTemplRange(templURI, cl.Range) + cl.Range = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, cl.Range) result[i] = cl } return @@ -482,7 +508,7 @@ func (p *Server) ColorPresentation(ctx context.Context, params *lsp.ColorPresent } for i, r := range result { if r.TextEdit != nil { - r.TextEdit.Range = p.convertGoRangeToTemplRange(templURI, r.TextEdit.Range) + r.TextEdit.Range = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, r.TextEdit.Range) } result[i] = r } @@ -492,6 +518,10 @@ func (p *Server) ColorPresentation(ctx context.Context, params *lsp.ColorPresent func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) (result *lsp.CompletionList, err error) { p.Log.Info("client -> server: Completion") defer p.Log.Info("client -> server: Completion end") + isTemplFile, _ := convertTemplToGoURI(params.TextDocument.URI) + if !isTemplFile { + return nil, nil + } if params.Context != nil && params.Context.TriggerCharacter == "<" { result = &lsp.CompletionList{ Items: htmlSnippets, @@ -537,12 +567,12 @@ func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) ( item.FilterText = stripTemplStringable(item.FilterText) if item.TextEdit != nil { if item.TextEdit.TextEdit != nil { - item.TextEdit.TextEdit.Range = p.convertGoRangeToTemplRange(templURI, item.TextEdit.TextEdit.Range) + item.TextEdit.TextEdit.Range = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, item.TextEdit.TextEdit.Range) item.TextEdit.TextEdit.NewText = stripTemplStringable(item.TextEdit.TextEdit.NewText) } if item.TextEdit.InsertReplaceEdit != nil { - item.TextEdit.InsertReplaceEdit.Insert = p.convertGoRangeToTemplRange(templURI, item.TextEdit.InsertReplaceEdit.Insert) - item.TextEdit.InsertReplaceEdit.Replace = p.convertGoRangeToTemplRange(templURI, item.TextEdit.InsertReplaceEdit.Replace) + item.TextEdit.InsertReplaceEdit.Insert = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, item.TextEdit.InsertReplaceEdit.Insert) + item.TextEdit.InsertReplaceEdit.Replace = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, item.TextEdit.InsertReplaceEdit.Replace) item.TextEdit.InsertReplaceEdit.NewText = stripTemplStringable(item.TextEdit.InsertReplaceEdit.NewText) } } @@ -641,62 +671,38 @@ func (p *Server) CompletionResolve(ctx context.Context, params *lsp.CompletionIt func (p *Server) Declaration(ctx context.Context, params *lsp.DeclarationParams) (result []lsp.Location /* Declaration | DeclarationLink[] | null */, err error) { p.Log.Info("client -> server: Declaration") defer p.Log.Info("client -> server: Declaration end") - // Rewrite the request. - templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) - if err != nil { - p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) - return - } - var ok bool - ok, params.TextDocument.URI, params.Position = p.updatePosition(templURI, params.Position) + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) if !ok { return nil, nil } - // Call gopls and get the result. - result, err = p.Target.Declaration(ctx, params) - if err != nil { - return + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos } - if result == nil { + result, err = p.Target.Declaration(ctx, params) + if err != nil || result == nil { return } - for i, r := range result { - if isTemplGoFile, templURI := convertTemplGoToTemplURI(r.URI); isTemplGoFile { - result[i].URI = templURI - result[i].Range = p.convertGoRangeToTemplRange(templURI, r.Range) - } - } + convertLocationResults(p.SourceMapCache, p.Log, result) return } func (p *Server) Definition(ctx context.Context, params *lsp.DefinitionParams) (result []lsp.Location /* Definition | DefinitionLink[] | null */, err error) { p.Log.Info("client -> server: Definition") defer p.Log.Info("client -> server: Definition end") - // Rewrite the request. - templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) - if err != nil { - p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) - return - } - var ok bool - ok, params.TextDocument.URI, params.Position = p.updatePosition(templURI, params.Position) + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) if !ok { - return result, nil + return nil, nil } - // Call gopls and get the result. - result, err = p.Target.Definition(ctx, params) - if err != nil { - return + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos } - if result == nil { + result, err = p.Target.Definition(ctx, params) + if err != nil || result == nil { return } - for i, r := range result { - if isTemplGoFile, templURI := convertTemplGoToTemplURI(r.URI); isTemplGoFile { - result[i].URI = templURI - result[i].Range = p.convertGoRangeToTemplRange(templURI, r.Range) - } - } + convertLocationResults(p.SourceMapCache, p.Log, result) return } @@ -710,8 +716,7 @@ func (p *Server) DidChange(ctx context.Context, params *lsp.DidChangeTextDocumen } isTemplFile, goURI := convertTemplToGoURI(templURI) if !isTemplFile { - p.Log.Error("not a templ file") - return + return p.Target.DidChange(ctx, params) } // Apply content changes to the cached template. d, err := p.TemplSource.Apply(string(templURI), params.ContentChanges) @@ -791,9 +796,10 @@ func (p *Server) DidClose(ctx context.Context, params *lsp.DidCloseTextDocumentP p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) return err } - + if isTemplFile, _ := convertTemplToGoURI(templURI); !isTemplFile { + return p.Target.DidClose(ctx, params) + } params.TextDocument.URI = templURI - return p.templDocLazyLoader.Unload(ctx, params) } @@ -828,6 +834,9 @@ func (p *Server) DidOpen(ctx context.Context, params *lsp.DidOpenTextDocumentPar defer p.Log.Info("client -> server: DidOpen end") if p.NoPreload { + if isTemplFile, _ := convertTemplToGoURI(templURI); !isTemplFile { + return p.Target.DidOpen(ctx, params) + } params.TextDocument.URI = templURI return p.templDocLazyLoader.Load(ctx, params) } @@ -903,7 +912,7 @@ func (p *Server) DocumentColor(ctx context.Context, params *lsp.DocumentColorPar return } for i, r := range result { - result[i].Range = p.convertGoRangeToTemplRange(templURI, r.Range) + result[i].Range = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, r.Range) } return } @@ -911,17 +920,37 @@ func (p *Server) DocumentColor(ctx context.Context, params *lsp.DocumentColorPar func (p *Server) DocumentHighlight(ctx context.Context, params *lsp.DocumentHighlightParams) (result []lsp.DocumentHighlight, err error) { p.Log.Info("client -> server: DocumentHighlight") defer p.Log.Info("client -> server: DocumentHighlight end") + if isPlainGoFile(params.TextDocument.URI) { + return nil, nil + } + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) + if !ok { + return nil, nil + } + templURI := params.TextDocument.URI + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos + } + result, err = p.Target.DocumentHighlight(ctx, params) + if err != nil || result == nil { + return + } + if isTempl { + for i, r := range result { + result[i].Range = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, r.Range) + } + } return } func (p *Server) DocumentLink(ctx context.Context, params *lsp.DocumentLinkParams) (result []lsp.DocumentLink, err error) { - templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) - if err != nil { - p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) - return - } - p.Log.Info("client -> server: DocumentLink", slog.String("uri", string(templURI))) + p.Log.Info("client -> server: DocumentLink", slog.String("uri", string(params.TextDocument.URI))) defer p.Log.Info("client -> server: DocumentLink end") + if isTemplFile, _ := convertTemplToGoURI(params.TextDocument.URI); !isTemplFile { + return nil, nil + } + // No document links for templ files. return } @@ -951,7 +980,7 @@ func (p *Server) DocumentLinkResolve(ctx context.Context, params *lsp.DocumentLi return } result.Target = templURI - result.Range = p.convertGoRangeToTemplRange(templURI, result.Range) + result.Range = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, result.Range) return } @@ -980,7 +1009,7 @@ func (p *Server) DocumentSymbol(ctx context.Context, params *lsp.DocumentSymbolP } if s.SymbolInformation != nil { s.SymbolInformation.Location.URI = templURI - s.SymbolInformation.Location.Range = p.convertGoRangeToTemplRange(templURI, s.SymbolInformation.Location.Range) + s.SymbolInformation.Location.Range = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, s.SymbolInformation.Location.Range) result = append(result, s) } } @@ -1011,7 +1040,7 @@ func (p *Server) convertSymbolRange(templURI lsp.DocumentURI, s *lsp.DocumentSym } // Within the symbol, we can select sub-sections. // These are Go expressions, in the standard source map. - s.SelectionRange = p.convertGoRangeToTemplRange(templURI, s.SelectionRange) + s.SelectionRange = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, s.SelectionRange) for i := range s.Children { p.convertSymbolRange(templURI, &s.Children[i]) if !isRangeWithin(s.Range, s.Children[i].Range) { @@ -1046,13 +1075,15 @@ func (p *Server) FoldingRanges(ctx context.Context, params *lsp.FoldingRangePara p.Log.Info("client -> server: FoldingRanges") defer p.Log.Info("client -> server: FoldingRanges end") // There are no folding ranges in templ files. - // return p.Target.FoldingRanges(ctx, params) return []lsp.FoldingRange{}, nil } func (p *Server) Formatting(ctx context.Context, params *lsp.DocumentFormattingParams) (result []lsp.TextEdit, err error) { p.Log.Info("client -> server: Formatting") defer p.Log.Info("client -> server: Formatting end") + if isTemplFile, _ := convertTemplToGoURI(params.TextDocument.URI); !isTemplFile { + return nil, nil + } // Format the current document. templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) if err != nil { @@ -1074,6 +1105,10 @@ func (p *Server) Formatting(ctx context.Context, params *lsp.DocumentFormattingP p.Log.Error("organise imports failure", slog.Any("error", err)) return } + if err = format.ApplyPrettier(template, p.formatConf); err != nil { + p.Log.Error("prettier failure", slog.Any("error", err)) + return + } w := new(strings.Builder) err = template.Write(w) if err != nil { @@ -1095,27 +1130,24 @@ func (p *Server) Formatting(ctx context.Context, params *lsp.DocumentFormattingP func (p *Server) Hover(ctx context.Context, params *lsp.HoverParams) (result *lsp.Hover, err error) { p.Log.Info("client -> server: Hover") defer p.Log.Info("client -> server: Hover end") - // Rewrite the request. - templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) - if err != nil { - p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) - return + if isPlainGoFile(params.TextDocument.URI) { + return nil, nil } - var ok bool - ok, params.TextDocument.URI, params.Position = p.updatePosition(templURI, params.Position) + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) if !ok { return nil, nil } - // Call gopls. + templURI := params.TextDocument.URI + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos + } result, err = p.Target.Hover(ctx, params) if err != nil { return } - // Rewrite the response. - if result != nil && result.Range != nil { - p.Log.Info("hover: result returned") - r := p.convertGoRangeToTemplRange(templURI, *result.Range) - p.Log.Info("hover: setting range") + if result != nil && result.Range != nil && isTempl { + r := convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, *result.Range) result.Range = &r } return @@ -1124,59 +1156,45 @@ func (p *Server) Hover(ctx context.Context, params *lsp.HoverParams) (result *ls func (p *Server) Implementation(ctx context.Context, params *lsp.ImplementationParams) (result []lsp.Location, err error) { p.Log.Info("client -> server: Implementation") defer p.Log.Info("client -> server: Implementation end") - templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) - if err != nil { - p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) - return - } - // Rewrite the request. - var ok bool - ok, params.TextDocument.URI, params.Position = p.updatePosition(templURI, params.Position) + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) if !ok { return nil, nil } - result, err = p.Target.Implementation(ctx, params) - if err != nil { - return + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos } - if result == nil { + result, err = p.Target.Implementation(ctx, params) + if err != nil || result == nil { return } - // Rewrite the response. - for i, r := range result { - r.URI = templURI - r.Range = p.convertGoRangeToTemplRange(templURI, r.Range) - result[i] = r - } + convertLocationResults(p.SourceMapCache, p.Log, result) return } func (p *Server) OnTypeFormatting(ctx context.Context, params *lsp.DocumentOnTypeFormattingParams) (result []lsp.TextEdit, err error) { p.Log.Info("client -> server: OnTypeFormatting") defer p.Log.Info("client -> server: OnTypeFormatting end") - templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) - if err != nil { - p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) - return + if isPlainGoFile(params.TextDocument.URI) { + return nil, nil } - // Rewrite the request. - var ok bool - ok, params.TextDocument.URI, params.Position = p.updatePosition(templURI, params.Position) + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) if !ok { return nil, nil } - // Get the response. - result, err = p.Target.OnTypeFormatting(ctx, params) - if err != nil { - return + templURI := params.TextDocument.URI + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos } - if result == nil { + result, err = p.Target.OnTypeFormatting(ctx, params) + if err != nil || result == nil { return } - // Rewrite the response. - for i, r := range result { - r.Range = p.convertGoRangeToTemplRange(templURI, r.Range) - result[i] = r + if isTempl { + for i, r := range result { + result[i].Range = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, r.Range) + } } return } @@ -1184,142 +1202,144 @@ func (p *Server) OnTypeFormatting(ctx context.Context, params *lsp.DocumentOnTyp func (p *Server) PrepareRename(ctx context.Context, params *lsp.PrepareRenameParams) (result *lsp.Range, err error) { p.Log.Info("client -> server: PrepareRename") defer p.Log.Info("client -> server: PrepareRename end") - templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) - if err != nil { - p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) - return - } - // Rewrite the request. - var ok bool - ok, params.TextDocument.URI, params.Position = p.updatePosition(templURI, params.Position) + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) if !ok { return nil, nil } - // Get the response. + templURI := params.TextDocument.URI + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos + } result, err = p.Target.PrepareRename(ctx, params) - if err != nil { + if err != nil || result == nil { return } - if result == nil { - return + if isTempl { + output := convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, *result) + return &output, nil } - // Rewrite the response. - output := p.convertGoRangeToTemplRange(templURI, *result) - return &output, nil + return } func (p *Server) RangeFormatting(ctx context.Context, params *lsp.DocumentRangeFormattingParams) (result []lsp.TextEdit, err error) { p.Log.Info("client -> server: RangeFormatting") defer p.Log.Info("client -> server: RangeFormatting end") - templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) - if err != nil { - p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) - return + templURI := params.TextDocument.URI + isTemplFile, goURI := convertTemplToGoURI(templURI) + if !isTemplFile { + return nil, nil } - // Rewrite the request. - var isTemplURI bool - isTemplURI, params.TextDocument.URI = convertTemplToGoURI(templURI) - if !isTemplURI { - err = fmt.Errorf("not a templ file") + var ok bool + if params.Range, ok = p.convertTemplRangeToGoRange(templURI, params.Range); !ok { return } - // Call gopls. + params.TextDocument.URI = goURI result, err = p.Target.RangeFormatting(ctx, params) if err != nil { return } - // Rewrite the response. for i, r := range result { - r.Range = p.convertGoRangeToTemplRange(templURI, r.Range) - result[i] = r + result[i].Range = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, r.Range) } - return result, err + return } func (p *Server) References(ctx context.Context, params *lsp.ReferenceParams) (result []lsp.Location, err error) { p.Log.Info("client -> server: References") defer p.Log.Info("client -> server: References end") - templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) - if err != nil { - p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) - return - } - // Rewrite the request. - var ok bool - ok, params.TextDocument.URI, params.Position = p.updatePosition(templURI, params.Position) + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) if !ok { return nil, nil } - // Call gopls. + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos + } result, err = p.Target.References(ctx, params) - if err != nil { + if err != nil || result == nil { return } - // Rewrite the response. - for i, r := range result { - isTemplURI, templURI := convertTemplGoToTemplURI(r.URI) - if isTemplURI { - p.Log.Info(fmt.Sprintf("references-%d - range conversion for %s", i, r.URI)) - r.URI, r.Range = templURI, p.convertGoRangeToTemplRange(templURI, r.Range) - } - p.Log.Info(fmt.Sprintf("references-%d: %+v", i, r)) - result[i] = r - } - return result, err + convertLocationResults(p.SourceMapCache, p.Log, result) + return } func (p *Server) Rename(ctx context.Context, params *lsp.RenameParams) (result *lsp.WorkspaceEdit, err error) { p.Log.Info("client -> server: Rename") defer p.Log.Info("client -> server: Rename end") - return p.Target.Rename(ctx, params) + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) + if !ok { + return nil, nil + } + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos + } + result, err = p.Target.Rename(ctx, params) + if err != nil { + return + } + convertWorkspaceEdit(p.SourceMapCache, p.Log, result) + return } func (p *Server) SignatureHelp(ctx context.Context, params *lsp.SignatureHelpParams) (result *lsp.SignatureHelp, err error) { p.Log.Info("client -> server: SignatureHelp") defer p.Log.Info("client -> server: SignatureHelp end") - templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) - if err != nil { - p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) - return + if isPlainGoFile(params.TextDocument.URI) { + return nil, nil } - var ok bool - ok, params.TextDocument.URI, params.Position = p.updatePosition(templURI, params.Position) + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) if !ok { return nil, nil } + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos + } return p.Target.SignatureHelp(ctx, params) } func (p *Server) Symbols(ctx context.Context, params *lsp.WorkspaceSymbolParams) (result []lsp.SymbolInformation, err error) { p.Log.Info("client -> server: Symbols") defer p.Log.Info("client -> server: Symbols end") - return p.Target.Symbols(ctx, params) + result, err = p.Target.Symbols(ctx, params) + if err != nil || result == nil { + return + } + for i, s := range result { + if isTemplGoFile, templURI := convertTemplGoToTemplURI(s.Location.URI); isTemplGoFile { + result[i].Location.URI = templURI + result[i].Location.Range = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, s.Location.Range) + } + } + return } func (p *Server) TypeDefinition(ctx context.Context, params *lsp.TypeDefinitionParams) (result []lsp.Location, err error) { p.Log.Info("client -> server: TypeDefinition") defer p.Log.Info("client -> server: TypeDefinition end") - templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) - if err != nil { - p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) - return - } - var ok bool - ok, params.TextDocument.URI, params.Position = p.updatePosition(templURI, params.Position) + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) if !ok { return nil, nil } - return p.Target.TypeDefinition(ctx, params) + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos + } + result, err = p.Target.TypeDefinition(ctx, params) + if err != nil || result == nil { + return + } + convertLocationResults(p.SourceMapCache, p.Log, result) + return } func (p *Server) WillSave(ctx context.Context, params *lsp.WillSaveTextDocumentParams) (err error) { p.Log.Info("client -> server: WillSave") defer p.Log.Info("client -> server: WillSave end") - var ok bool - ok, params.TextDocument.URI = convertTemplToGoURI(params.TextDocument.URI) - if !ok { - p.Log.Error("not a templ file") - return nil + if isTemplFile, goURI := convertTemplToGoURI(params.TextDocument.URI); isTemplFile { + params.TextDocument.URI = goURI } return p.Target.WillSave(ctx, params) } @@ -1381,19 +1401,55 @@ func (p *Server) CodeLensRefresh(ctx context.Context) (err error) { func (p *Server) PrepareCallHierarchy(ctx context.Context, params *lsp.CallHierarchyPrepareParams) (result []lsp.CallHierarchyItem, err error) { p.Log.Info("client -> server: PrepareCallHierarchy") defer p.Log.Info("client -> server: PrepareCallHierarchy end") - return p.Target.PrepareCallHierarchy(ctx, params) + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) + if !ok { + return nil, nil + } + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos + } + result, err = p.Target.PrepareCallHierarchy(ctx, params) + if err != nil || result == nil { + return + } + for i := range result { + convertCallHierarchyItem(p.SourceMapCache, p.Log, &result[i]) + } + return } func (p *Server) IncomingCalls(ctx context.Context, params *lsp.CallHierarchyIncomingCallsParams) (result []lsp.CallHierarchyIncomingCall, err error) { p.Log.Info("client -> server: IncomingCalls") defer p.Log.Info("client -> server: IncomingCalls end") - return p.Target.IncomingCalls(ctx, params) + result, err = p.Target.IncomingCalls(ctx, params) + if err != nil || result == nil { + return + } + for i := range result { + // Check the original URI before converting the item. + isTemplGoFile, templURI := convertTemplGoToTemplURI(result[i].From.URI) + convertCallHierarchyItem(p.SourceMapCache, p.Log, &result[i].From) + if isTemplGoFile { + for j, r := range result[i].FromRanges { + result[i].FromRanges[j] = convertGoRangeToTemplRange(p.SourceMapCache, p.Log, templURI, r) + } + } + } + return } func (p *Server) OutgoingCalls(ctx context.Context, params *lsp.CallHierarchyOutgoingCallsParams) (result []lsp.CallHierarchyOutgoingCall, err error) { p.Log.Info("client -> server: OutgoingCalls") defer p.Log.Info("client -> server: OutgoingCalls end") - return p.Target.OutgoingCalls(ctx, params) + result, err = p.Target.OutgoingCalls(ctx, params) + if err != nil || result == nil { + return + } + for i := range result { + convertCallHierarchyItem(p.SourceMapCache, p.Log, &result[i].To) + } + return } func (p *Server) SemanticTokensFull(ctx context.Context, params *lsp.SemanticTokensParams) (result *lsp.SemanticTokens, err error) { @@ -1444,16 +1500,17 @@ func (p *Server) LinkedEditingRange(ctx context.Context, params *lsp.LinkedEditi func (p *Server) Moniker(ctx context.Context, params *lsp.MonikerParams) (result []lsp.Moniker, err error) { p.Log.Info("client -> server: Moniker") defer p.Log.Info("client -> server: Moniker end") - templURI, err := uri.ParseDocumentURI(string(params.TextDocument.URI)) - if err != nil { - p.Log.Error("invalid uri", slog.String("uri", string(params.TextDocument.URI))) - return + if isPlainGoFile(params.TextDocument.URI) { + return nil, nil } - var ok bool - ok, params.TextDocument.URI, params.Position = p.updatePosition(templURI, params.Position) + isTempl, goURI, goPos, ok := p.proxyPositionRequest(params.TextDocument.URI, params.Position) if !ok { return nil, nil } + if isTempl { + params.TextDocument.URI = goURI + params.Position = goPos + } return p.Target.Moniker(ctx, params) } diff --git a/vendor/github.com/a-h/templ/cmd/templ/main.go b/vendor/github.com/a-h/templ/cmd/templ/main.go index 3f15d24042..4787f10df7 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/main.go +++ b/vendor/github.com/a-h/templ/cmd/templ/main.go @@ -16,6 +16,7 @@ import ( "github.com/a-h/templ/cmd/templ/infocmd" "github.com/a-h/templ/cmd/templ/lspcmd" "github.com/a-h/templ/cmd/templ/sloghandler" + "github.com/a-h/templ/internal/format" "github.com/fatih/color" ) @@ -241,6 +242,10 @@ Args: Enable http debug server by setting a listen address (e.g. localhost:7474) -no-preload Disable preloading of templ files on server startup and use custom GOPACKAGESDRIVER for lazy loading (useful for large monorepos). GOPACKAGESDRIVER environment variable must be set. + -prettier-command + Set the command to use for formatting HTML, CSS, and JS blocks. Default is "prettier --stdin-filepath $TEMPL_PRETTIER_FILENAME". + -prettier-required + Set to true to return an error the prettier command is not available. Default is false. ` func lspCmd(stdin io.Reader, stdout, stderr io.Writer, args []string) (code int) { @@ -253,6 +258,8 @@ func lspCmd(stdin io.Reader, stdout, stderr io.Writer, args []string) (code int) pprofFlag := cmd.Bool("pprof", false, "") httpDebugFlag := cmd.String("http", "", "") noPreloadFlag := cmd.Bool("no-preload", false, "") + prettierCommand := cmd.String("prettier-command", "", "") + prettierRequired := cmd.Bool("prettier-required", false, "") err := cmd.Parse(args) if err != nil { _, _ = fmt.Fprint(stderr, lspUsageText) @@ -271,6 +278,10 @@ func lspCmd(stdin io.Reader, stdout, stderr io.Writer, args []string) (code int) PPROF: *pprofFlag, HTTPDebug: *httpDebugFlag, NoPreload: *noPreloadFlag && os.Getenv("GOPACKAGESDRIVER") != "", + FormatConfig: format.Config{ + PrettierCommand: *prettierCommand, + PrettierRequired: *prettierRequired, + }, }) if err != nil { _, _ = fmt.Fprintln(stderr, err.Error()) diff --git a/vendor/github.com/a-h/templ/cmd/templ/processor/processor.go b/vendor/github.com/a-h/templ/cmd/templ/processor/processor.go index aa2d00d511..fa44d0b78a 100644 --- a/vendor/github.com/a-h/templ/cmd/templ/processor/processor.go +++ b/vendor/github.com/a-h/templ/cmd/templ/processor/processor.go @@ -17,18 +17,18 @@ type Result struct { ChangesMade bool } -func Process(dir string, f func(fileName string) (error, bool), workerCount int, results chan<- Result) { +func Process(dir string, f func(fileName string) (error, bool), workerCount int, shouldSkip func(string) bool, results chan<- Result) { templates := make(chan string) go func() { defer close(templates) - if err := FindTemplates(dir, templates); err != nil { + if err := FindTemplates(dir, shouldSkip, templates); err != nil { results <- Result{Error: err} } }() ProcessChannel(templates, dir, f, workerCount, results) } -func FindTemplates(srcPath string, output chan<- string) (err error) { +func FindTemplates(srcPath string, shouldSkip func(string) bool, output chan<- string) (err error) { return filepath.WalkDir(srcPath, func(currentPath string, info fs.DirEntry, err error) error { if err != nil { return err @@ -36,6 +36,12 @@ func FindTemplates(srcPath string, output chan<- string) (err error) { if info.IsDir() && skipdir.ShouldSkip(currentPath) { return filepath.SkipDir } + if shouldSkip != nil && shouldSkip(currentPath) { + if info.IsDir() { + return filepath.SkipDir + } + return nil + } if !info.IsDir() && strings.HasSuffix(currentPath, ".templ") { output <- currentPath } diff --git a/vendor/github.com/a-h/templ/flake.lock b/vendor/github.com/a-h/templ/flake.lock index 21008b634c..5d6ac51ffb 100644 --- a/vendor/github.com/a-h/templ/flake.lock +++ b/vendor/github.com/a-h/templ/flake.lock @@ -22,27 +22,27 @@ }, "nixpkgs": { "locked": { - "lastModified": 1772047000, - "narHash": "sha256-7DaQVv4R97cii/Qdfy4tmDZMB2xxtyIvNGSwXBBhSmo=", + "lastModified": 1791031588, + "narHash": "sha256-/FGDr01siZ8txrVzkOz6CYjiz0XR27JuVJP+aWchi4s=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1267bb4920d0fc06ea916734c11b0bf004bbe17e", + "rev": "825e2028c29b702a4a5f085f08095d12099784f2", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-25.11", + "ref": "nixos-26.05", "repo": "nixpkgs", "type": "github" } }, "nixpkgs-unstable": { "locked": { - "lastModified": 1771848320, - "narHash": "sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=", + "lastModified": 1791048980, + "narHash": "sha256-KgItSKML8Xvte0B7/uGnBDsYzSnnKHcOaiUbgWBXLXw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2fc6539b481e1d2569f25f8799236694180c0993", + "rev": "a7868a727837f3c09cee2ce0ca671c76b1589fed", "type": "github" }, "original": { @@ -67,16 +67,15 @@ ] }, "locked": { - "lastModified": 1749991223, - "narHash": "sha256-K6OM2m+Bdkbq7MvTIwI1t0aPIwmkLUDeUfev5VHpiwg=", + "lastModified": 1785656599, + "narHash": "sha256-8uEQBbupqXo3MH0Kufz544tVNnwM5Wfsp7m1sbF2Q0M=", "owner": "a-h", "repo": "version", - "rev": "da721166410c6e7e2bea37cf3dee3948b5d0c83f", + "rev": "913277d49bdedfcfb02abff1481155ec50000d4b", "type": "github" }, "original": { "owner": "a-h", - "ref": "0.0.10", "repo": "version", "type": "github" } diff --git a/vendor/github.com/a-h/templ/flake.nix b/vendor/github.com/a-h/templ/flake.nix index dc2e54487e..0365221d96 100644 --- a/vendor/github.com/a-h/templ/flake.nix +++ b/vendor/github.com/a-h/templ/flake.nix @@ -2,19 +2,26 @@ description = "templ"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05"; nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable"; gitignore = { url = "github:hercules-ci/gitignore.nix"; inputs.nixpkgs.follows = "nixpkgs"; }; version = { - url = "github:a-h/version/0.0.10"; + url = "github:a-h/version"; inputs.nixpkgs.follows = "nixpkgs"; }; }; - outputs = { self, nixpkgs, nixpkgs-unstable, gitignore, version }: + outputs = + { + self, + nixpkgs, + nixpkgs-unstable, + gitignore, + version, + }: let allSystems = [ "x86_64-linux" # 64-bit Intel/AMD Linux @@ -22,33 +29,40 @@ "x86_64-darwin" # 64-bit Intel macOS "aarch64-darwin" # 64-bit ARM macOS ]; - forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f { - inherit system; - pkgs = - let - pkgs-unstable = import nixpkgs-unstable { inherit system; }; - in - import nixpkgs { + forAllSystems = + f: + nixpkgs.lib.genAttrs allSystems ( + system: + f { inherit system; - overlays = [ - (final: prev: { - gopls = pkgs-unstable.gopls; - version = version.packages.${system}.default; # Used to apply version numbers to the repo. - }) - ]; - }; - }); + pkgs = + let + pkgs-unstable = import nixpkgs-unstable { inherit system; }; + in + import nixpkgs { + inherit system; + overlays = [ + (final: prev: { + gopls = pkgs-unstable.gopls; + version = version.packages.${system}.default; # Used to apply version numbers to the repo. + }) + ]; + }; + } + ); in { - packages = forAllSystems ({ pkgs, ... }: + packages = forAllSystems ( + { pkgs, ... }: rec { default = templ; templ = pkgs.buildGoModule { - name = "templ"; + pname = "templ"; + version = builtins.readFile ./.version; subPackages = [ "cmd/templ" ]; src = gitignore.lib.gitignoreSource ./.; - vendorHash = "sha256-i4uDGZb3VZUvIyO2Tt53VR1Do/3OYtj6JccGoFnnlbs="; + vendorHash = "sha256-WXUlbUR+5a0BRStFF4A9mMjGiYFL5ULu1yrRhIG0vwc="; env = { CGO_ENABLED = 0; }; @@ -61,27 +75,76 @@ "-extldflags -static" ]; }; - }); + + # `nix build .#docs` builds the documentation site. + docs = pkgs.buildNpmPackage { + pname = "templ-docs"; + version = builtins.readFile ./.version; + src = gitignore.lib.gitignoreSource ./docs; + npmDeps = pkgs.importNpmLock { + npmRoot = gitignore.lib.gitignoreSource ./docs; + }; + npmConfigHook = pkgs.importNpmLock.npmConfigHook; + installPhase = '' + runHook preInstall + cp -r build $out + runHook postInstall + ''; + }; + } + # `nix build .#docker-image` builds the Docker image. It requires a Linux system. + # The image runs as the nonroot user (UID 65532) and has no shell. + // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux { + docker-image = pkgs.dockerTools.buildLayeredImage { + name = "ghcr.io/a-h/templ"; + tag = "latest"; + # Produce an uncompressed tarball for `crane push`. + compressor = "none"; + contents = [ + pkgs.dockerTools.caCertificates + self.packages.${pkgs.stdenv.hostPlatform.system}.templ + (pkgs.writeTextDir "etc/passwd" "nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin\n") + (pkgs.writeTextDir "etc/group" "nonroot:x:65532:\n") + ]; + fakeRootCommands = '' + mkdir -p home/nonroot + chown 65532:65532 home/nonroot + mkdir -m 1777 tmp + ''; + config = { + Entrypoint = [ "/bin/templ" ]; + Env = [ + "PATH=/bin" + "HOME=/home/nonroot" + ]; + User = "65532:65532"; + }; + }; + } + ); # `nix develop` provides a shell containing development tools. - devShell = forAllSystems ({ pkgs, ... }: + devShell = forAllSystems ( + { pkgs, ... }: pkgs.mkShell { buildInputs = [ pkgs.golangci-lint pkgs.cosign # Used to sign container images. + pkgs.crane # Used to push and tag Docker images. pkgs.esbuild # Used to package JS examples. pkgs.go pkgs.gopls pkgs.goreleaser pkgs.gotestsum pkgs.govulncheck - pkgs.ko # Used to build Docker images. pkgs.nodejs # Used to build templ-docs. - pkgs.nodePackages.prettier # Used for formatting JS and CSS. + pkgs.prettier # Used for formatting JS and CSS. + pkgs.syft # Used to generate SBOMs for Docker images. pkgs.version pkgs.xc ]; - }); + } + ); # This flake outputs an overlay that can be used to add templ and # templ-docs to nixpkgs as per https://templ.guide/quick-start/installation/#nix @@ -96,4 +159,3 @@ }; }; } - diff --git a/vendor/github.com/a-h/templ/generator/generator.go b/vendor/github.com/a-h/templ/generator/generator.go index 5b0ee4d7d9..b3e080529f 100644 --- a/vendor/github.com/a-h/templ/generator/generator.go +++ b/vendor/github.com/a-h/templ/generator/generator.go @@ -646,10 +646,7 @@ func (g *generator) writeNode(indentLevel int, current parser.Node, next parser. return fmt.Errorf("unhandled type: %v", reflect.TypeOf(n)) } // Write trailing whitespace, if there is a next node that might need the space. - // If the next node is inline or text, we might need it. - // If the current node is a block element, we don't need it. - needed := (isInlineOrText(current) && isInlineOrText(next)) - if ws, ok := current.(parser.WhitespaceTrailer); ok && needed { + if ws, ok := current.(parser.WhitespaceTrailer); ok && isTrailingSpaceNeeded(current, next) { if err := g.writeWhitespaceTrailer(indentLevel, ws.Trailing()); err != nil { return err } @@ -680,6 +677,40 @@ func isInlineOrText(next parser.Node) bool { return false } +func isTextLike(n parser.Node) bool { + switch n.(type) { + case *parser.Text: + return true + case *parser.StringExpression: + return true + } + return false +} + +func isSelfClosingTemplElementExpression(n parser.Node) bool { + tee, ok := n.(*parser.TemplElementExpression) + return ok && len(tee.Children) == 0 +} + +// isTrailingSpaceNeeded reports whether trailing whitespace should be emitted between current and next. +// Trailing space is needed when both nodes are inline content (text, inline elements, control flow), or +// when a self-closing templ element expression is adjacent to text or a string expression. When text +// precedes a self-closing templ expression, trailing space is only emitted for horizontal whitespace +// (same-line), not vertical (different lines), to avoid adding spaces before block-level components. +func isTrailingSpaceNeeded(current, next parser.Node) bool { + if isInlineOrText(current) && isInlineOrText(next) { + return true + } + if isSelfClosingTemplElementExpression(current) && isTextLike(next) { + return true + } + if isTextLike(current) && isSelfClosingTemplElementExpression(next) { + ws, ok := current.(parser.WhitespaceTrailer) + return ok && ws.Trailing() == parser.SpaceHorizontal + } + return false +} + func (g *generator) writeWhitespaceTrailer(indentLevel int, n parser.TrailingSpace) (err error) { if n == parser.SpaceNone { return nil @@ -1472,6 +1503,8 @@ func (g *generator) writeElementAttributes(indentLevel int, name string, attrs [ err = g.writeSpreadAttributes(indentLevel, attr) case *parser.ConditionalAttribute: err = g.writeConditionalAttribute(indentLevel, name, attr) + case *parser.AttributeComment: + continue default: err = fmt.Errorf("unknown attribute type %T", attr) } diff --git a/vendor/github.com/a-h/templ/internal/format/attributes.go b/vendor/github.com/a-h/templ/internal/format/attributes.go new file mode 100644 index 0000000000..d6bf7583ad --- /dev/null +++ b/vendor/github.com/a-h/templ/internal/format/attributes.go @@ -0,0 +1,110 @@ +package format + +import ( + "fmt" + "html" + "strconv" + "strings" + + "github.com/a-h/templ/internal/htmlfind" + "github.com/a-h/templ/internal/prettier" + parser "github.com/a-h/templ/parser/v2" + "github.com/a-h/templ/parser/v2/visitor" + nethtml "golang.org/x/net/html" +) + +// templIDMatcher matches
elements that have a data-templ-id attribute. +var templIDMatcher = func(n *nethtml.Node) bool { + if n.Type != nethtml.ElementNode || n.Data != "div" { + return false + } + for _, a := range n.Attr { + if a.Key == "data-templ-id" { + return true + } + } + return false +} + +type constantAttributeEntry struct { + attr *parser.ConstantAttribute + key string +} + +// Attributes sends constant attribute values through prettier so that plugins +// like prettier-plugin-tailwindcss can process them. Each attribute becomes a +// separate synthetic
element to avoid duplicate-key issues. +func Attributes(children []parser.Node, prettierCommand string) error { + var entries []constantAttributeEntry + collector := visitor.New() + collector.ConstantAttribute = func(n *parser.ConstantAttribute) error { + key, ok := n.Key.(parser.ConstantAttributeKey) + if !ok { + return nil + } + entries = append(entries, constantAttributeEntry{attr: n, key: key.Name}) + return nil + } + for _, child := range children { + if err := child.Visit(collector); err != nil { + return err + } + } + if len(entries) == 0 { + return nil + } + + // Build synthetic HTML: one
per attribute. + var sb strings.Builder + for i, e := range entries { + fmt.Fprintf(&sb, `
`, i, e.key, html.EscapeString(e.attr.Value)) + sb.WriteByte('\n') + } + + formatted, err := prettier.Run(sb.String(), "templ_content.html", prettierCommand) + if err != nil { + return fmt.Errorf("prettier attribute formatting error: %w", err) + } + + // Parse the formatted output and read back attribute values. + doc, err := nethtml.Parse(strings.NewReader(formatted)) + if err != nil { + return fmt.Errorf("failed to parse prettier output for attributes: %w", err) + } + + nodes := htmlfind.All(doc, templIDMatcher) + for _, node := range nodes { + idVal, ok := getAttr(node, "data-templ-id") + if !ok { + return fmt.Errorf("matched element missing data-templ-id attribute") + } + id, err := strconv.Atoi(idVal) + if err != nil { + return fmt.Errorf("failed to parse data-templ-id: %w", err) + } + if id < 0 || id >= len(entries) { + return fmt.Errorf("data-templ-id %d out of range [0, %d)", id, len(entries)) + } + e := entries[id] + val, ok := getAttr(node, e.key) + if !ok { + return fmt.Errorf("prettier removed attribute %q from element with data-templ-id %d", e.key, id) + } + e.attr.Value = val + e.attr.SingleQuote = strings.Contains(val, `"`) + } + + return nil +} + +// getAttr returns the value of the attribute with the given key. The comparison +// is case-insensitive because golang.org/x/net/html lowercases attribute keys +// during parsing. +func getAttr(n *nethtml.Node, key string) (val string, ok bool) { + for _, a := range n.Attr { + if strings.EqualFold(a.Key, key) { + return a.Val, true + } + } + return "", false +} diff --git a/vendor/github.com/a-h/templ/internal/format/templ.go b/vendor/github.com/a-h/templ/internal/format/templ.go index 969d23ddac..a5b7db322f 100644 --- a/vendor/github.com/a-h/templ/internal/format/templ.go +++ b/vendor/github.com/a-h/templ/internal/format/templ.go @@ -31,7 +31,7 @@ func Templ(src []byte, fileName string, config Config) (output []byte, changed b return nil, false, err } - if err = applyPrettier(t, config); err != nil { + if err = ApplyPrettier(t, config); err != nil { return nil, false, err } @@ -44,7 +44,7 @@ func Templ(src []byte, fileName string, config Config) (output []byte, changed b return out, changed, nil } -func applyPrettier(t *parser.TemplateFile, config Config) (err error) { +func ApplyPrettier(t *parser.TemplateFile, config Config) (err error) { // Check to see if prettier can be run. if config.PrettierCommand == "" { config.PrettierCommand = prettier.DefaultCommand() @@ -65,6 +65,10 @@ func applyPrettier(t *parser.TemplateFile, config Config) (err error) { for _, child := range n.Children { calculateNodeDepth(child, nodeToDepth, 1) } + // Format constant attribute values in a single prettier batch. + if err := Attributes(n.Children, config.PrettierCommand); err != nil { + return err + } // Now that we have the depth of each node, we can format them. for _, child := range n.Children { if err := child.Visit(nodeFormatter); err != nil { diff --git a/vendor/github.com/a-h/templ/internal/ignorefile/ignorefile.go b/vendor/github.com/a-h/templ/internal/ignorefile/ignorefile.go new file mode 100644 index 0000000000..31ef5fbc85 --- /dev/null +++ b/vendor/github.com/a-h/templ/internal/ignorefile/ignorefile.go @@ -0,0 +1,91 @@ +package ignorefile + +import ( + "bufio" + "errors" + "os" + "path/filepath" + "strings" +) + +// Patterns holds a list of glob patterns parsed from an ignore file. +type Patterns []string + +// Parse reads an ignore file and returns the patterns. Returns nil and no +// error if the file does not exist. +func Parse(path string) (patterns Patterns, err error) { + f, err := os.Open(path) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil, nil + } + return nil, err + } + defer func() { + if closeErr := f.Close(); err == nil { + err = closeErr + } + }() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + patterns = append(patterns, line) + } + return patterns, scanner.Err() +} + +// ShouldSkipFunc returns a function that reports whether a path should be +// skipped based on the ignore file found relative to root. If root is a file +// rather than a directory, its parent directory is used. The returned function +// accepts both absolute and relative paths; absolute paths are converted to +// relative paths before matching. If the ignore file does not exist, the +// returned function always returns false. +func ShouldSkipFunc(root, filename string) (func(string) bool, error) { + info, err := os.Stat(root) + if err != nil { + return nil, err + } + if !info.IsDir() { + root = filepath.Dir(root) + } + patterns, err := Parse(filepath.Join(root, filename)) + if err != nil { + return nil, err + } + return func(path string) bool { + if filepath.IsAbs(path) { + rel, err := filepath.Rel(root, path) + if err != nil { + return false + } + path = rel + } + return patterns.Matches(path) + }, nil +} + +// Matches reports whether the given path matches any pattern. It checks the +// full path and each directory prefix so that a pattern like "generator/test-*" +// matches "generator/test-foo/bar.templ". +func (p Patterns) Matches(path string) bool { + if len(p) == 0 { + return false + } + path = filepath.ToSlash(path) + parts := strings.Split(path, "/") + for _, pattern := range p { + pattern = filepath.ToSlash(pattern) + // Check progressively longer directory prefixes, then the full path. + for i := range parts { + prefix := strings.Join(parts[:i+1], "/") + if matched, _ := filepath.Match(pattern, prefix); matched { + return true + } + } + } + return false +} diff --git a/vendor/github.com/a-h/templ/internal/prettier/prettier.go b/vendor/github.com/a-h/templ/internal/prettier/prettier.go index 8cc9e3c15e..026fd55527 100644 --- a/vendor/github.com/a-h/templ/internal/prettier/prettier.go +++ b/vendor/github.com/a-h/templ/internal/prettier/prettier.go @@ -12,23 +12,42 @@ import ( "github.com/a-h/templ/internal/htmlfind" ) -const defaultPosixCommand = "prettier --use-tabs --stdin-filepath $TEMPL_PRETTIER_FILENAME" +var posixCommands = map[string]string{ + "prettier": "prettier --use-tabs --stdin-filepath $TEMPL_PRETTIER_FILENAME", + "prettierd": "prettierd --use-tabs --stdin-filepath $TEMPL_PRETTIER_FILENAME", +} + +var nuCommands = map[string]string{ + "prettier": "prettier --use-tabs --stdin-filepath $env.TEMPL_PRETTIER_FILENAME", + "prettierd": "prettierd --use-tabs --stdin-filepath $env.TEMPL_PRETTIER_FILENAME", +} -var shellNameToCommand = map[string]string{ - "nu": "prettier --use-tabs --stdin-filepath $env.TEMPL_PRETTIER_FILENAME", +var shellNameToCommands = map[string]map[string]string{ + "nu": nuCommands, } // DefaultCommand returns the default prettier command appropriate for the current shell. +// It prefers prettier over prettierd, falling back to prettierd if prettier is not found. func DefaultCommand() string { - shell := os.Getenv("SHELL") + return defaultCommand(os.Getenv("SHELL"), exec.LookPath) +} + +func defaultCommand(shell string, lookPath func(string) (string, error)) string { if shell == "" { shell = "/bin/sh" } shellName := filepath.Base(shell) - if shellCommand, ok := shellNameToCommand[shellName]; ok { - return shellCommand + commands := posixCommands + if shellCommands, ok := shellNameToCommands[shellName]; ok { + commands = shellCommands + } + for _, name := range []string{"prettier", "prettierd"} { + if _, err := lookPath(name); err == nil { + return commands[name] + } } - return defaultPosixCommand + // Neither found, return the prettier command so error messages reference it. + return commands["prettier"] } func IsAvailable(command string) bool { @@ -75,7 +94,7 @@ func Element(name string, typeAttrValue string, content string, depth int, prett // Add divs to the start and end of the script to ensure that prettier formats the content with // correct indentation. for i := range depth { - indentationWrapper.WriteString(fmt.Sprintf("
", i)) + fmt.Fprintf(&indentationWrapper, "
", i) } // Write start tag with type attribute if present. @@ -106,7 +125,7 @@ func Element(name string, typeAttrValue string, content string, depth int, prett return "", fmt.Errorf("prettier error: %w", err) } if before == after { - return before, nil + return content, nil } // Chop off the start and end divs we added to get prettier to format the content with correct @@ -114,14 +133,14 @@ func Element(name string, typeAttrValue string, content string, depth int, prett matcher := htmlfind.Element(name) nodes, err := htmlfind.AllReader(strings.NewReader(after), matcher) if err != nil { - return before, fmt.Errorf("htmlfind error: %w", err) + return content, fmt.Errorf("htmlfind error: %w", err) } if len(nodes) != 1 { - return before, fmt.Errorf("expected 1 %q node, got %d", name, len(nodes)) + return content, fmt.Errorf("expected 1 %q node, got %d", name, len(nodes)) } scriptNode := nodes[0] if scriptNode.FirstChild == nil { - return before, fmt.Errorf("%q node has no children", name) + return content, fmt.Errorf("%q node has no children", name) } var sb strings.Builder for node := range scriptNode.ChildNodes() { diff --git a/vendor/github.com/a-h/templ/lsp/protocol/client.go b/vendor/github.com/a-h/templ/lsp/protocol/client.go index 0526514e40..4092014c9b 100644 --- a/vendor/github.com/a-h/templ/lsp/protocol/client.go +++ b/vendor/github.com/a-h/templ/lsp/protocol/client.go @@ -198,6 +198,18 @@ func clientDispatch(ctx context.Context, log *slog.Logger, client Client, reply return true, reply(ctx, resp, err) + case MethodShowDocument: // request + defer log.Debug(MethodShowDocument, slog.Any("error", err)) + + var params ShowDocumentParams + if err := dec.Decode(¶ms); err != nil { + return true, replyParseError(ctx, reply, err) + } + + resp, err := client.ShowDocument(ctx, ¶ms) + + return true, reply(ctx, resp, err) + default: return false, nil } @@ -217,6 +229,7 @@ type Client interface { ApplyEdit(ctx context.Context, params *ApplyWorkspaceEditParams) (result *ApplyWorkspaceEditResponse, err error) Configuration(ctx context.Context, params *ConfigurationParams) (result []any, err error) WorkspaceFolders(ctx context.Context) (result []WorkspaceFolder, err error) + ShowDocument(ctx context.Context, params *ShowDocumentParams) (result *ShowDocumentResult, err error) } // list of client methods. @@ -410,3 +423,17 @@ func (c *client) WorkspaceFolders(ctx context.Context) (result []WorkspaceFolder return result, nil } + +// ShowDocument sends the request from a server to a client to ask the client to display a particular document in the user interface. +// +// @since 3.16.0. +func (c *client) ShowDocument(ctx context.Context, params *ShowDocumentParams) (result *ShowDocumentResult, err error) { + c.logger.Debug("call " + MethodShowDocument) + defer c.logger.Debug("end "+MethodShowDocument, slog.Any("error", err)) + + if err := Call(ctx, c.Conn, MethodShowDocument, params, &result); err != nil { + return nil, err + } + + return result, nil +} diff --git a/vendor/github.com/a-h/templ/parser/v2/doctypeparser.go b/vendor/github.com/a-h/templ/parser/v2/doctypeparser.go index b5212a936d..e852dc7094 100644 --- a/vendor/github.com/a-h/templ/parser/v2/doctypeparser.go +++ b/vendor/github.com/a-h/templ/parser/v2/doctypeparser.go @@ -14,20 +14,30 @@ var docType = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) { if _, ok, err = doctypeStartParser.Parse(pi); err != nil || !ok { return } + // The parser consumes the required separator space, but OpenRange covers only the opener. + openEnd := pi.PositionAt(pi.Index() - 1) - // Once a doctype has started, take everything until the end. - r := &DocType{} + r := &DocType{ + OpenRange: NewRange(start, openEnd), + } + + valueStart := pi.Position() if r.Value, ok, err = stringUntilLtOrGt.Parse(pi); err != nil || !ok { err = parse.Error("unclosed DOCTYPE", start) return } + valueEnd := pi.Position() + r.ValueRange = NewRange(valueStart, valueEnd) - // Clear the final '>'. + closeStart := pi.Position() if _, ok, err = gt.Parse(pi); err != nil || !ok { err = parse.Error("unclosed DOCTYPE", start) return } - r.Range = NewRange(start, pi.Position()) + closeEnd := pi.Position() + r.CloseRange = NewRange(closeStart, closeEnd) + + r.Range = NewRange(start, closeEnd) return r, true, nil }) diff --git a/vendor/github.com/a-h/templ/parser/v2/elementparser.go b/vendor/github.com/a-h/templ/parser/v2/elementparser.go index 2efdda24f2..8d79b880a3 100644 --- a/vendor/github.com/a-h/templ/parser/v2/elementparser.go +++ b/vendor/github.com/a-h/templ/parser/v2/elementparser.go @@ -12,11 +12,12 @@ import ( // Element open tag. type elementOpenTag struct { - Name string - Attributes []Attribute - IndentAttrs bool - NameRange Range - Void bool + Name string + Attributes []Attribute + IndentAttrs bool + NameRange Range + OpenTagRange Range + SelfClosing bool } var elementOpenTagParser = parse.Func(func(pi *parse.Input) (e elementOpenTag, matched bool, err error) { @@ -25,10 +26,12 @@ var elementOpenTagParser = parse.Func(func(pi *parse.Input) (e elementOpenTag, m return e, false, nil } + startIndex := pi.Index() // < if _, matched, err = lt.Parse(pi); err != nil || !matched { return } + openTagStart := pi.PositionAt(startIndex) // Element name. l := pi.Position().Line @@ -65,7 +68,8 @@ var elementOpenTagParser = parse.Func(func(pi *parse.Input) (e elementOpenTag, m return e, true, err } if matched { - e.Void = true + e.OpenTagRange = NewRange(openTagStart, pi.Position()) + e.SelfClosing = true return e, true, nil } @@ -80,6 +84,8 @@ var elementOpenTagParser = parse.Func(func(pi *parse.Input) (e elementOpenTag, m return } + e.OpenTagRange = NewRange(openTagStart, pi.Position()) + return e, true, nil }) @@ -110,6 +116,7 @@ type attributeValueParser struct { EqualsAndQuote parse.Parser[string] Suffix parse.Parser[string] UseSingleQuote bool + ConsumeSuffix bool } func (avp attributeValueParser) Parse(pi *parse.Input) (value string, valueRange Range, ok bool, err error) { @@ -128,6 +135,9 @@ func (avp attributeValueParser) Parse(pi *parse.Input) (value string, valueRange pi.Seek(start) return } + if !avp.ConsumeSuffix { + pi.Seek(valueEnd) + } return value, valueRange, true, nil } @@ -135,13 +145,13 @@ func (avp attributeValueParser) Parse(pi *parse.Input) (value string, valueRange var ( attributeValueParsers = []attributeValueParser{ // Double quoted. - {EqualsAndQuote: parse.StringFrom(parse.OptionalWhitespace, parse.String(`="`)), Suffix: parse.String(`"`), UseSingleQuote: false}, + {EqualsAndQuote: parse.StringFrom(parse.OptionalWhitespace, parse.String(`="`)), Suffix: parse.String(`"`), UseSingleQuote: false, ConsumeSuffix: true}, // Single quoted. - {EqualsAndQuote: parse.StringFrom(parse.OptionalWhitespace, parse.String(`='`)), Suffix: parse.String(`'`), UseSingleQuote: true}, + {EqualsAndQuote: parse.StringFrom(parse.OptionalWhitespace, parse.String(`='`)), Suffix: parse.String(`'`), UseSingleQuote: true, ConsumeSuffix: true}, // Unquoted. // A valid unquoted attribute value in HTML is any string of text that is not an empty string, // and that doesn’t contain spaces, tabs, line feeds, form feeds, carriage returns, ", ', `, =, <, or >. - {EqualsAndQuote: parse.StringFrom(parse.OptionalWhitespace, parse.String("=")), Suffix: parse.Any(parse.RuneIn(" \t\n\r\"'`=<>/"), parse.EOF[string]()), UseSingleQuote: false}, + {EqualsAndQuote: parse.StringFrom(parse.OptionalWhitespace, parse.String("=")), Suffix: parse.Any(parse.RuneIn(" \t\n\r\"'`=<>/"), parse.EOF[string]()), UseSingleQuote: false, ConsumeSuffix: false}, } constantAttributeParser = parse.Func(func(pi *parse.Input) (attr *ConstantAttribute, ok bool, err error) { start := pi.Index() @@ -447,6 +457,31 @@ type attributesParser struct{} func (attributesParser) Parse(in *parse.Input) (attributes []Attribute, ok bool, err error) { for { + // Try to parse and handle any comments first. + for { + if _, _, err = parse.OptionalWhitespace.Parse(in); err != nil { + return + } + + commentStart := in.Index() + if node, commentOk, commentErr := goComment.Parse(in); commentErr != nil { + return attributes, false, commentErr + } else if commentOk { + // Found a Go comment, add it as an attribute comment + if goNode, ok := node.(*GoComment); ok { + attributes = append(attributes, &AttributeComment{ + Comment: goNode.Contents, + Multiline: goNode.Multiline, + Range: goNode.Range, + }) + } + continue + } + + in.Seek(commentStart) + break + } + var attr Attribute attr, ok, err = attribute.Parse(in) if err != nil { @@ -531,17 +566,19 @@ func (elementParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { return } r := &Element{ - Name: ot.Name, - Attributes: ot.Attributes, - IndentAttrs: ot.IndentAttrs, - NameRange: ot.NameRange, + Name: ot.Name, + Attributes: ot.Attributes, + IndentAttrs: ot.IndentAttrs, + NameRange: ot.NameRange, + OpenTagRange: ot.OpenTagRange, + SelfClosing: ot.SelfClosing, } // Once we've got an open tag, the rest must be present. l := pi.Position().Line // If the element is self-closing, even if it's not really a void element (br, hr etc.), we can return early. - if ot.Void || r.IsVoidElement() { + if ot.SelfClosing || r.IsVoidElement() { // Escape early, no need to try to parse children for self-closing elements. return addTrailingSpaceAndValidate(start, r, pi) } @@ -566,6 +603,7 @@ func (elementParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { } // Close tag. + closeTagStart := pi.Position() _, ok, err = closer.Parse(pi) if err != nil { return r, true, err @@ -574,6 +612,8 @@ func (elementParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { err = parse.Error(fmt.Sprintf("<%s>: expected end tag not present or invalid tag contents", r.Name), pi.Position()) return r, true, err } + closeTagRange := NewRange(closeTagStart, pi.Position()) + r.CloseTagRange = &closeTagRange return addTrailingSpaceAndValidate(start, r, pi) } diff --git a/vendor/github.com/a-h/templ/parser/v2/goparser.go b/vendor/github.com/a-h/templ/parser/v2/goparser.go index 24412999a5..c6494ee1e9 100644 --- a/vendor/github.com/a-h/templ/parser/v2/goparser.go +++ b/vendor/github.com/a-h/templ/parser/v2/goparser.go @@ -2,6 +2,8 @@ package parser import ( "fmt" + "go/scanner" + "go/token" "strings" "github.com/a-h/parse" @@ -13,7 +15,11 @@ func parseGoFuncDecl(prefix string, pi *parse.Input) (name string, expression Ex from := pi.Index() src, _ := pi.Peek(-1) src = strings.TrimPrefix(src, prefix) - name, expr, err := goexpression.Func("func " + src) + decl, err := extractFuncDeclSignature(src) + if err != nil { + return name, expression, parse.Error(fmt.Sprintf("invalid %s declaration: %v", prefix, err.Error()), pi.Position()) + } + name, expr, err := goexpression.Func("func " + decl + "{}") if err != nil { return name, expression, parse.Error(fmt.Sprintf("invalid %s declaration: %v", prefix, err.Error()), pi.Position()) } @@ -22,6 +28,45 @@ func parseGoFuncDecl(prefix string, pi *parse.Input) (name string, expression Ex return name, NewExpression(expr, pi.PositionAt(from+len(prefix)), to), nil } +func extractFuncDeclSignature(src string) (string, error) { + var s scanner.Scanner + fset := token.NewFileSet() + file := fset.AddFile("", fset.Base(), len(src)) + s.Init(file, []byte(src), nil, scanner.ScanComments) + + var parenDepth, bracketDepth, braceDepth int + for { + pos, tok, lit := s.Scan() + switch tok { + case token.EOF: + return "", fmt.Errorf("function body open brace not found") + case token.ILLEGAL: + return "", fmt.Errorf("illegal token %q in declaration", lit) + case token.LPAREN: + parenDepth++ + case token.RPAREN: + if parenDepth > 0 { + parenDepth-- + } + case token.LBRACK: + bracketDepth++ + case token.RBRACK: + if bracketDepth > 0 { + bracketDepth-- + } + case token.LBRACE: + if parenDepth == 0 && bracketDepth == 0 && braceDepth == 0 { + return src[:int(pos)-1], nil + } + braceDepth++ + case token.RBRACE: + if braceDepth > 0 { + braceDepth-- + } + } + } +} + func parseTemplFuncDecl(pi *parse.Input) (name string, expression Expression, err error) { return parseGoFuncDecl("templ", pi) } diff --git a/vendor/github.com/a-h/templ/parser/v2/raw.go b/vendor/github.com/a-h/templ/parser/v2/raw.go index c18c771953..72e8dda852 100644 --- a/vendor/github.com/a-h/templ/parser/v2/raw.go +++ b/vendor/github.com/a-h/templ/parser/v2/raw.go @@ -21,13 +21,16 @@ func (p rawElementParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { if _, ok, err = lt.Parse(pi); err != nil || !ok { return } + openTagStart := pi.PositionAt(start) // Element name. e := &RawElement{} + nameStartIndex := pi.Index() if e.Name, ok, err = elementNameParser.Parse(pi); err != nil || !ok { pi.Seek(start) return } + e.NameRange = NewRange(pi.PositionAt(nameStartIndex), pi.Position()) if e.Name != p.name { pi.Seek(start) @@ -51,6 +54,7 @@ func (p rawElementParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { pi.Seek(start) return } + e.OpenTagRange = NewRange(openTagStart, pi.Position()) // Once we've got an open tag, parse anything until the end tag as the tag contents. // It's going to be rendered out raw. @@ -60,7 +64,9 @@ func (p rawElementParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { return } // Cut the end element. + closeTagStart := pi.Position() _, _, _ = end.Parse(pi) + e.CloseTagRange = NewRange(closeTagStart, pi.Position()) e.Range = NewRange(pi.PositionAt(start), pi.Position()) return e, true, nil diff --git a/vendor/github.com/a-h/templ/parser/v2/scriptparser.go b/vendor/github.com/a-h/templ/parser/v2/scriptparser.go index 2059a342b4..4483ab25d5 100644 --- a/vendor/github.com/a-h/templ/parser/v2/scriptparser.go +++ b/vendor/github.com/a-h/templ/parser/v2/scriptparser.go @@ -26,6 +26,7 @@ func (p scriptElementParser) Parse(pi *parse.Input) (n Node, ok bool, err error) if _, ok, err = lt.Parse(pi); err != nil || !ok { return } + openTagStart := pi.PositionAt(start) // Element name. e := &ScriptElement{} @@ -56,6 +57,7 @@ func (p scriptElementParser) Parse(pi *parse.Input) (n Node, ok bool, err error) pi.Seek(start) return n, false, parse.Error("