github.com/julianthome/gore@v0.0.0-20231109011145-b3a6bbe6fe55/liner.go (about)

     1  package gore
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  	"text/scanner"
     9  
    10  	"github.com/peterh/liner"
    11  )
    12  
    13  const (
    14  	promptDefault  = "gore> "
    15  	promptContinue = "..... "
    16  	indent         = "    "
    17  )
    18  
    19  type contLiner struct {
    20  	*liner.State
    21  	buffer        string
    22  	depth         int
    23  	defaultPrompt string
    24  }
    25  
    26  func newContLiner(defaultPrompt string) *contLiner {
    27  	rl := liner.NewLiner()
    28  	rl.SetCtrlCAborts(true)
    29  	return &contLiner{State: rl, defaultPrompt: defaultPrompt}
    30  }
    31  
    32  func (cl *contLiner) promptString() string {
    33  	if cl.buffer != "" {
    34  		return promptContinue + strings.Repeat(indent, cl.depth)
    35  	}
    36  
    37  	return cl.defaultPrompt 
    38  }
    39  
    40  func (cl *contLiner) Prompt() (string, error) {
    41  	line, err := cl.State.Prompt(cl.promptString())
    42  	if err == io.EOF {
    43  		if cl.buffer != "" {
    44  			// cancel line continuation
    45  			cl.Accepted()
    46  			fmt.Println()
    47  			err = nil
    48  		}
    49  	} else if err == liner.ErrPromptAborted {
    50  		err = nil
    51  		if cl.buffer != "" {
    52  			cl.Accepted()
    53  		} else {
    54  			fmt.Println("(^D to quit)")
    55  		}
    56  	} else if err == nil {
    57  		if cl.buffer != "" {
    58  			cl.buffer = cl.buffer + "\n" + line
    59  		} else {
    60  			cl.buffer = line
    61  		}
    62  	}
    63  
    64  	return cl.buffer, err
    65  }
    66  
    67  func (cl *contLiner) Accepted() {
    68  	cl.State.AppendHistory(cl.buffer)
    69  	cl.buffer = ""
    70  }
    71  
    72  func (cl *contLiner) Clear() {
    73  	cl.buffer = ""
    74  	cl.depth = 0
    75  }
    76  
    77  var errUnmatchedBraces = fmt.Errorf("unmatched braces")
    78  
    79  func (cl *contLiner) Reindent() error {
    80  	oldDepth := cl.depth
    81  	cl.depth = cl.countDepth()
    82  
    83  	if cl.depth < 0 {
    84  		return errUnmatchedBraces
    85  	}
    86  
    87  	if cl.depth < oldDepth {
    88  		lines := strings.Split(cl.buffer, "\n")
    89  		if len(lines) > 1 {
    90  			lastLine := lines[len(lines)-1]
    91  
    92  			cursorUp()
    93  			fmt.Printf("\r%s%s", cl.promptString(), lastLine)
    94  			eraseInLine()
    95  			fmt.Print("\n")
    96  		}
    97  	}
    98  
    99  	return nil
   100  }
   101  
   102  func (cl *contLiner) countDepth() int {
   103  	reader := bytes.NewBufferString(cl.buffer)
   104  	sc := new(scanner.Scanner)
   105  	sc.Init(reader)
   106  	sc.Error = func(_ *scanner.Scanner, msg string) {
   107  		debugf("scanner: %s", msg)
   108  	}
   109  
   110  	depth := 0
   111  	for {
   112  		switch sc.Scan() {
   113  		case '{', '(':
   114  			depth++
   115  		case '}', ')':
   116  			depth--
   117  		case scanner.EOF:
   118  			return depth
   119  		}
   120  	}
   121  }