From e093b7bcad9df93e85d434a881ea8a3da1ce20c9 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Tue, 2 Apr 2024 14:41:42 -0300 Subject: [PATCH] feat: open (#60) * feat: open Signed-off-by: Carlos Alexandro Becker * chore: go 1.18 Signed-off-by: Carlos Alexandro Becker * build: fix go.work --------- Signed-off-by: Carlos Alexandro Becker --- .github/dependabot.yml | 9 +++++++++ .github/workflows/open.yml | 30 ++++++++++++++++++++++++++++++ .gitignore | 2 ++ exp/open/cmd/main.go | 12 ++++++++++++ exp/open/go.mod | 3 +++ exp/open/go.sum | 0 exp/open/open.go | 28 ++++++++++++++++++++++++++++ exp/open/open_unix.go | 28 ++++++++++++++++++++++++++++ exp/open/open_windows.go | 16 ++++++++++++++++ exp/teatest/go.mod | 4 ++-- exp/teatest/go.sum | 5 ++--- go.work | 3 ++- go.work.sum | 4 ++++ 13 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/open.yml create mode 100644 exp/open/cmd/main.go create mode 100644 exp/open/go.mod create mode 100644 exp/open/go.sum create mode 100644 exp/open/open.go create mode 100644 exp/open/open_unix.go create mode 100644 exp/open/open_windows.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8bd5eb6b..6d9ca3e0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -47,6 +47,15 @@ updates: commit-message: prefix: "chore" include: "scope" + - package-ecosystem: "gomod" + directory: "/exp/open" + schedule: + interval: "daily" + labels: + - "dependencies" + commit-message: + prefix: "chore" + include: "scope" - package-ecosystem: "gomod" directory: "/exp/ordered" schedule: diff --git a/.github/workflows/open.yml b/.github/workflows/open.yml new file mode 100644 index 00000000..aadd4e78 --- /dev/null +++ b/.github/workflows/open.yml @@ -0,0 +1,30 @@ +# auto-generated by scripts/dependabot. DO NOT EDIT. +name: open + +on: + push: + branches: + - main + pull_request: + paths: + - exp/open/** + - .github/workflows/open.yml + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: ./exp/open + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./exp/open/go.mod + cache: true + cache-dependency-path: ./exp/open.sum + - run: go build -v ./... + - run: go test -race -v ./... diff --git a/.gitignore b/.gitignore index 66fd13c9..2ce36c19 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ + +!go.work diff --git a/exp/open/cmd/main.go b/exp/open/cmd/main.go new file mode 100644 index 00000000..8369c937 --- /dev/null +++ b/exp/open/cmd/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "context" + "fmt" + + "github.com/charmbracelet/x/exp/open" +) + +func main() { + fmt.Println(open.Open(context.Background(), "https://charm.sh")) +} diff --git a/exp/open/go.mod b/exp/open/go.mod new file mode 100644 index 00000000..c7a5278f --- /dev/null +++ b/exp/open/go.mod @@ -0,0 +1,3 @@ +module github.com/charmbracelet/x/exp/open + +go 1.18 diff --git a/exp/open/go.sum b/exp/open/go.sum new file mode 100644 index 00000000..e69de29b diff --git a/exp/open/open.go b/exp/open/open.go new file mode 100644 index 00000000..02a1a5bf --- /dev/null +++ b/exp/open/open.go @@ -0,0 +1,28 @@ +package open + +import ( + "context" + "errors" + "fmt" +) + +// ErrNotSupported occurs when no ways to open a file are found. +var ErrNotSupported = errors.New("not supported") + +// Open the given input. +func Open(ctx context.Context, input string) error { + return With(ctx, "", input) +} + +// With opens the given input using the given app. +func With(ctx context.Context, app, input string) error { + cmd := buildCmd(ctx, app, input) + if cmd == nil { + return ErrNotSupported + } + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("open: %w: %s", err, string(out)) + } + return nil +} diff --git a/exp/open/open_unix.go b/exp/open/open_unix.go new file mode 100644 index 00000000..ad9ea9b7 --- /dev/null +++ b/exp/open/open_unix.go @@ -0,0 +1,28 @@ +//go:build !windows +// +build !windows + +package open + +import ( + "context" + "os/exec" +) + +func buildCmd(ctx context.Context, app, path string) *exec.Cmd { + if _, err := exec.LookPath("open"); err == nil { + var arg []string + if app != "" { + arg = append(arg, "-a", app) + } + arg = append(arg, path) + return exec.CommandContext(ctx, "open", arg...) + } + + if _, err := exec.LookPath("xdg-open"); err == nil { + if app == "" { + return exec.CommandContext(ctx, app, path) + } + return exec.CommandContext(ctx, "xdg-open", path) + } + return nil +} diff --git a/exp/open/open_windows.go b/exp/open/open_windows.go new file mode 100644 index 00000000..1fb994aa --- /dev/null +++ b/exp/open/open_windows.go @@ -0,0 +1,16 @@ +//go:build windows +// +build windows + +package open + +import ( + "context" + "os/exec" +) + +func buildCmd(ctx context.Context, app, path string) *exec.Cmd { + if app != "" { + return exec.Command("cmd", "/C", "start", "", app, path) + } + return exec.CommandContext(ctx, "rundll32", "url.dll,FileProtocolHandler", path) +} diff --git a/exp/teatest/go.mod b/exp/teatest/go.mod index d4460e17..2f410ddd 100644 --- a/exp/teatest/go.mod +++ b/exp/teatest/go.mod @@ -19,9 +19,9 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.2 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.6.0 // indirect golang.org/x/text v0.3.8 // indirect ) diff --git a/exp/teatest/go.sum b/exp/teatest/go.sum index f16eb29d..26071cd1 100644 --- a/exp/teatest/go.sum +++ b/exp/teatest/go.sum @@ -26,14 +26,13 @@ github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKt github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= diff --git a/go.work b/go.work index 5dc5d56f..49a55e17 100644 --- a/go.work +++ b/go.work @@ -1,10 +1,11 @@ -go 1.21 +go 1.22 use ( ./editor ./errors ./exp/golden ./exp/higherorder + ./exp/open ./exp/ordered ./exp/slice ./exp/strings diff --git a/go.work.sum b/go.work.sum index 7bcd8737..bb774b85 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,8 +1,11 @@ +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= @@ -11,3 +14,4 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=