package terminal import ( "bufio" "fmt" "os" "golang.org/x/term" ) const ClearScreen = "2J" const ResetCursor = "H" var reader = bufio.NewReader(os.Stdin) func ReadKey() (rune, int) { ch, size, err := reader.ReadRune() if err != nil { panic(err) } return ch, size } // Is this a valid interactive terminal? func check() { if !term.IsTerminal(int(os.Stdin.Fd())) { panic("An interactive terminal is required to use a text editor!") } } // Put the terminal in raw mode func RawOn() *term.State { check() oldState, err := term.MakeRaw(int(os.Stdin.Fd())) if err != nil { panic(err) } return oldState } // Restore the terminal to canonical mode func RawOff(oldState *term.State) { err := term.Restore(int(os.Stdin.Fd()), oldState) if err != nil { panic(err) } } func ANSICode (code string) { fmt.Printf("\x1b[%s", code) } func sizeTrick () (rows int, cols int) { // Move cursor to location further than likely screen size // The cursor will move to maximum available position fmt.Print("\x1b[999C\x1b[99B") // Ask the terminal where the cursor is fmt.Print("\x1b[6n") // Read stdin looking for the reported location buffer := "" for char, _ := ReadKey(); char != 'R'; char, _ = ReadKey() { if char == '\x1b' || char == '[' { continue } if char == 'R' || char == '\x00'{ break } buffer += string(char) } _, err := fmt.Sscanf(buffer, "%d;%d", &rows, &cols) if err != nil { panic(err) } return rows, cols } func Size () (width int, height int) { width = 80 height = 24 // Try the syscall first width, height, err := term.GetSize(int(os.Stdin.Fd())) if err == nil { return width, height } // Figure out the size the hard way height, width = sizeTrick() WriteLn("%d Rows, %d Cols", height, width) return width, height } // Print a formatted string to stdout, with CRLF line endings for proper terminal formatting func WriteLn(format string, a ...interface{}) { formatted := fmt.Sprintf(format, a...) fmt.Printf("%s\r\n", formatted) } func Write(format string, a ...interface{}) { fmt.Printf(format, a...) }