More work on readline
This commit is contained in:
parent
565526624f
commit
c8296a44eb
@ -8,6 +8,7 @@ import (
|
|||||||
"kitty/tools/cli"
|
"kitty/tools/cli"
|
||||||
"kitty/tools/cli/markup"
|
"kitty/tools/cli/markup"
|
||||||
"kitty/tools/tui/loop"
|
"kitty/tools/tui/loop"
|
||||||
|
"kitty/tools/tui/readline"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
@ -21,11 +22,18 @@ func shell_loop(kill_if_signaled bool) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 1, err
|
return 1, err
|
||||||
}
|
}
|
||||||
|
rl := readline.New(lp, readline.RlInit{Prompt: prompt})
|
||||||
|
|
||||||
lp.OnInitialize = func() (string, error) {
|
lp.OnInitialize = func() (string, error) {
|
||||||
lp.QueueWriteString(prompt)
|
rl.Start()
|
||||||
return "\r\n", nil
|
return "\r\n", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lp.OnResumeFromStop = func() error {
|
||||||
|
rl.Start()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
err = lp.Run()
|
err = lp.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 1, err
|
return 1, err
|
||||||
|
|||||||
@ -193,6 +193,40 @@ func (self *Loop) Beep() {
|
|||||||
self.QueueWriteString("\a")
|
self.QueueWriteString("\a")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Loop) StartAtomicUpdate() {
|
||||||
|
self.QueueWriteString(PENDING_UPDATE.EscapeCodeToSet())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Loop) EndAtomicUpdate() {
|
||||||
|
self.QueueWriteString(PENDING_UPDATE.EscapeCodeToReset())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Loop) SetCursorShape(shape CursorShapes, blink bool) {
|
||||||
|
self.QueueWriteString(CursorShape(shape, blink))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Loop) MoveCursorHorizontally(amt int) {
|
||||||
|
suffix := "C"
|
||||||
|
if amt < 0 {
|
||||||
|
suffix = "D"
|
||||||
|
amt *= -1
|
||||||
|
}
|
||||||
|
self.QueueWriteString(fmt.Sprintf("\x1b[%d%s", amt, suffix))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Loop) MoveCursorVertically(amt int) {
|
||||||
|
suffix := "B"
|
||||||
|
if amt < 0 {
|
||||||
|
suffix = "A"
|
||||||
|
amt *= -1
|
||||||
|
}
|
||||||
|
self.QueueWriteString(fmt.Sprintf("\x1b[%d%s", amt, suffix))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Loop) ClearToEndOfScreen() {
|
||||||
|
self.QueueWriteString("\x1b[J")
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Loop) Quit(exit_code int) {
|
func (self *Loop) Quit(exit_code int) {
|
||||||
self.exit_code = exit_code
|
self.exit_code = exit_code
|
||||||
self.keep_going = false
|
self.keep_going = false
|
||||||
|
|||||||
@ -21,6 +21,14 @@ const (
|
|||||||
CLEAR_SCREEN = "\033[H\033[2J"
|
CLEAR_SCREEN = "\033[H\033[2J"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CursorShapes uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
BLOCK_CURSOR CursorShapes = 1
|
||||||
|
UNDERLINE_CURSOR CursorShapes = 3
|
||||||
|
BAR_CURSOR CursorShapes = 5
|
||||||
|
)
|
||||||
|
|
||||||
type Mode uint32
|
type Mode uint32
|
||||||
|
|
||||||
const private Mode = 1 << 31
|
const private Mode = 1 << 31
|
||||||
@ -147,3 +155,10 @@ func (self *TerminalStateOptions) ResetStateEscapeCodes() string {
|
|||||||
sb.WriteString(RESTORE_COLORS)
|
sb.WriteString(RESTORE_COLORS)
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CursorShape(shape CursorShapes, blink bool) string {
|
||||||
|
if !blink {
|
||||||
|
shape += 1
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("\x1b[%d q", shape)
|
||||||
|
}
|
||||||
|
|||||||
81
tools/tui/readline/api.go
Normal file
81
tools/tui/readline/api.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package readline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"kitty/tools/tui/loop"
|
||||||
|
"kitty/tools/wcswidth"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
const ST = "\x1b\\"
|
||||||
|
const PROMPT_MARK = "\x1b]133;"
|
||||||
|
|
||||||
|
type RlInit struct {
|
||||||
|
Prompt string
|
||||||
|
ContinuationPrompt string
|
||||||
|
EmptyContinuationPrompt bool
|
||||||
|
DontMarkPrompts bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Readline struct {
|
||||||
|
prompt string
|
||||||
|
prompt_len int
|
||||||
|
continuation_prompt string
|
||||||
|
continuation_prompt_len int
|
||||||
|
mark_prompts bool
|
||||||
|
loop *loop.Loop
|
||||||
|
|
||||||
|
// The number of lines after the initial line
|
||||||
|
cursor_y int
|
||||||
|
// Input lines
|
||||||
|
lines []string
|
||||||
|
// The line the cursor is at currently
|
||||||
|
cursor_line int
|
||||||
|
// The offset into the text of the cursor line
|
||||||
|
cursor_pos_in_line int
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(loop *loop.Loop, r RlInit) *Readline {
|
||||||
|
ans := &Readline{
|
||||||
|
prompt: r.Prompt, prompt_len: wcswidth.Stringwidth(r.Prompt), mark_prompts: !r.DontMarkPrompts,
|
||||||
|
loop: loop, lines: []string{""},
|
||||||
|
}
|
||||||
|
if r.ContinuationPrompt != "" || !r.EmptyContinuationPrompt {
|
||||||
|
ans.continuation_prompt = r.ContinuationPrompt
|
||||||
|
if ans.continuation_prompt == "" {
|
||||||
|
ans.continuation_prompt = "> "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ans.mark_prompts {
|
||||||
|
ans.prompt = PROMPT_MARK + "A" + ST + ans.prompt
|
||||||
|
ans.continuation_prompt = PROMPT_MARK + "A;k=s" + ST + ans.continuation_prompt
|
||||||
|
}
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Readline) Start() {
|
||||||
|
self.loop.SetCursorShape(loop.BAR_CURSOR, true)
|
||||||
|
self.Redraw()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Readline) Redraw() {
|
||||||
|
self.loop.StartAtomicUpdate()
|
||||||
|
self.RedrawNonAtomic()
|
||||||
|
self.loop.EndAtomicUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Readline) RedrawNonAtomic() {
|
||||||
|
self.redraw()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Readline) End() {
|
||||||
|
self.loop.QueueWriteString("\r\n")
|
||||||
|
self.loop.SetCursorShape(loop.BLOCK_CURSOR, true)
|
||||||
|
if self.mark_prompts {
|
||||||
|
self.loop.QueueWriteString(PROMPT_MARK + "C" + ST)
|
||||||
|
}
|
||||||
|
}
|
||||||
58
tools/tui/readline/draw.go
Normal file
58
tools/tui/readline/draw.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package readline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"kitty/tools/wcswidth"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
func (self *Readline) write_line_with_prompt(line, prompt string, screen_width int) int {
|
||||||
|
self.loop.QueueWriteString(prompt)
|
||||||
|
self.loop.QueueWriteString(line)
|
||||||
|
w := wcswidth.Stringwidth(prompt) + wcswidth.Stringwidth(line)
|
||||||
|
return w / screen_width
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Readline) move_cursor_to_text_position(pos, screen_width int) int {
|
||||||
|
num_of_lines := pos / screen_width
|
||||||
|
if num_of_lines > 0 {
|
||||||
|
self.loop.MoveCursorVertically(num_of_lines)
|
||||||
|
}
|
||||||
|
self.loop.QueueWriteString("\r")
|
||||||
|
x := pos % screen_width
|
||||||
|
self.loop.MoveCursorHorizontally(x)
|
||||||
|
return num_of_lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Readline) redraw() {
|
||||||
|
if self.cursor_y > 0 {
|
||||||
|
self.loop.MoveCursorVertically(-self.cursor_y)
|
||||||
|
}
|
||||||
|
self.loop.QueueWriteString("\r")
|
||||||
|
self.loop.ClearToEndOfScreen()
|
||||||
|
y := 0
|
||||||
|
line_with_cursor := 0
|
||||||
|
screen_size, err := self.loop.ScreenSize()
|
||||||
|
if err != nil {
|
||||||
|
screen_size.WidthCells = 80
|
||||||
|
screen_size.HeightCells = 24
|
||||||
|
}
|
||||||
|
for i, line := range self.lines {
|
||||||
|
p := self.prompt
|
||||||
|
if i > 0 {
|
||||||
|
p = self.continuation_prompt
|
||||||
|
}
|
||||||
|
num_lines := self.write_line_with_prompt(line, p, int(screen_size.WidthCells))
|
||||||
|
if i == self.cursor_line {
|
||||||
|
line_with_cursor = y
|
||||||
|
}
|
||||||
|
y += num_lines
|
||||||
|
}
|
||||||
|
self.loop.MoveCursorVertically(-y + line_with_cursor)
|
||||||
|
line := self.lines[self.cursor_line]
|
||||||
|
line_with_cursor += self.move_cursor_to_text_position(wcswidth.Stringwidth(line[:self.cursor_pos_in_line]), int(screen_size.WidthCells))
|
||||||
|
self.cursor_y = line_with_cursor
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user