|
@@ -0,0 +1,549 @@
|
|
|
+package hhccli
|
|
|
+
|
|
|
+import (
|
|
|
+ "bufio"
|
|
|
+ "context"
|
|
|
+ "fmt"
|
|
|
+ "git.swzry.com/zry/go-hhc-cli/hhc_ast"
|
|
|
+ "git.swzry.com/zry/go-hhc-cli/hhc_common"
|
|
|
+ "git.swzry.com/zry/ztermtab"
|
|
|
+ "io"
|
|
|
+ "runtime"
|
|
|
+ "unicode"
|
|
|
+ "unicode/utf8"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ TermInteractiveLogLevel_Fatal TerminalInteractiveDebugLogLevel = 0
|
|
|
+ TermInteractiveLogLevel_Error TerminalInteractiveDebugLogLevel = 1
|
|
|
+ TermInteractiveLogLevel_Warn TerminalInteractiveDebugLogLevel = 2
|
|
|
+ TermInteractiveLogLevel_Info TerminalInteractiveDebugLogLevel = 3
|
|
|
+ TermInteractiveLogLevel_Debug TerminalInteractiveDebugLogLevel = 4
|
|
|
+)
|
|
|
+
|
|
|
+func (lv TerminalInteractiveDebugLogLevel) Name() string {
|
|
|
+ switch lv {
|
|
|
+ case TermInteractiveLogLevel_Fatal:
|
|
|
+ return "FATAL"
|
|
|
+ case TermInteractiveLogLevel_Error:
|
|
|
+ return "ERROR"
|
|
|
+ case TermInteractiveLogLevel_Warn:
|
|
|
+ return "WARN"
|
|
|
+ case TermInteractiveLogLevel_Info:
|
|
|
+ return "INFO"
|
|
|
+ case TermInteractiveLogLevel_Debug:
|
|
|
+ return "DEBUG"
|
|
|
+ default:
|
|
|
+ return "???"
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type TerminalInteractiveDebugLogLevel uint8
|
|
|
+
|
|
|
+type TerminalInteractiveDebugLog func(lv TerminalInteractiveDebugLogLevel, msg ...interface{})
|
|
|
+
|
|
|
+type TerminalInteractive struct {
|
|
|
+ inStream io.Reader
|
|
|
+ outStream io.Writer
|
|
|
+ errStream io.Writer
|
|
|
+ debugWrite TerminalInteractiveDebugLog
|
|
|
+ termWidth int
|
|
|
+ inBufReader *bufio.Reader
|
|
|
+ errCh chan error
|
|
|
+ runeCh chan rune
|
|
|
+ printResultRuneCh chan rune
|
|
|
+ currentPrompt string
|
|
|
+ views map[string]CliView
|
|
|
+ currentView CliView
|
|
|
+ termState hhc_common.TerminalState
|
|
|
+ parser *hhc_ast.SDTLineParser
|
|
|
+ runesEditor *hhc_common.RunesEditor
|
|
|
+ backspaceRune rune
|
|
|
+ tcesParser *SimpleTCESParser
|
|
|
+ printModePrintLines []string
|
|
|
+ printModeLineCount int
|
|
|
+ printModeCurrentLine int
|
|
|
+ printModeIsHelp bool
|
|
|
+ execModeContext *hhc_ast.ExecContext
|
|
|
+}
|
|
|
+
|
|
|
+type TerminalHandlerInterface interface {
|
|
|
+ Reset(iow io.Writer, promptStr string, sdtp *hhc_ast.SDTLineParser)
|
|
|
+ ProcessRune(r rune) (end bool, reprint bool)
|
|
|
+ GetCmdLinePrintable(termWidth int) []string
|
|
|
+ GetCalculatedCleanLineCount() int
|
|
|
+}
|
|
|
+
|
|
|
+type TerminalInteractiveConfig struct {
|
|
|
+ InputStream io.Reader
|
|
|
+ OutputStream io.Writer
|
|
|
+ ErrorStream io.Writer
|
|
|
+ InitialTerminalWidth int
|
|
|
+ DebugLogFunction TerminalInteractiveDebugLog
|
|
|
+ InitialPrompt string
|
|
|
+ BackspaceRune rune
|
|
|
+}
|
|
|
+
|
|
|
+func nullDebugLogFunc(lv TerminalInteractiveDebugLogLevel, msg ...interface{}) {
|
|
|
+}
|
|
|
+
|
|
|
+func NewTerminalInteractive(cfg TerminalInteractiveConfig) *TerminalInteractive {
|
|
|
+ ti := &TerminalInteractive{
|
|
|
+ inStream: cfg.InputStream,
|
|
|
+ outStream: cfg.OutputStream,
|
|
|
+ errStream: cfg.ErrorStream,
|
|
|
+ termWidth: cfg.InitialTerminalWidth,
|
|
|
+ inBufReader: bufio.NewReader(cfg.InputStream),
|
|
|
+ currentPrompt: cfg.InitialPrompt,
|
|
|
+ views: map[string]CliView{},
|
|
|
+ termState: hhc_common.TerminalState_Idle,
|
|
|
+ backspaceRune: cfg.BackspaceRune,
|
|
|
+ tcesParser: NewSimpleTCESParser(),
|
|
|
+ }
|
|
|
+ if cfg.DebugLogFunction == nil {
|
|
|
+ ti.debugWrite = nullDebugLogFunc
|
|
|
+ } else {
|
|
|
+ ti.debugWrite = cfg.DebugLogFunction
|
|
|
+ }
|
|
|
+ if ti.currentPrompt == "" {
|
|
|
+ ti.currentPrompt = ">"
|
|
|
+ }
|
|
|
+ if ti.backspaceRune == 0 {
|
|
|
+ ti.backspaceRune = '\x7F'
|
|
|
+ }
|
|
|
+ return ti
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) RegisterView(viewClassName string, view CliView) {
|
|
|
+ ti.views[viewClassName] = view
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) SetCurrentView(viewClassName string) error {
|
|
|
+ v, ok := ti.views[viewClassName]
|
|
|
+ if ok {
|
|
|
+ ti.currentView = v
|
|
|
+ } else {
|
|
|
+ ti.currentView = nil
|
|
|
+ return fmt.Errorf("invalid vcn")
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) readRunesRoutine() {
|
|
|
+ for {
|
|
|
+ rr, _, err := ti.inBufReader.ReadRune()
|
|
|
+ if err != nil {
|
|
|
+ ti.errCh <- err
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ti.runeCh <- rr
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) SetPrompt(p string) {
|
|
|
+ ti.currentPrompt = p
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) Run(ctx context.Context) error {
|
|
|
+ ti.errCh = make(chan error)
|
|
|
+ ti.runeCh = make(chan rune)
|
|
|
+ go ti.readRunesRoutine()
|
|
|
+ err := ti.gotoStateInput(false)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ for {
|
|
|
+ select {
|
|
|
+ case <-ctx.Done():
|
|
|
+ ti.debugWrite(TermInteractiveLogLevel_Info, "canceled by user")
|
|
|
+ return nil
|
|
|
+ case err := <-ti.errCh:
|
|
|
+ ti.debugWrite(TermInteractiveLogLevel_Error, err.Error())
|
|
|
+ return err
|
|
|
+ case rr := <-ti.runeCh:
|
|
|
+ err := ti.stateMachinePushRune(rr)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) stateMachinePushRune(r rune) error {
|
|
|
+ for {
|
|
|
+ switch ti.termState {
|
|
|
+ case hhc_common.TerminalState_Idle:
|
|
|
+ return ti.gotoStateInput(false)
|
|
|
+ case hhc_common.TerminalState_Input:
|
|
|
+ b, err := ti.stateInputPushRune(r)
|
|
|
+ if b || err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ case hhc_common.TerminalState_TCES:
|
|
|
+ b, err := ti.stateTCESPushRune(r)
|
|
|
+ if b || err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ case hhc_common.TerminalState_Execute:
|
|
|
+ if ti.execModeContext != nil {
|
|
|
+ ti.execModeContext.WriteRune(r)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ case hhc_common.TerminalState_PrintResult:
|
|
|
+ b, err := ti.statePrintResultPushRune(r)
|
|
|
+ if b || err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ return fmt.Errorf("invalid state for terminal handler state machine: %d", ti.termState)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) gotoStateInput(saveHistory bool) error {
|
|
|
+ if ti.currentView == nil {
|
|
|
+ return fmt.Errorf("no current view specified")
|
|
|
+ }
|
|
|
+ sdtf := ti.currentView.GetSDTRoot()
|
|
|
+ sdtw := hhc_ast.NewSDTWalker(sdtf)
|
|
|
+ ti.parser = hhc_ast.NewSDTLineParser(sdtw)
|
|
|
+ if ti.runesEditor == nil {
|
|
|
+ ti.runesEditor = hhc_common.NewRunesEditor()
|
|
|
+ } else {
|
|
|
+ ti.runesEditor.ClearCurrentLine(saveHistory)
|
|
|
+ }
|
|
|
+ _, _ = fmt.Fprint(ti.outStream, "\r\n")
|
|
|
+ ti.runesEditor.PeekIntoTerminal(ti.currentPrompt, ti.termWidth, ti.outStream)
|
|
|
+ ti.termState = hhc_common.TerminalState_Input
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) gotoStateTCES() error {
|
|
|
+ ti.termState = hhc_common.TerminalState_TCES
|
|
|
+ ti.tcesParser.ResetState()
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) gotoStatePrintResult(s []string, isHelp bool) {
|
|
|
+ ti.printModeIsHelp = isHelp
|
|
|
+ ti.printModeLineCount = len(s)
|
|
|
+ ti.printModeCurrentLine = 0
|
|
|
+ ti.printModePrintLines = s
|
|
|
+ ti.termState = hhc_common.TerminalState_PrintResult
|
|
|
+ _, _ = fmt.Fprint(ti.errStream, "---- More ----")
|
|
|
+ ti.printLinesInPrintResultState(20)
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) gotoStateExecute(ef hhc_ast.ExecuteFunc, ctx *hhc_ast.SDTWalkContext) {
|
|
|
+ ti.termState = hhc_common.TerminalState_Execute
|
|
|
+ ti.execModeContext = hhc_ast.WrapNewExecContext(ctx, ti.outStream, ti.errStream, ti.termWidth)
|
|
|
+ go ti.executeRoutine(ef)
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) exitStateExecute() {
|
|
|
+ if ti.execModeContext != nil {
|
|
|
+ nv := ti.execModeContext.GetNextView()
|
|
|
+ if nv != "" {
|
|
|
+ err := ti.SetCurrentView(nv)
|
|
|
+ if err != nil {
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, "\r\n %% command line system error in change view: %s\r\n", err.Error())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ wcp, np := ti.execModeContext.GetNextPrompt()
|
|
|
+ if wcp {
|
|
|
+ ti.SetPrompt(np)
|
|
|
+ }
|
|
|
+ if ti.execModeContext.WillGotoPrintResultMode() {
|
|
|
+ res := ti.execModeContext.GetResultPrintLines()
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, "\r\n")
|
|
|
+ ti.gotoStatePrintResult(res, false)
|
|
|
+ } else {
|
|
|
+ err := ti.gotoStateInput(false)
|
|
|
+ if err != nil {
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, "\r\n %% command line system error: %s\r\n", err.Error())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ti.execModeContext = nil
|
|
|
+ } else {
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, "\r\n %% broken command (this is a bug... or may be a feature?)\r\n")
|
|
|
+ err := ti.gotoStateInput(false)
|
|
|
+ if err != nil {
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, "\r\n %% command line system error: %s\r\n", err.Error())
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) executeRoutine(ef hhc_ast.ExecuteFunc) {
|
|
|
+ if ti.execModeContext != nil {
|
|
|
+ if ef != nil {
|
|
|
+ err := ti.protectedExecute(ef, ti.execModeContext)
|
|
|
+ if err != nil {
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, "\r\n %% broken command (this is a bug... or may be a feature?)\r\n")
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, "command line execute error: %s\r\n", err.Error())
|
|
|
+ }
|
|
|
+ ti.exitStateExecute()
|
|
|
+ } else {
|
|
|
+ ti.exitStateExecute()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ ti.exitStateExecute()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) protectedExecute(ef hhc_ast.ExecuteFunc, ctx *hhc_ast.ExecContext) error {
|
|
|
+ var err error
|
|
|
+ defer func() {
|
|
|
+ rec := recover()
|
|
|
+ switch rec.(type) {
|
|
|
+ case runtime.Error:
|
|
|
+ err = rec.(runtime.Error)
|
|
|
+ break
|
|
|
+ case error:
|
|
|
+ err = rec.(error)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }()
|
|
|
+ ef(ctx.GetUserContext())
|
|
|
+ return err
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) printLinesInPrintResultState(nline int) {
|
|
|
+ if ti.printModePrintLines == nil {
|
|
|
+ if ti.printModeIsHelp {
|
|
|
+ ti.termState = hhc_common.TerminalState_Input
|
|
|
+ ti.runesEditor.PeekIntoTerminal(ti.currentPrompt, ti.termWidth, ti.outStream)
|
|
|
+ return
|
|
|
+ } else {
|
|
|
+ _ = ti.gotoStateInput(true)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ isend := false
|
|
|
+ _, _ = fmt.Fprint(ti.errStream, "\r\033[K")
|
|
|
+ for i := 0; i < nline; i++ {
|
|
|
+ if ti.printModeCurrentLine < ti.printModeLineCount {
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, " %s\r\n", ti.printModePrintLines[ti.printModeCurrentLine])
|
|
|
+ ti.printModeCurrentLine++
|
|
|
+ }
|
|
|
+ if ti.printModeCurrentLine == ti.printModeLineCount {
|
|
|
+ isend = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if isend {
|
|
|
+ _, _ = fmt.Fprint(ti.errStream, "\r\n")
|
|
|
+ ti.printModePrintLines = nil
|
|
|
+ if ti.printModeIsHelp {
|
|
|
+ ti.termState = hhc_common.TerminalState_Input
|
|
|
+ ti.runesEditor.PeekIntoTerminal(ti.currentPrompt, ti.termWidth, ti.outStream)
|
|
|
+ return
|
|
|
+ } else {
|
|
|
+ _ = ti.gotoStateInput(true)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ _, _ = fmt.Fprint(ti.errStream, "---- More ----")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) stateInputPushRune(r rune) (bool, error) {
|
|
|
+ if utf8.ValidRune(r) {
|
|
|
+ if unicode.IsControl(r) {
|
|
|
+ return ti.stateInputPushControlRune(r)
|
|
|
+ }
|
|
|
+ if unicode.IsGraphic(r) {
|
|
|
+ return ti.stateInputPushGraphicRune(r)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) stateInputPushControlRune(r rune) (bool, error) {
|
|
|
+ switch r {
|
|
|
+ case '\x03':
|
|
|
+ err := ti.gotoStateInput(false)
|
|
|
+ if err != nil {
|
|
|
+ return true, err
|
|
|
+ }
|
|
|
+ return true, nil
|
|
|
+ case ti.backspaceRune:
|
|
|
+ if ti.runesEditor != nil {
|
|
|
+ ti.runesEditor.Backspace()
|
|
|
+ ti.runesEditor.RefreshTerminal(ti.outStream, ti.currentPrompt, ti.termWidth, false)
|
|
|
+ }
|
|
|
+ return true, nil
|
|
|
+ case '\x1b':
|
|
|
+ err := ti.gotoStateTCES()
|
|
|
+ if err != nil {
|
|
|
+ return true, err
|
|
|
+ }
|
|
|
+ return true, nil
|
|
|
+ case '\x09':
|
|
|
+ ti.parser.Reset()
|
|
|
+ rs := ti.runesEditor.Peek()
|
|
|
+ ti.parser.Parse(rs)
|
|
|
+ if ti.parser.HasError() {
|
|
|
+ return true, nil
|
|
|
+ }
|
|
|
+ hi, tok := ti.parser.GetHelpListForTabComplete()
|
|
|
+ if len(hi) == 1 {
|
|
|
+ if hi[0].IsArg {
|
|
|
+ return true, nil
|
|
|
+ }
|
|
|
+ suf := hhc_common.GetCompleteSuffix(hi[0].Name, tok)
|
|
|
+ ti.runesEditor.InsertString(suf)
|
|
|
+ ti.runesEditor.RefreshTerminal(ti.outStream, ti.currentPrompt, ti.termWidth, false)
|
|
|
+ } else {
|
|
|
+ ttp := ztermtab.NewTerminalTablePrinter(ti.termWidth, " ", " | ", "")
|
|
|
+ ttp.AddColumn("", 10, 30, false)
|
|
|
+ ttp.AddColumn("", 0, 0, false)
|
|
|
+ ttp.SetAutoWidthColumn(1)
|
|
|
+ for _, v := range hi {
|
|
|
+ ttp.AddRow([]string{v.Name, v.Description})
|
|
|
+ }
|
|
|
+ _, _ = fmt.Fprint(ti.errStream, "\r\n")
|
|
|
+ ti.gotoStatePrintResult(ttp.RenderToTerminalLines(), true)
|
|
|
+ }
|
|
|
+ return true, nil
|
|
|
+ case '\r':
|
|
|
+ if len(ti.runesEditor.Peek()) <= 0 {
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, "\r\n")
|
|
|
+ ti.runesEditor.PeekIntoTerminal(ti.currentPrompt, ti.termWidth, ti.outStream)
|
|
|
+ return true, nil
|
|
|
+ }
|
|
|
+ ti.parser.Reset()
|
|
|
+ rs := ti.runesEditor.Enter()
|
|
|
+ ti.parser.Parse(rs)
|
|
|
+ ok, ef := ti.parser.TryGetRunFunc()
|
|
|
+ if ti.parser.HasError() {
|
|
|
+ e, p := ti.parser.GetError()
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, "\r\n %% %s at position %d\r\n", hhc_common.FirstToUpper(e.EES()), p)
|
|
|
+ ti.termState = hhc_common.TerminalState_Input
|
|
|
+ ti.runesEditor.PeekIntoTerminal(ti.currentPrompt, ti.termWidth, ti.outStream)
|
|
|
+ return true, nil
|
|
|
+ }
|
|
|
+ if ok {
|
|
|
+ if ef == nil {
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, "\r\n %% broken command (this is a bug... or may be a feature?)\r\n")
|
|
|
+ ti.termState = hhc_common.TerminalState_Input
|
|
|
+ ti.runesEditor.PeekIntoTerminal(ti.currentPrompt, ti.termWidth, ti.outStream)
|
|
|
+ return true, nil
|
|
|
+ } else {
|
|
|
+ ti.gotoStateExecute(ef, ti.parser.GetWalkContext())
|
|
|
+ return true, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, "\r\n %% incomplete command\r\n")
|
|
|
+ ti.termState = hhc_common.TerminalState_Input
|
|
|
+ ti.runesEditor.PeekIntoTerminal(ti.currentPrompt, ti.termWidth, ti.outStream)
|
|
|
+ return true, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) stateInputPushGraphicRune(r rune) (bool, error) {
|
|
|
+ if ti.runesEditor != nil {
|
|
|
+ ti.runesEditor.InsertRune(r)
|
|
|
+ ti.runesEditor.RefreshTerminal(ti.outStream, ti.currentPrompt, ti.termWidth, false)
|
|
|
+ }
|
|
|
+ if r == '?' {
|
|
|
+ ti.parser.Reset()
|
|
|
+ rs := ti.runesEditor.Peek()
|
|
|
+ ti.parser.Parse(rs)
|
|
|
+ if !ti.parser.HelpTriggerd() {
|
|
|
+ return true, nil
|
|
|
+ }
|
|
|
+ _, _ = fmt.Fprint(ti.errStream, "\r\n")
|
|
|
+ if ti.parser.HasError() {
|
|
|
+ e, p := ti.parser.GetError()
|
|
|
+ _, _ = fmt.Fprintf(ti.errStream, " %% %s at position %d\r\n", hhc_common.FirstToUpper(e.EES()), p)
|
|
|
+ ti.runesEditor.Backspace()
|
|
|
+ ti.termState = hhc_common.TerminalState_Input
|
|
|
+ ti.runesEditor.PeekIntoTerminal(ti.currentPrompt, ti.termWidth, ti.outStream)
|
|
|
+ return true, nil
|
|
|
+ }
|
|
|
+ if ti.parser.WillPrintHelp() {
|
|
|
+ ti.runesEditor.Backspace()
|
|
|
+ hi := ti.parser.GetHelpMessage()
|
|
|
+ ttp := ztermtab.NewTerminalTablePrinter(ti.termWidth, " ", " | ", "")
|
|
|
+ ttp.AddColumn("", 10, 30, false)
|
|
|
+ ttp.AddColumn("", 0, 0, false)
|
|
|
+ ttp.SetAutoWidthColumn(1)
|
|
|
+ for _, v := range hi {
|
|
|
+ ttp.AddRow([]string{v.Name, v.Description})
|
|
|
+ }
|
|
|
+ ti.gotoStatePrintResult(ttp.RenderToTerminalLines(), true)
|
|
|
+ return true, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) stateTCESPushRune(r rune) (bool, error) {
|
|
|
+ b, res := ti.tcesParser.PushRune(r)
|
|
|
+ if b {
|
|
|
+ ti.termState = hhc_common.TerminalState_Input
|
|
|
+ if ti.runesEditor == nil {
|
|
|
+ return true, nil
|
|
|
+ }
|
|
|
+ switch res {
|
|
|
+ case SimpleTCESParserResult_LeftArrow:
|
|
|
+ ti.runesEditor.MoveInsertPosition(-1)
|
|
|
+ break
|
|
|
+ case SimpleTCESParserResult_RightArrow:
|
|
|
+ ti.runesEditor.MoveInsertPosition(1)
|
|
|
+ break
|
|
|
+ case SimpleTCESParserResult_UpArrow:
|
|
|
+ ti.runesEditor.LoadPreviousHistory()
|
|
|
+ break
|
|
|
+ case SimpleTCESParserResult_DownArrow:
|
|
|
+ ti.runesEditor.LoadNextHistory()
|
|
|
+ break
|
|
|
+ case SimpleTCESParserResult_HomeKey:
|
|
|
+ ti.runesEditor.MoveInsertPositionToHome()
|
|
|
+ break
|
|
|
+ case SimpleTCESParserResult_EndKey:
|
|
|
+ ti.runesEditor.MoveInsertPositionToEnd()
|
|
|
+ break
|
|
|
+ case SimpleTCESParserResult_DelKey:
|
|
|
+ ti.runesEditor.Delete()
|
|
|
+ break
|
|
|
+ default:
|
|
|
+ break
|
|
|
+ }
|
|
|
+ ti.runesEditor.RefreshTerminal(ti.outStream, ti.currentPrompt, ti.termWidth, false)
|
|
|
+ }
|
|
|
+ return true, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) statePrintResultPushRune(r rune) (bool, error) {
|
|
|
+ switch r {
|
|
|
+ case '\r':
|
|
|
+ ti.printLinesInPrintResultState(1)
|
|
|
+ break
|
|
|
+ case ' ':
|
|
|
+ ti.printLinesInPrintResultState(20)
|
|
|
+ break
|
|
|
+ case '\x03':
|
|
|
+ _, _ = fmt.Fprint(ti.errStream, "\r\n")
|
|
|
+ ti.printModePrintLines = nil
|
|
|
+ if ti.printModeIsHelp {
|
|
|
+ ti.termState = hhc_common.TerminalState_Input
|
|
|
+ ti.runesEditor.PeekIntoTerminal(ti.currentPrompt, ti.termWidth, ti.outStream)
|
|
|
+ } else {
|
|
|
+ _ = ti.gotoStateInput(true)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ return true, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) SetTerminalWidth(w int) {
|
|
|
+ ti.termWidth = w
|
|
|
+}
|
|
|
+
|
|
|
+func (ti *TerminalInteractive) GetTerminalWidth() int {
|
|
|
+ return ti.termWidth
|
|
|
+}
|