├── README.md ├── example └── echo.go └── readline.go /README.md: -------------------------------------------------------------------------------- 1 | * ReadLine Wrapper for Golang * 2 | =============================== 3 | 4 | Originally cloned from https://bitbucket.org/taruti/go-readline 5 | 6 | Former code compile successfully with example but panic while window resizing.So i clone and patch it with 7 | signal SIGWINCH handling with go codes. 8 | 9 | -------------------------------------------------------------------------- 10 | 11 | package main 12 | 13 | import "github.com/shavac/readline" 14 | 15 | func main() { 16 | prompt := "by your command> "; 17 | //loop until ReadLine returns nil (signalling EOF) 18 | L: 19 | for { 20 | switch result := readline.ReadLine(&prompt); true { 21 | case result == nil: 22 | println() 23 | break L //exit loop with EOF(^D) 24 | case *result != "": //ignore blank lines 25 | println(*result); 26 | readline.AddHistory(*result); //allow user to recall this line 27 | } 28 | } 29 | } 30 | 31 | --------------------------------------------------------------------------- 32 | -------------------------------------------------------------------------------- /example/echo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/shavac/readline" 4 | 5 | func main() { 6 | prompt := "> " 7 | //loop until ReadLine returns nil (signalling EOF) 8 | L: 9 | for { 10 | switch result := readline.ReadLine(&prompt); true { 11 | case result == nil: 12 | println() 13 | break L //exit loop 14 | case *result != "": //ignore blank lines 15 | println(*result) 16 | readline.AddHistory(*result) //allow user to recall this line 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /readline.go: -------------------------------------------------------------------------------- 1 | // Wrapper around the GNU readline(3) library 2 | 3 | package readline 4 | 5 | // TODO: 6 | // implement a go-oriented command completion 7 | 8 | /* 9 | #cgo darwin CFLAGS: -I/opt/local/include 10 | #cgo darwin LDFLAGS: -L/opt/local/lib 11 | #cgo LDFLAGS: -lreadline 12 | 13 | #include 14 | #include 15 | #include 16 | #include "readline/readline.h" 17 | #include "readline/history.h" 18 | 19 | char* _go_readline_strarray_at(char **strarray, int idx) 20 | { 21 | return strarray[idx]; 22 | } 23 | 24 | int _go_readline_strarray_len(char **strarray) 25 | { 26 | int sz = 0; 27 | while (strarray[sz] != NULL) { 28 | sz += 1; 29 | } 30 | return sz; 31 | } 32 | */ 33 | import "C" 34 | import "unsafe" 35 | import "syscall" 36 | import "os" 37 | import "os/signal" 38 | 39 | //SIGWINCH handling is here. 40 | func init() { 41 | C.rl_catch_sigwinch = 0 42 | c := make(chan os.Signal, 1) 43 | signal.Notify(c, syscall.SIGWINCH) 44 | go func() { 45 | for sig := range c { 46 | switch sig { 47 | case syscall.SIGWINCH: 48 | Resize() 49 | default: 50 | ; 51 | } 52 | } 53 | }() 54 | } 55 | 56 | func Resize() { 57 | C.rl_resize_terminal() 58 | } 59 | 60 | func ReadLine(prompt *string) *string { 61 | var p *C.char 62 | 63 | //readline allows an empty prompt(NULL) 64 | if prompt != nil { 65 | p = C.CString(*prompt) 66 | } 67 | 68 | ret := C.readline(p) 69 | 70 | if p != nil { 71 | C.free(unsafe.Pointer(p)) 72 | } 73 | 74 | if ret == nil { 75 | return nil 76 | } //EOF 77 | 78 | s := C.GoString(ret) 79 | C.free(unsafe.Pointer(ret)) 80 | return &s 81 | } 82 | 83 | func AddHistory(s string) { 84 | p := C.CString(s) 85 | defer C.free(unsafe.Pointer(p)) 86 | C.add_history(p) 87 | } 88 | 89 | // Parse and execute single line of a readline init file. 90 | func ParseAndBind(s string) { 91 | p := C.CString(s) 92 | defer C.free(unsafe.Pointer(p)) 93 | C.rl_parse_and_bind(p) 94 | } 95 | 96 | // Parse a readline initialization file. 97 | // The default filename is the last filename used. 98 | func ReadInitFile(s string) error { 99 | p := C.CString(s) 100 | defer C.free(unsafe.Pointer(p)) 101 | errno := C.rl_read_init_file(p) 102 | if errno == 0 { 103 | return nil 104 | } 105 | return syscall.Errno(errno) 106 | } 107 | 108 | // Load a readline history file. 109 | // The default filename is ~/.history. 110 | func ReadHistoryFile(s string) error { 111 | p := C.CString(s) 112 | defer C.free(unsafe.Pointer(p)) 113 | errno := C.read_history(p) 114 | if errno == 0 { 115 | return nil 116 | } 117 | return syscall.Errno(errno) 118 | } 119 | 120 | var ( 121 | HistoryLength = -1 122 | ) 123 | 124 | // Save a readline history file. 125 | // The default filename is ~/.history. 126 | func WriteHistoryFile(s string) error { 127 | p := C.CString(s) 128 | defer C.free(unsafe.Pointer(p)) 129 | errno := C.write_history(p) 130 | if errno == 0 && HistoryLength >= 0 { 131 | errno = C.history_truncate_file(p, C.int(HistoryLength)) 132 | } 133 | if errno == 0 { 134 | return nil 135 | } 136 | return syscall.Errno(errno) 137 | } 138 | 139 | // Set the readline word delimiters for tab-completion 140 | func SetCompleterDelims(break_chars string) { 141 | p := C.CString(break_chars) 142 | //defer C.free(unsafe.Pointer(p)) 143 | C.free(unsafe.Pointer(C.rl_completer_word_break_characters)) 144 | C.rl_completer_word_break_characters = p 145 | } 146 | 147 | // Get the readline word delimiters for tab-completion 148 | func GetCompleterDelims() string { 149 | cstr := C.rl_completer_word_break_characters 150 | delims := C.GoString(cstr) 151 | return delims 152 | } 153 | 154 | // 155 | func CompletionMatches(text string, cbk func(text string, state int) string) []string { 156 | c_text := C.CString(text) 157 | defer C.free(unsafe.Pointer(c_text)) 158 | c_cbk := (*C.rl_compentry_func_t)(unsafe.Pointer(&cbk)) 159 | c_matches := C.rl_completion_matches(c_text, c_cbk) 160 | n_matches := int(C._go_readline_strarray_len(c_matches)) 161 | matches := make([]string, n_matches) 162 | for i := 0; i < n_matches; i++ { 163 | matches[i] = C.GoString(C._go_readline_strarray_at(c_matches, C.int(i))) 164 | } 165 | return matches 166 | } 167 | 168 | // 169 | func SetAttemptedCompletionFunction(cbk func(text string, start, end int) []string) { 170 | c_cbk := (*C.rl_completion_func_t)(unsafe.Pointer(&cbk)) 171 | C.rl_attempted_completion_function = c_cbk 172 | } 173 | 174 | /* EOF */ 175 | --------------------------------------------------------------------------------