github.com/oweisse/u-root@v0.0.0-20181109060735-d005ad25fef1/pkg/complete/line.go (about)

     1  // Copyright 2012-2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package complete
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  	"log"
    12  )
    13  
    14  // LineReader has three things and returns on string.
    15  // The three things are an io.Reader, an io.Writer, and a Completer
    16  // Bytes are read one at a time, and depending on their value,
    17  // are written to the io.Writer.
    18  // If the completer returns 0 or 1 answers, a 0 or 1 length string is returned.
    19  // If there are two or more answers, they are printed and the line is
    20  // printed out again.
    21  // Most characters are just echoed.
    22  // Special handling: newline or space returns.
    23  // tab tries to complete
    24  // backspace erases. Since everything is ansi, we assume ansi.
    25  //
    26  // LineReader is used to implement input for a Completer.
    27  // It uses a Completer, io.Reader, io.Writer, and bytes.Buffer.
    28  // bytes are read from the reader, processed, held in the
    29  // bytes.Buffer and, as a side effect, some information is
    30  // written to the io.Writer.
    31  type LineReader struct {
    32  	// Completer for this LineReader
    33  	C Completer
    34  	// R is used for input. Most characters are stored in the
    35  	// Line, while some initiate special processing.
    36  	R io.Reader
    37  	// W is used for output, usually for showing completions.
    38  	W io.Writer
    39  	// Lines holds incoming data as it is read.
    40  	Line *bytes.Buffer
    41  }
    42  
    43  // NewLineReader returns a LineReader.
    44  func NewLineReader(c Completer, r io.Reader, w io.Writer) *LineReader {
    45  	return &LineReader{C: c, R: r, W: w, Line: bytes.NewBufferString("")}
    46  }
    47  
    48  // ReadOne tries to read one choice from l.R, printing out progress to l.W.
    49  // In the case of \t it will try to complete given what it has.
    50  // It will show the result of trying to complete it.
    51  // If there is only one possible completion, it will return the result.
    52  // In the case of \r or \n, if there is more than one choice,
    53  // it will return the list of choices, preprended with what has been typed so far.
    54  func (l *LineReader) ReadOne() ([]string, error) {
    55  	Debug("LineReader: start with %v", l)
    56  	for {
    57  		var b [1]byte
    58  		n, err := l.R.Read(b[:])
    59  		if err != nil {
    60  			if err == io.EOF {
    61  				ln := l.Line.String()
    62  				if ln == "" {
    63  					return []string{}, nil
    64  				}
    65  				return l.C.Complete(ln)
    66  			}
    67  			return nil, err
    68  		}
    69  		Debug("LineReader.ReadOne: got %s, %v, %v", b, n, err)
    70  		if n == 0 {
    71  			continue
    72  		}
    73  		switch b[0] {
    74  		default:
    75  			Debug("LineReader.Just add it to line and pipe")
    76  			l.Line.Write(b[:])
    77  			l.W.Write(b[:])
    78  		case 8, 127:
    79  			// We may have found a use for the io stuff, we'll see.
    80  			s := l.Line.String()
    81  			if len(s) > 0 {
    82  				s = s[:len(s)-1]
    83  				l.Line = bytes.NewBufferString(s)
    84  				l.W.Write(b[:])
    85  			}
    86  		case '\n', '\r':
    87  			err = ErrEOL
    88  			fallthrough
    89  		case ' ':
    90  			if b[0] == ' ' {
    91  				l.W.Write(b[:])
    92  			}
    93  			ln := l.Line.String()
    94  			if ln == "" {
    95  				return []string{}, err
    96  			}
    97  			s, _ := l.C.Complete(ln)
    98  			// If there is no match or too many matches,
    99  			// return what is typed so far.
   100  			Debug("LineReader ln %v, err %v, s %v", ln, err, s)
   101  			if len(s) != 1 {
   102  				s = []string{ln}
   103  			}
   104  			return s, err
   105  		case '\t':
   106  			Debug("LineReader.Try complete with %s", l.Line.String())
   107  			s, err := l.C.Complete(l.Line.String())
   108  			Debug("LineReader.Complete returns %v, %v", s, err)
   109  			if err != nil {
   110  				return nil, err
   111  			}
   112  			if len(s) < 2 {
   113  				Debug("Return is %v", s)
   114  				return s, nil
   115  			}
   116  			if _, err := l.W.Write([]byte(fmt.Sprintf("\n\r%v\n\r%v", s, l.Line.String()))); err != nil {
   117  				log.Printf("Write %v: %v", s, err)
   118  			}
   119  
   120  		}
   121  	}
   122  }