You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
3.9 KiB
215 lines
3.9 KiB
package main |
|
|
|
import ( |
|
"log" |
|
"sort" |
|
"strings" |
|
"unicode/utf8" |
|
|
|
"github.com/tncardoso/gocurses" |
|
) |
|
|
|
var consoleInput []rune |
|
|
|
func draw(msgs []string) { |
|
prompt, ok := ConfKey("console_prompt").(string) |
|
if !ok { |
|
prefix, ok := ConfKey("command_prefix").(string) |
|
if !ok { |
|
prefix = "#" |
|
} |
|
|
|
prompt = prefix + ">" |
|
} |
|
|
|
gocurses.Clear() |
|
|
|
row, _ := gocurses.Getmaxyx() |
|
|
|
i := len(msgs) |
|
for _, msg := range msgs { |
|
gocurses.Mvaddstr(row-i-1, 0, msg) |
|
i-- |
|
} |
|
gocurses.Mvaddstr(row-i-1, 0, prompt+string(consoleInput)) |
|
|
|
gocurses.Refresh() |
|
} |
|
|
|
func autoComplete(all []string, current string) string { |
|
if len(all) > 1 && len(current) > 0 { |
|
for k, v := range all { |
|
if v == current { |
|
if k+1 < len(all) { |
|
return all[k+1] |
|
} else { |
|
return all[0] |
|
} |
|
} else if strings.HasPrefix(v, current) { |
|
return v |
|
} |
|
} |
|
} else if len(all) >= 1 { |
|
return all[0] |
|
} |
|
|
|
return "" |
|
} |
|
|
|
func autoCompleteName(input []rune) []rune { |
|
var name, tmpName []rune |
|
for i := len(input) - 1; i >= 0; i-- { |
|
if input[i] == ' ' { |
|
input = input[:i+1] |
|
break |
|
} |
|
tmpName = append(tmpName, input[i]) |
|
} |
|
|
|
for i := len(tmpName) - 1; i >= 0; i-- { |
|
name = append(name, tmpName[i]) |
|
} |
|
|
|
var names []string |
|
for _, c2 := range Conns() { |
|
names = append(names, c2.Username()) |
|
} |
|
|
|
return append(input, []rune(autoComplete(names, string(name)))...) |
|
} |
|
|
|
func autoCompleteCommand(input []rune) []rune { |
|
var cmds []string |
|
for cmd := range chatCommands { |
|
if chatCommands[cmd].console { |
|
cmds = append(cmds, cmd) |
|
} |
|
} |
|
sort.Strings(cmds) |
|
|
|
return []rune(autoComplete(cmds, string(input))) |
|
} |
|
|
|
func initCurses(l *Logger) { |
|
gocurses.Initscr() |
|
gocurses.Cbreak() |
|
gocurses.Noecho() |
|
gocurses.Stdscr.Keypad(true) |
|
|
|
go func() { |
|
h := &History{} |
|
|
|
for { |
|
var ch rune |
|
ch1 := gocurses.Stdscr.Getch() % 255 |
|
if ch1 > 0x7F { |
|
ch2 := gocurses.Stdscr.Getch() |
|
ch, _ = utf8.DecodeRune([]byte{byte(ch1), byte(ch2)}) |
|
} else { |
|
ch = rune(ch1) |
|
} |
|
|
|
switch ch { |
|
case 3: |
|
consoleInput = h.Next() |
|
case 4: |
|
consoleInput = h.Prev(consoleInput) |
|
case 5: |
|
rows, _ := gocurses.Getmaxyx() |
|
start := len(l.lines) - rows + 1 - l.offset |
|
if start < 0 { |
|
start = 0 |
|
} |
|
|
|
if start > 0 { |
|
l.offset += 1 |
|
if l.offset > len(l.lines)-1 { |
|
l.offset = len(l.lines) - 1 |
|
} |
|
} |
|
case 6: |
|
l.offset -= 1 |
|
if l.offset < 0 { |
|
l.offset = 0 |
|
} |
|
case '\b': |
|
if len(consoleInput) > 0 { |
|
consoleInput = consoleInput[:len(consoleInput)-1] |
|
} |
|
case '\t': |
|
if strings.Count(string(consoleInput), " ") > 0 { |
|
consoleInput = autoCompleteName(consoleInput) |
|
} else { |
|
consoleInput = autoCompleteCommand(consoleInput) |
|
} |
|
case '\n': |
|
params := strings.Split(string(consoleInput), " ") |
|
h.Add(consoleInput) |
|
consoleInput = []rune{} |
|
|
|
if chatCommands[params[0]].function == nil { |
|
log.Print("Unknown command ", params[0]) |
|
continue |
|
} |
|
|
|
if !chatCommands[params[0]].console { |
|
log.Print("This command is not available to the console!") |
|
continue |
|
} |
|
|
|
chatCommands[params[0]].function(nil, strings.Join(params[1:], " ")) |
|
default: |
|
consoleInput = append(consoleInput, ch) |
|
} |
|
|
|
rows, _ := gocurses.Getmaxyx() |
|
start := len(l.lines) - rows + 1 - l.offset |
|
if start < 0 { |
|
start = 0 |
|
} |
|
|
|
draw(l.lines[start : len(l.lines)-l.offset]) |
|
} |
|
}() |
|
} |
|
|
|
type History struct { |
|
lines [][]rune |
|
i int |
|
} |
|
|
|
func (h *History) Add(line []rune) { |
|
for k, v := range h.lines { |
|
if string(v) == string(line) { |
|
if k+1 < len(h.lines) { |
|
h.lines = append(h.lines[:k], h.lines[k+1:]...) |
|
} else { |
|
h.lines = h.lines[:k] |
|
} |
|
} |
|
} |
|
|
|
h.lines = append(h.lines, line) |
|
h.i = 0 |
|
} |
|
|
|
func (h *History) Prev(current []rune) []rune { |
|
h.i++ |
|
i := len(h.lines) - h.i |
|
if i < 0 || i >= len(h.lines) { |
|
h.i-- |
|
return current |
|
} |
|
|
|
return h.lines[i] |
|
} |
|
|
|
func (h *History) Next() []rune { |
|
h.i-- |
|
if h.i < 1 { |
|
h.i = 0 |
|
return []rune{} |
|
} |
|
|
|
return h.lines[len(h.lines)-h.i] |
|
}
|
|
|