diff --git a/pkg/template/template.go b/pkg/template/template.go index 4151646..657006b 100644 --- a/pkg/template/template.go +++ b/pkg/template/template.go @@ -25,6 +25,7 @@ const ( // Template is the representation of a template. type Template struct { colorEnabled bool + isTTY bool output io.Writer tmpl *template.Template tp tableprinter.TablePrinter @@ -32,9 +33,10 @@ type Template struct { } // New initializes a Template. -func New(w io.Writer, width int, colorEnabled bool) Template { +func New(w io.Writer, width int, colorEnabled, isTTY bool) Template { return Template{ colorEnabled: colorEnabled, + isTTY: isTTY, output: w, tp: tableprinter.New(w, true, width), width: width, @@ -47,6 +49,7 @@ func (t *Template) Parse(tmpl string) error { templateFuncs := map[string]interface{}{ "autocolor": colorFunc, "color": colorFunc, + "hyperlink": hyperlinkFunc(t.isTTY), "join": joinFunc, "pluck": pluckFunc, "tablerender": func() (string, error) { @@ -233,3 +236,19 @@ func truncateMultiline(maxWidth int, s string) string { } return text.Truncate(maxWidth, s) } + +func hyperlinkFunc(isTTY bool) func(string, string) string { + if !isTTY { + return func(link, text string) string { + return link + } + } + + return func(link, text string) string { + if text == "" { + text = link + } + + return fmt.Sprintf("\x1b]8;;%s\x1b\\%s\x1b]8;;\x1b\\", link, text) + } +} diff --git a/pkg/template/template_test.go b/pkg/template/template_test.go index 0f9e5ae..ec8dea2 100644 --- a/pkg/template/template_test.go +++ b/pkg/template/template_test.go @@ -17,13 +17,14 @@ import ( func ExampleTemplate() { // Information about the terminal can be obtained using the [pkg/term] package. colorEnabled := true + isTTY := true termWidth := 14 json := strings.NewReader(heredoc.Doc(`[ {"number": 1, "title": "One"}, {"number": 2, "title": "Two"} ]`)) template := "HEADER\n\n{{range .}}{{tablerow .number .title}}{{end}}{{tablerender}}\nFOOTER" - tmpl := New(os.Stdout, termWidth, colorEnabled) + tmpl := New(os.Stdout, termWidth, colorEnabled, isTTY) if err := tmpl.Parse(template); err != nil { log.Fatal(err) } @@ -100,6 +101,7 @@ func TestExecute(t *testing.T) { json io.Reader template string colorize bool + isTTY bool } tests := []struct { name string @@ -322,11 +324,45 @@ func TestExecute(t *testing.T) { }, wantErr: true, }, + { + name: "hyperlink enabled", + args: args{ + isTTY: true, + json: strings.NewReader(`{"link":"https://github.com"}`), + template: `{{ hyperlink .link "" }}`, + }, + wantW: "\x1b]8;;https://github.com\x1b\\https://github.com\x1b]8;;\x1b\\", + }, + { + name: "hyperlink disabled", + args: args{ + json: strings.NewReader(`{"link":"https://github.com"}`), + template: `{{ hyperlink .link "" }}`, + }, + wantW: "https://github.com", + }, + { + name: "hyperlink enabled", + args: args{ + isTTY: true, + json: strings.NewReader(`{"link":"https://github.com","text":"GitHub"}`), + template: `{{ hyperlink .link .text }}`, + }, + wantW: "\x1b]8;;https://github.com\x1b\\GitHub\x1b]8;;\x1b\\", + }, + { + name: "hyperlink disabled", + args: args{ + json: strings.NewReader(`{"link":"https://github.com","text":"GitHub"}`), + template: `{{ hyperlink .link .text }}`, + }, + wantW: "https://github.com", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { w := &bytes.Buffer{} - tmpl := New(w, 80, tt.args.colorize) + tmpl := New(w, 80, tt.args.colorize, tt.args.isTTY) err := tmpl.Parse(tt.args.template) assert.NoError(t, err) err = tmpl.Execute(tt.args.json)