Files
kitty/kittens/choose_files/cmd_preview.go
Kovid Goyal 97d8c2a230 ...
2025-12-05 19:56:55 +05:30

102 lines
2.6 KiB
Go

package choose_files
import (
"bytes"
"encoding/json"
"fmt"
"io/fs"
"os"
"os/exec"
"path/filepath"
"github.com/kovidgoyal/kitty/tools/icons"
"github.com/kovidgoyal/kitty/tools/utils/humanize"
"github.com/kovidgoyal/kitty/tools/utils/images"
"golang.org/x/sys/unix"
)
var _ = fmt.Print
const CMD_METADATA_KEY = "cmd-metadata.json"
type cmd_renderer struct {
cmdline []string
}
func (c cmd_renderer) String() string {
return c.cmdline[0]
}
type CmdResult struct {
Lines []string `json:"lines"`
Image string `json:"image"`
TitleExtra string `json:"title_extra"`
}
func (c cmd_renderer) Render(path string) (m map[string][]byte, mi metadata, img *images.ImageData, err error) {
cmdline := append(c.cmdline, path)
cmd := exec.Command(cmdline[0], cmdline[1:]...)
cmd.Stdin = nil
cmd.SysProcAttr = &unix.SysProcAttr{Setsid: true}
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err = cmd.Run(); err != nil {
err = fmt.Errorf("failed to run %v to read metadata from %s with error: %w and stderr: %s", c.cmdline, path, err, stderr.String())
return
}
var md CmdResult
if err = json.Unmarshal(stdout.Bytes(), &md); err != nil {
err = fmt.Errorf("could not decode JSON response from %v for %s: %w", c.cmdline, path, err)
}
if md.Image != "" {
var ip ImagePreviewRenderer
if m, mi, img, err = ip.Render(md.Image); err != nil {
return
}
}
mi.custom = &md
return
}
func (c cmd_renderer) Unmarshall(m map[string]string) (any, error) {
data, err := os.ReadFile(m[CMD_METADATA_KEY])
if err != nil {
return nil, err
}
var ans CmdResult
if err = json.Unmarshal(data, &ans); err != nil {
return nil, err
}
return &ans, nil
}
func (c cmd_renderer) ShowMetadata(h *Handler, s ShowData) (offset int) {
w := func(text string, center bool) {
if s.height > offset {
offset += h.render_wrapped_text_in_region(text, s.x, s.y+offset, s.width, s.height-offset, center)
}
}
ext := filepath.Ext(s.abspath)
r := s.custom_metadata.custom.(*CmdResult)
text := fmt.Sprintf("%s: %s%s", ext, humanize.Bytes(uint64(s.metadata.Size())), r.TitleExtra)
icon := icons.IconForPath(s.abspath)
w(icon+" "+text, true)
for _, line := range r.Lines {
w(line, false)
}
h.lp.QueueWriteString("\x1b[m") // reset SGR attributes
return
}
func NewCmdPreview(
abspath string, metadata fs.FileInfo, opts Settings, WakeupMainThread func() bool, p previewer,
) Preview {
c := cmd_renderer{p.cmdline}
if ans, err := NewImagePreview(abspath, metadata, opts, WakeupMainThread, c); err == nil {
return ans
} else {
return NewErrorPreview(err)
}
}