Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Command mode #4

Merged
merged 6 commits into from
Nov 9, 2023
Merged
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
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