Skip to content

Commit

Permalink
Merge pull request #4 from ksysoev/command_mode
Browse files Browse the repository at this point in the history
Command mode
  • Loading branch information
ksysoev authored Nov 9, 2023
2 parents 35973c0 + 7f9aa4d commit 3353d33
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 50 deletions.
50 changes: 30 additions & 20 deletions pkg/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import (
)

const (
HistoryFilename = ".wsget_history"
CommandsLimit = 100
HistoryLimit = 100
HistoryFilename = ".wsget_history"
HistoryCmdFilename = ".wsget_cmd_history"
CommandsLimit = 100
HistoryLimit = 100

MacOSDeleteKey = 127

Expand All @@ -26,12 +27,13 @@ const (
)

type CLI struct {
formater *formater.Formater
wsConn *ws.Connection
editor *Editor
input Inputer
output io.Writer
commands chan Executer
formater *formater.Formater
wsConn *ws.Connection
editor *Editor
cmdEditor *Editor
input Inputer
output io.Writer
commands chan Executer
}

type RunOptions struct {
Expand All @@ -53,16 +55,18 @@ func NewCLI(wsConn *ws.Connection, input Inputer, output io.Writer) (*CLI, error
homeDir := currentUser.HomeDir

history := NewHistory(homeDir+"/"+HistoryFilename, HistoryLimit)
cmdHistory := NewHistory(homeDir+"/"+HistoryCmdFilename, HistoryLimit)

commands := make(chan Executer, CommandsLimit)

return &CLI{
formater: formater.NewFormatter(),
editor: NewEditor(output, history),
wsConn: wsConn,
input: input,
output: output,
commands: commands,
formater: formater.NewFormatter(),
editor: NewEditor(output, history, false),
cmdEditor: NewEditor(output, cmdHistory, true),
wsConn: wsConn,
input: input,
output: output,
commands: commands,
}, nil
}

Expand Down Expand Up @@ -92,11 +96,8 @@ func (c *CLI) Run(opts RunOptions) error {

exCtx := &ExecutionContext{
input: keysEvents,
output: c.output,
editor: c.editor,
wsConn: c.wsConn,
cli: c,
outputFile: opts.OutputFile,
formater: c.formater,
}

for {
Expand All @@ -116,7 +117,16 @@ func (c *CLI) Run(opts RunOptions) error {
case keyboard.KeyEnter:
c.commands <- NewCommandEdit("")
default:
continue
if event.Key > 0 {
continue
}

switch event.Rune {
case ':':
c.commands <- NewCommandCmdEdit()
default:
continue
}
}

case msg, ok := <-c.wsConn.Messages:
Expand Down
68 changes: 46 additions & 22 deletions pkg/cli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/eiannone/keyboard"
"github.com/fatih/color"
"github.com/ksysoev/wsget/pkg/formater"
"github.com/ksysoev/wsget/pkg/ws"
)

Expand All @@ -19,21 +18,14 @@ const (

type ExecutionContext struct {
input <-chan keyboard.KeyEvent
output io.Writer
editor *Editor
wsConn *ws.Connection
cli *CLI
outputFile io.Writer
formater *formater.Formater
}

type Executer interface {
Execute(*ExecutionContext) (Executer, error)
}

type CommandEdit struct {
content string
}

func CommandFactory(raw string) (Executer, error) {
if raw == "" {
return nil, fmt.Errorf("empty command")
Expand Down Expand Up @@ -76,17 +68,21 @@ func CommandFactory(raw string) (Executer, error) {
}
}

type CommandEdit struct {
content string
}

func NewCommandEdit(content string) *CommandEdit {
return &CommandEdit{content}
}

func (c *CommandEdit) Execute(exCtx *ExecutionContext) (Executer, error) {
color.New(color.FgGreen).Fprint(exCtx.output, "->\n")
color.New(color.FgGreen).Fprint(exCtx.cli.output, "->\n")

fmt.Fprint(exCtx.output, ShowCursor)
req, err := exCtx.editor.EditRequest(exCtx.input, c.content)
fmt.Fprint(exCtx.output, LineUp+LineClear)
fmt.Fprint(exCtx.output, HideCursor)
fmt.Fprint(exCtx.cli.output, ShowCursor)
req, err := exCtx.cli.editor.EditRequest(exCtx.input, c.content)
fmt.Fprint(exCtx.cli.output, LineUp+LineClear)
fmt.Fprint(exCtx.cli.output, HideCursor)

if err != nil || req == "" {
return nil, err
Expand All @@ -104,7 +100,7 @@ func NewCommandSend(request string) *CommandSend {
}

func (c *CommandSend) Execute(exCtx *ExecutionContext) (Executer, error) {
msg, err := exCtx.wsConn.Send(c.request)
msg, err := exCtx.cli.wsConn.Send(c.request)
if err != nil {
return nil, fmt.Errorf("fail to send request: %s", err)
}
Expand All @@ -122,25 +118,25 @@ func NewCommandPrintMsg(msg ws.Message) *CommandPrintMsg {

func (c *CommandPrintMsg) Execute(exCtx *ExecutionContext) (Executer, error) {
msg := c.msg
output, err := exCtx.formater.FormatMessage(msg)
output, err := exCtx.cli.formater.FormatMessage(msg)

if err != nil {
return nil, fmt.Errorf("fail to format for output file: %s, data: %q", err, msg.Data)
}

switch msg.Type {
case ws.Request:
color.New(color.FgGreen).Fprint(exCtx.output, "->\n")
color.New(color.FgGreen).Fprint(exCtx.cli.output, "->\n")
case ws.Response:
color.New(color.FgRed).Fprint(exCtx.output, "<-\n")
color.New(color.FgRed).Fprint(exCtx.cli.output, "<-\n")
default:
return nil, fmt.Errorf("unknown message type: %s, data: %q", msg.Type, msg.Data)
}

fmt.Fprintf(exCtx.output, "%s\n", output)
fmt.Fprintf(exCtx.cli.output, "%s\n", output)

if exCtx.outputFile != nil {
output, err := exCtx.formater.FormatForFile(msg)
output, err := exCtx.cli.formater.FormatForFile(msg)
if err != nil {
return nil, fmt.Errorf("fail to write to output file: %s", err)
}
Expand Down Expand Up @@ -171,7 +167,7 @@ func NewCommandWaitForResp(timeout time.Duration) *CommandWaitForResp {

func (c *CommandWaitForResp) Execute(exCtx *ExecutionContext) (Executer, error) {
if c.timeout.Seconds() == 0 {
msg, ok := <-exCtx.wsConn.Messages
msg, ok := <-exCtx.cli.wsConn.Messages
if !ok {
return nil, fmt.Errorf("connection closed")
}
Expand All @@ -182,11 +178,39 @@ func (c *CommandWaitForResp) Execute(exCtx *ExecutionContext) (Executer, error)
select {
case <-time.After(c.timeout):
return nil, fmt.Errorf("timeout")
case msg, ok := <-exCtx.wsConn.Messages:
case msg, ok := <-exCtx.cli.wsConn.Messages:
if !ok {
return nil, fmt.Errorf("connection closed")
}

return NewCommandPrintMsg(msg), nil
}
}

type CommandCmdEdit struct{}

func NewCommandCmdEdit() *CommandCmdEdit {
return &CommandCmdEdit{}
}

func (c *CommandCmdEdit) Execute(exCtx *ExecutionContext) (Executer, error) {
fmt.Fprint(exCtx.cli.output, ":")

fmt.Fprint(exCtx.cli.output, ShowCursor)
rawCmd, err := exCtx.cli.cmdEditor.EditRequest(exCtx.input, "")
fmt.Fprint(exCtx.cli.output, LineClear+"\r")
fmt.Fprint(exCtx.cli.output, HideCursor)

if err != nil {
return nil, err
}

cmd, err := CommandFactory(rawCmd)

if err != nil {
color.New(color.FgRed).Fprintln(exCtx.cli.output, err)
return nil, nil
}

return cmd, nil
}
15 changes: 13 additions & 2 deletions pkg/cli/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

const (
PastingTimingThresholdInMicrosec = 250
ErrInterrupted = "interrupted"
)

type Editor struct {
Expand All @@ -19,16 +20,18 @@ type Editor struct {
prevPressedTime time.Time
buffer []rune
pos int
isSingleLine bool
}

func NewEditor(output io.Writer, history *History) *Editor {
func NewEditor(output io.Writer, history *History, isSingleLine bool) *Editor {
return &Editor{
History: history,
content: NewContent(),
buffer: make([]rune, 0),
pos: 0,
output: output,
prevPressedTime: time.Now(),
isSingleLine: isSingleLine,
}
}

Expand All @@ -45,7 +48,7 @@ func (ed *Editor) EditRequest(keyStream <-chan keyboard.KeyEvent, initBuffer str

switch e.Key {
case keyboard.KeyCtrlC, keyboard.KeyCtrlD:
return "", fmt.Errorf("interrupted")
return "", fmt.Errorf(ErrInterrupted)
case keyboard.KeyCtrlS:
return ed.done()
case keyboard.KeyEsc:
Expand Down Expand Up @@ -74,6 +77,10 @@ func (ed *Editor) EditRequest(keyStream <-chan keyboard.KeyEvent, initBuffer str
continue
}

if ed.isSingleLine && e.Rune == '\n' {
continue
}

fmt.Fprint(ed.output, ed.content.InsertSymbol(e.Rune))
}
}
Expand Down Expand Up @@ -120,6 +127,10 @@ func (ed *Editor) nextFromHistory() {
func (ed *Editor) newLineOrDone(isPasting bool) (isDone bool) {
prev := ed.content.PrevSymbol()

if ed.isSingleLine {
return true
}

isDone = prev != '\\'
if !isDone {
fmt.Fprint(ed.output, ed.content.RemoveSymbol())
Expand Down
12 changes: 6 additions & 6 deletions pkg/cli/editor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
func TestNewEditor(t *testing.T) {
output := new(bytes.Buffer)
history := NewHistory("", 0)
editor := NewEditor(output, history)
editor := NewEditor(output, history, false)

if editor.content == nil {
t.Error("Expected non-nil content")
Expand Down Expand Up @@ -44,7 +44,7 @@ func TestEditRequest(t *testing.T) {

output := new(bytes.Buffer)
history := NewHistory(tmpfile.Name(), 5)
editor := NewEditor(output, history)
editor := NewEditor(output, history, false)

keyStream := make(chan keyboard.KeyEvent)
defer close(keyStream)
Expand Down Expand Up @@ -78,7 +78,7 @@ func TestEditRequestInterrupted(t *testing.T) {

output := new(bytes.Buffer)
history := NewHistory(tmpfile.Name(), 5)
editor := NewEditor(output, history)
editor := NewEditor(output, history, false)

keyStream := make(chan keyboard.KeyEvent)
defer close(keyStream)
Expand Down Expand Up @@ -122,7 +122,7 @@ func TestEditRequestExitEditor(t *testing.T) {

output := new(bytes.Buffer)
history := NewHistory(tmpfile.Name(), 5)
editor := NewEditor(output, history)
editor := NewEditor(output, history, false)

keyStream := make(chan keyboard.KeyEvent)
defer close(keyStream)
Expand Down Expand Up @@ -152,7 +152,7 @@ func TestEditRequestClosingKeyboard(t *testing.T) {

output := new(bytes.Buffer)
history := NewHistory(tmpfile.Name(), 5)
editor := NewEditor(output, history)
editor := NewEditor(output, history, false)

keyStream := make(chan keyboard.KeyEvent)
close(keyStream)
Expand All @@ -178,7 +178,7 @@ func TestEditRequestSpecialKeys(t *testing.T) {

output := new(bytes.Buffer)
history := NewHistory(tmpfile.Name(), 5)
editor := NewEditor(output, history)
editor := NewEditor(output, history, false)

keyStream := make(chan keyboard.KeyEvent)

Expand Down

0 comments on commit 3353d33

Please sign in to comment.