Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions oviewer/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ func (root *Root) event(ctx context.Context, ev tcell.Event) bool {
root.setMultiColor(ev.value)
case *eventSaveBuffer:
root.saveBuffer(ev.value)
case *eventPipeBuffer:
root.pipeBuffer(ev.value)
case *eventInputSearch:
root.firstSearch(ctx, ev.searchType)
case *eventSkipLines:
Expand Down
3 changes: 3 additions & 0 deletions oviewer/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ const (
JumpTarget
// SaveBuffer is for saving the buffer.
SaveBuffer
// PipeBuffer is for piping the buffer to a command.
PipeBuffer
// SectionNum is for setting the section number.
SectionNum
// ConvertType is for setting the convert type.
Expand Down Expand Up @@ -91,6 +93,7 @@ func NewInput() *Input {
i.Candidate[MultiColor] = multiColorCandidate()
i.Candidate[JumpTarget] = jumpTargetCandidate()
i.Candidate[SaveBuffer] = blankCandidate()
i.Candidate[PipeBuffer] = blankCandidate()
i.Candidate[ConvertType] = converterCandidate()

i.Event = &eventNormal{}
Expand Down
53 changes: 53 additions & 0 deletions oviewer/input_pipebuffer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package oviewer

import (
"context"

"github.com/gdamore/tcell/v3"
)

// inputPipeBuffer is a wrapper to move to setPipeBufferMode.
func (root *Root) inputPipeBuffer(_ context.Context) {
input := root.input
input.reset()
input.Event = newPipeBufferEvent(input.Candidate[PipeBuffer])
}

// eventPipeBuffer represents the input event for pipe buffer mode.
type eventPipeBuffer struct {
tcell.EventTime
clist *candidate
value string
}

// newPipeBufferEvent returns an eventPipeBuffer for pipe buffer mode.
func newPipeBufferEvent(clist *candidate) *eventPipeBuffer {
return &eventPipeBuffer{clist: clist}
}

// Mode returns InputMode.
func (*eventPipeBuffer) Mode() InputMode {
return PipeBuffer
}

// Prompt returns the prompt string in the input field.
func (*eventPipeBuffer) Prompt() string {
return "(Pipe)command:"
}

// Confirm returns the event when the input is confirmed.
func (e *eventPipeBuffer) Confirm(str string) tcell.Event {
e.value = str
e.SetEventNow()
return e
}

// Up returns strings when the up key is pressed during input.
func (e *eventPipeBuffer) Up(_ string) string {
return e.clist.up()
}

// Down returns strings when the down key is pressed during input.
func (e *eventPipeBuffer) Down(_ string) string {
return e.clist.down()
}
4 changes: 4 additions & 0 deletions oviewer/keybind.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ const (
actionJumpTarget = "jump_target"
actionMultiColor = "multi_color"
actionSaveBuffer = "save_buffer"
actionPipeBuffer = "pipe_buffer"
actionSearch = "search"
actionBackSearch = "backsearch"
actionFilter = "filter"
Expand Down Expand Up @@ -188,6 +189,7 @@ func (root *Root) handlers() map[string]func(context.Context) {
actionJumpTarget: root.inputJumpTarget,
actionMultiColor: root.inputMultiColor,
actionSaveBuffer: root.inputSaveBuffer,
actionPipeBuffer: root.inputPipeBuffer,
actionSearch: root.inputForwardSearch,
actionBackSearch: root.inputBackSearch,
actionFilter: root.inputSearchFilter,
Expand Down Expand Up @@ -293,6 +295,7 @@ func defaultKeyBinds() KeyBind {
actionJumpTarget: {"j"},
actionMultiColor: {"."},
actionSaveBuffer: {"S"},
actionPipeBuffer: {"|"},
actionSearch: {"/"},
actionBackSearch: {"?"},
actionFilter: {"&"},
Expand Down Expand Up @@ -337,6 +340,7 @@ func (k KeyBind) String() string {
k.writeKeyBind(&b, actionFollowAll, "follow all mode toggle")
k.writeKeyBind(&b, actionToggleMouse, "enable/disable mouse")
k.writeKeyBind(&b, actionSaveBuffer, "save buffer to file")
k.writeKeyBind(&b, actionPipeBuffer, "pipe buffer to command")

writeHeader(&b, "Moving")
k.writeKeyBind(&b, actionMoveDown, "forward by one line")
Expand Down
108 changes: 108 additions & 0 deletions oviewer/pipe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package oviewer

import (
"fmt"
"log"
"os"
"os/exec"
"runtime"
"strings"

"golang.org/x/term"
)

// pipeBuffer pipes the buffer content to the specified command.
func (root *Root) pipeBuffer(input string) {
cmdStr := strings.TrimSpace(input)
if cmdStr == "" {
root.setMessage("pipe command is empty")
return
}

log.Printf("Pipe buffer to command: %s\n", cmdStr)
if err := root.Screen.Suspend(); err != nil {
root.setMessageLog(err.Error())
return
}
defer func() {
log.Println("Resume from pipe")
if err := root.Screen.Resume(); err != nil {
log.Println(err)
}
}()

if err := root.runPipeCommand(cmdStr); err != nil {
fmt.Fprintf(os.Stderr, "pipe command error: %s\n", err)
}

fmt.Println("press any key to continue...")
if err := waitForKey(); err != nil {
log.Printf("waitForKey error: %s\n", err)
}
}

// runPipeCommand runs the command with buffer content as stdin.
func (root *Root) runPipeCommand(cmdStr string) error {
shell := os.Getenv("SHELL")
if shell == "" {
shell = getShell()
}

var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.Command("cmd", "/c", cmdStr)
} else {
cmd = exec.Command(shell, "-c", cmdStr)
}

stdin, err := cmd.StdinPipe()
if err != nil {
return fmt.Errorf("failed to get stdin pipe: %w", err)
}

cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start command: %w", err)
}

// Export buffer content to the command's stdin.
if root.Doc.PlainMode {
if err := root.Doc.ExportPlain(stdin, root.Doc.BufStartNum(), root.Doc.BufEndNum()); err != nil {
stdin.Close()
return fmt.Errorf("failed to export buffer: %w", err)
}
} else {
if err := root.Doc.Export(stdin, root.Doc.BufStartNum(), root.Doc.BufEndNum()); err != nil {
stdin.Close()
return fmt.Errorf("failed to export buffer: %w", err)
}
}
stdin.Close()

if err := cmd.Wait(); err != nil {
return fmt.Errorf("command failed: %w", err)
}

return nil
}

// waitForKey waits for the user to press any key.
func waitForKey() error {
tty, err := getTTY()
if err != nil {
return err
}
defer tty.Close()

oldState, err := term.MakeRaw(int(tty.Fd()))
if err != nil {
return err
}
defer term.Restore(int(tty.Fd()), oldState)

buf := make([]byte, 1)
_, err = tty.Read(buf)
return err
}