forked from toolshed/abra
		
	chore: vendor
This commit is contained in:
		
							
								
								
									
										209
									
								
								vendor/github.com/AlecAivazis/survey/v2/terminal/cursor.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								vendor/github.com/AlecAivazis/survey/v2/terminal/cursor.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,209 @@ | ||||
| //go:build !windows | ||||
| // +build !windows | ||||
|  | ||||
| package terminal | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"regexp" | ||||
| 	"strconv" | ||||
| ) | ||||
|  | ||||
| var COORDINATE_SYSTEM_BEGIN Short = 1 | ||||
|  | ||||
| var dsrPattern = regexp.MustCompile(`\x1b\[(\d+);(\d+)R$`) | ||||
|  | ||||
| type Cursor struct { | ||||
| 	In  FileReader | ||||
| 	Out FileWriter | ||||
| } | ||||
|  | ||||
| // Up moves the cursor n cells to up. | ||||
| func (c *Cursor) Up(n int) error { | ||||
| 	_, err := fmt.Fprintf(c.Out, "\x1b[%dA", n) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Down moves the cursor n cells to down. | ||||
| func (c *Cursor) Down(n int) error { | ||||
| 	_, err := fmt.Fprintf(c.Out, "\x1b[%dB", n) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Forward moves the cursor n cells to right. | ||||
| func (c *Cursor) Forward(n int) error { | ||||
| 	_, err := fmt.Fprintf(c.Out, "\x1b[%dC", n) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Back moves the cursor n cells to left. | ||||
| func (c *Cursor) Back(n int) error { | ||||
| 	_, err := fmt.Fprintf(c.Out, "\x1b[%dD", n) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // NextLine moves cursor to beginning of the line n lines down. | ||||
| func (c *Cursor) NextLine(n int) error { | ||||
| 	if err := c.Down(1); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return c.HorizontalAbsolute(0) | ||||
| } | ||||
|  | ||||
| // PreviousLine moves cursor to beginning of the line n lines up. | ||||
| func (c *Cursor) PreviousLine(n int) error { | ||||
| 	if err := c.Up(1); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return c.HorizontalAbsolute(0) | ||||
| } | ||||
|  | ||||
| // HorizontalAbsolute moves cursor horizontally to x. | ||||
| func (c *Cursor) HorizontalAbsolute(x int) error { | ||||
| 	_, err := fmt.Fprintf(c.Out, "\x1b[%dG", x) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Show shows the cursor. | ||||
| func (c *Cursor) Show() error { | ||||
| 	_, err := fmt.Fprint(c.Out, "\x1b[?25h") | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Hide hide the cursor. | ||||
| func (c *Cursor) Hide() error { | ||||
| 	_, err := fmt.Fprint(c.Out, "\x1b[?25l") | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // move moves the cursor to a specific x,y location. | ||||
| func (c *Cursor) move(x int, y int) error { | ||||
| 	_, err := fmt.Fprintf(c.Out, "\x1b[%d;%df", x, y) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Save saves the current position | ||||
| func (c *Cursor) Save() error { | ||||
| 	_, err := fmt.Fprint(c.Out, "\x1b7") | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Restore restores the saved position of the cursor | ||||
| func (c *Cursor) Restore() error { | ||||
| 	_, err := fmt.Fprint(c.Out, "\x1b8") | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // for comparability purposes between windows | ||||
| // in unix we need to print out a new line on some terminals | ||||
| func (c *Cursor) MoveNextLine(cur *Coord, terminalSize *Coord) error { | ||||
| 	if cur.Y == terminalSize.Y { | ||||
| 		if _, err := fmt.Fprintln(c.Out); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return c.NextLine(1) | ||||
| } | ||||
|  | ||||
| // Location returns the current location of the cursor in the terminal | ||||
| func (c *Cursor) Location(buf *bytes.Buffer) (*Coord, error) { | ||||
| 	// ANSI escape sequence for DSR - Device Status Report | ||||
| 	// https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences | ||||
| 	if _, err := fmt.Fprint(c.Out, "\x1b[6n"); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// There may be input in Stdin prior to CursorLocation so make sure we don't | ||||
| 	// drop those bytes. | ||||
| 	var loc []int | ||||
| 	var match string | ||||
| 	for loc == nil { | ||||
| 		// Reports the cursor position (CPR) to the application as (as though typed at | ||||
| 		// the keyboard) ESC[n;mR, where n is the row and m is the column. | ||||
| 		reader := bufio.NewReader(c.In) | ||||
| 		text, err := reader.ReadSlice(byte('R')) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		loc = dsrPattern.FindStringIndex(string(text)) | ||||
| 		if loc == nil { | ||||
| 			// After reading slice to byte 'R', the bufio Reader may have read more | ||||
| 			// bytes into its internal buffer which will be discarded on next ReadSlice. | ||||
| 			// We create a temporary buffer to read the remaining buffered slice and | ||||
| 			// write them to output buffer. | ||||
| 			buffered := make([]byte, reader.Buffered()) | ||||
| 			_, err = io.ReadFull(reader, buffered) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			// Stdin contains R that doesn't match DSR, so pass the bytes along to | ||||
| 			// output buffer. | ||||
| 			buf.Write(text) | ||||
| 			buf.Write(buffered) | ||||
| 		} else { | ||||
| 			// Write the non-matching leading bytes to output buffer. | ||||
| 			buf.Write(text[:loc[0]]) | ||||
|  | ||||
| 			// Save the matching bytes to extract the row and column of the cursor. | ||||
| 			match = string(text[loc[0]:loc[1]]) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	matches := dsrPattern.FindStringSubmatch(string(match)) | ||||
| 	if len(matches) != 3 { | ||||
| 		return nil, fmt.Errorf("incorrect number of matches: %d", len(matches)) | ||||
| 	} | ||||
|  | ||||
| 	col, err := strconv.Atoi(matches[2]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	row, err := strconv.Atoi(matches[1]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &Coord{Short(col), Short(row)}, nil | ||||
| } | ||||
|  | ||||
| func (cur Coord) CursorIsAtLineEnd(size *Coord) bool { | ||||
| 	return cur.X == size.X | ||||
| } | ||||
|  | ||||
| func (cur Coord) CursorIsAtLineBegin() bool { | ||||
| 	return cur.X == COORDINATE_SYSTEM_BEGIN | ||||
| } | ||||
|  | ||||
| // Size returns the height and width of the terminal. | ||||
| func (c *Cursor) Size(buf *bytes.Buffer) (*Coord, error) { | ||||
| 	// the general approach here is to move the cursor to the very bottom | ||||
| 	// of the terminal, ask for the current location and then move the | ||||
| 	// cursor back where we started | ||||
|  | ||||
| 	// hide the cursor (so it doesn't blink when getting the size of the terminal) | ||||
| 	c.Hide() | ||||
| 	defer c.Show() | ||||
|  | ||||
| 	// save the current location of the cursor | ||||
| 	c.Save() | ||||
| 	defer c.Restore() | ||||
|  | ||||
| 	// move the cursor to the very bottom of the terminal | ||||
| 	c.move(999, 999) | ||||
|  | ||||
| 	// ask for the current location | ||||
| 	bottom, err := c.Location(buf) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// since the bottom was calculated in the lower right corner, it | ||||
| 	// is the dimensions we are looking for | ||||
| 	return bottom, nil | ||||
| } | ||||
		Reference in New Issue
	
	Block a user