mirror of https://github.com/japl-lang/jale.git
new keybindings
better keybinding support added shift and ctrl versions of arrow keys added page up/down added a few extra ctrl versions of utility keys getkey is a utility for learning key numbers
This commit is contained in:
parent
49fa8ab937
commit
9b12850ffb
|
@ -1 +1,2 @@
|
|||
main
|
||||
getkey
|
||||
|
|
10
defaults.nim
10
defaults.nim
|
@ -18,6 +18,14 @@ proc populateDefaults*(editor: LineEditor) =
|
|||
editor.content.up()
|
||||
editor.bindKey("down"):
|
||||
editor.content.down()
|
||||
editor.bindKey("home"):
|
||||
editor.content.home()
|
||||
editor.bindKey("end"):
|
||||
editor.content.`end`()
|
||||
editor.bindKey("pageup"):
|
||||
editor.content.vhome()
|
||||
editor.bindKey("pagedown"):
|
||||
editor.content.vend()
|
||||
editor.bindKey("backspace"):
|
||||
editor.content.backspace()
|
||||
editor.bindKey("delete"):
|
||||
|
@ -26,7 +34,7 @@ proc populateDefaults*(editor: LineEditor) =
|
|||
if editor.content.Y() == editor.content.high() and editor.content.getLine(editor.content.high()) == "":
|
||||
editor.finish()
|
||||
else:
|
||||
editor.content.insertline()
|
||||
editor.content.enter()
|
||||
editor.bindKey("ctrl+c"):
|
||||
editor.finish()
|
||||
editor.events.call(jeQuit)
|
||||
|
|
12
editor.nim
12
editor.nim
|
@ -69,6 +69,8 @@ proc render(editor: LineEditor, line: int = -1, hscroll: bool = true) =
|
|||
0
|
||||
)
|
||||
|
||||
proc clearLine =
|
||||
write stdout, "\r" & " ".repeat(terminalWidth())
|
||||
|
||||
proc fullRender(editor: LineEditor) =
|
||||
# from the top cursor pos, it draws the entire multiline prompt, then
|
||||
|
@ -80,7 +82,15 @@ proc fullRender(editor: LineEditor) =
|
|||
else:
|
||||
write stdout, "\n"
|
||||
inc editor.rendered
|
||||
cursorUp(editor.content.len() - editor.content.Y)
|
||||
|
||||
var extraup = 0
|
||||
while editor.content.len() < editor.rendered:
|
||||
clearLine()
|
||||
cursorDown(1)
|
||||
dec editor.rendered
|
||||
inc extraup
|
||||
|
||||
cursorUp(editor.content.len() - editor.content.Y + extraup)
|
||||
|
||||
proc moveCursorToEnd(editor: LineEditor) =
|
||||
# only called when read finished
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# handy tool that prints out char codes when pressed
|
||||
|
||||
import terminal
|
||||
import strutils
|
||||
|
||||
echo "Press 'ctrl+c' to quit"
|
||||
echo "Press 'ctrl+a' to print a horizontal bar"
|
||||
while true:
|
||||
let key = int(getch())
|
||||
if key == 3:
|
||||
break
|
||||
if key == 1:
|
||||
echo "=".repeat(terminalWidth())
|
||||
else:
|
||||
echo key
|
47
keycodes.nim
47
keycodes.nim
|
@ -7,7 +7,12 @@ import terminal
|
|||
type
|
||||
JaleKeycode* = enum
|
||||
jkStart = 255 # jale keycodes exported start one above jkStart
|
||||
jkLeft = 256, jkRight, jkUp, jkDown, jkHome, jkEnd, jkDelete, jkBackspace,
|
||||
jkLeft = 256, jkRight, jkUp, jkDown,
|
||||
jkHome, jkEnd, jkPageUp, jkPageDown,
|
||||
jkShiftLeft, jkShiftRight, jkShiftUp, jkShiftDown,
|
||||
jkCtrlLeft, jkCtrlRight, jkCtrlUp, jkCtrlDown,
|
||||
jkCtrlHome, jkCtrlEnd, jkCtrlPageUp, jkCtrlPageDown,
|
||||
jkDelete, jkBackspace,
|
||||
jkInsert, jkEnter
|
||||
|
||||
jkFinish, # jale keycodes exported end one below jkFinish
|
||||
|
@ -56,20 +61,60 @@ block:
|
|||
defEscSeq(@[224, 83], jkDelete)
|
||||
# TODO: finish defining escape sequences
|
||||
else:
|
||||
# arrow keys
|
||||
defEscSeq(@[27], jkContinue)
|
||||
defEscSeq(@[27, 91], jkContinue)
|
||||
defEscSeq(@[27, 91, 65], jkUp)
|
||||
defEscSeq(@[27, 91, 66], jkDown)
|
||||
defEscSeq(@[27, 91, 67], jkRight)
|
||||
defEscSeq(@[27, 91, 68], jkLeft)
|
||||
|
||||
# shift+arrow keys
|
||||
defEscSeq(@[27, 91, 49], jkContinue)
|
||||
defEscSeq(@[27, 91, 49, 59], jkContinue)
|
||||
defEscSeq(@[27, 91, 49, 59, 53], jkContinue) # ctrl
|
||||
defEscSeq(@[27, 91, 49, 59, 50], jkContinue) # shift
|
||||
|
||||
defEscSeq(@[27, 91, 49, 59, 50, 65], jkShiftUp)
|
||||
defEscSeq(@[27, 91, 49, 59, 50, 66], jkShiftDown)
|
||||
defEscSeq(@[27, 91, 49, 59, 50, 67], jkShiftRight)
|
||||
defEscSeq(@[27, 91, 49, 59, 50, 68], jkShiftLeft)
|
||||
|
||||
defEscSeq(@[27, 91, 49, 59, 53, 65], jkCtrlUp) # ctrl
|
||||
defEscSeq(@[27, 91, 49, 59, 53, 66], jkCtrlDown) # ctrl
|
||||
defEscSeq(@[27, 91, 49, 59, 53, 67], jkCtrlRight) # ctrl
|
||||
defEscSeq(@[27, 91, 49, 59, 53, 68], jkCtrlLeft) # ctrl
|
||||
|
||||
# home, end
|
||||
defEscSeq(@[27, 91, 72], jkHome)
|
||||
defEscSeq(@[27, 91, 70], jkEnd)
|
||||
|
||||
# ctrl+ home, end
|
||||
defEscSeq(@[27, 91, 49, 59, 53, 72], jkCtrlHome)
|
||||
defEscSeq(@[27, 91, 49, 59, 53, 70], jkCtrlEnd)
|
||||
|
||||
# fancy keys like delete, insert, pgup, pgdown
|
||||
defEscSeq(@[27, 91, 51], jkContinue)
|
||||
defEscSeq(@[27, 91, 50], jkContinue)
|
||||
defEscSeq(@[27, 91, 54], jkContinue)
|
||||
defEscSeq(@[27, 91, 53], jkContinue)
|
||||
defEscSeq(@[27, 91, 51, 126], jkDelete)
|
||||
defEscSeq(@[27, 91, 50, 126], jkInsert)
|
||||
defEscSeq(@[27, 91, 53, 126], jkPageUp)
|
||||
defEscSeq(@[27, 91, 54, 126], jkPageDown)
|
||||
|
||||
# ctrl + fancy keys like pgup, pgdown
|
||||
defEscSeq(@[27, 91, 53, 59], jkContinue)
|
||||
defEscSeq(@[27, 91, 53, 59, 53], jkContinue)
|
||||
defEscSeq(@[27, 91, 53, 59, 53, 126], jkCtrlPageUp)
|
||||
defEscSeq(@[27, 91, 54, 59], jkContinue)
|
||||
defEscSeq(@[27, 91, 54, 59, 53], jkContinue)
|
||||
defEscSeq(@[27, 91, 54, 59, 53, 126], jkCtrlPageDown)
|
||||
|
||||
# other keys
|
||||
defEscSeq(@[13], jkEnter)
|
||||
defEscSeq(@[127], jkBackspace)
|
||||
# TODO ctrl+h as backspace? test more terminals
|
||||
|
||||
proc getKey*: int =
|
||||
var key: int = 0
|
||||
|
|
23
line.nim
23
line.nim
|
@ -6,13 +6,10 @@ type
|
|||
Line* = ref object
|
||||
content: string
|
||||
|
||||
# getters/setters
|
||||
# getter
|
||||
|
||||
proc content*(l: Line): string =
|
||||
l.content
|
||||
|
||||
proc `content=`*(l: Line, str: string) =
|
||||
l.content = str
|
||||
proc content*(line: Line): string =
|
||||
line.content
|
||||
|
||||
# constructor
|
||||
|
||||
|
@ -38,3 +35,17 @@ proc delete*(line: Line, start: int, finish: int) =
|
|||
if finish < line.content.high():
|
||||
result &= line.content[finish+1..line.content.high()]
|
||||
line.content = result
|
||||
|
||||
proc range*(line: Line, start: int, finish: int): string =
|
||||
if start > finish or start < 0 or finish > line.content.high():
|
||||
raise newException(CatchableError, &"Invalid arguments for Line.range: start {start}, finish {finish} for line of length {line.content.len()}")
|
||||
result = line.content[start..finish]
|
||||
|
||||
proc len*(line: Line): int =
|
||||
line.content.len()
|
||||
|
||||
proc high*(line: Line): int =
|
||||
line.content.high()
|
||||
|
||||
proc clearLine*(line: Line) =
|
||||
line.content = ""
|
||||
|
|
|
@ -27,7 +27,7 @@ proc newMultiline*: Multiline =
|
|||
# methods
|
||||
|
||||
proc lineLen*(ml: Multiline): int =
|
||||
ml.lines[ml.y].content.len()
|
||||
ml.lines[ml.y].len()
|
||||
|
||||
proc lineHigh*(ml: Multiline): int =
|
||||
ml.lineLen() - 1
|
||||
|
@ -38,6 +38,20 @@ proc len*(ml: Multiline): int =
|
|||
proc high*(ml: Multiline): int =
|
||||
ml.lines.high()
|
||||
|
||||
# internal setter
|
||||
proc sety(ml: Multiline, target: int) =
|
||||
ml.y = target
|
||||
if ml.x > ml.lineLen():
|
||||
ml.x = ml.lineLen()
|
||||
|
||||
# warning check before calling them if y lands in illegal territory
|
||||
# these are unsafe!
|
||||
proc decy(ml: Multiline) =
|
||||
ml.sety(ml.y-1)
|
||||
|
||||
proc incy(ml: Multiline) =
|
||||
ml.sety(ml.y+1)
|
||||
|
||||
proc left*(ml: Multiline) =
|
||||
if ml.x > 0:
|
||||
dec ml.x
|
||||
|
@ -48,15 +62,24 @@ proc right*(ml: Multiline) =
|
|||
|
||||
proc up*(ml: Multiline) =
|
||||
if ml.y > 0:
|
||||
dec ml.y
|
||||
if ml.x > ml.lineLen():
|
||||
ml.x = ml.lineLen()
|
||||
ml.decy
|
||||
|
||||
proc down*(ml: Multiline) =
|
||||
if ml.y < ml.lines.high():
|
||||
inc ml.y
|
||||
if ml.x > ml.lineLen():
|
||||
ml.x = ml.lineLen()
|
||||
ml.incy
|
||||
|
||||
proc home*(ml: Multiline) =
|
||||
ml.x = 0
|
||||
|
||||
proc `end`*(ml: Multiline) =
|
||||
ml.x = ml.lineLen()
|
||||
|
||||
proc vhome*(ml: Multiline) =
|
||||
ml.sety(0)
|
||||
|
||||
|
||||
proc vend*(ml: Multiline) =
|
||||
ml.sety(ml.high())
|
||||
|
||||
proc insert*(ml: Multiline, str: string) =
|
||||
ml.lines[ml.y].insert(str, ml.x)
|
||||
|
@ -70,9 +93,16 @@ proc backspace*(ml: Multiline) =
|
|||
if ml.x > 0:
|
||||
ml.lines[ml.y].delete(ml.x - 1, ml.x - 1)
|
||||
dec ml.x
|
||||
elif ml.x == 0 and ml.y > 0:
|
||||
let cut = ml.lines[ml.y].content
|
||||
ml.lines.delete(ml.y)
|
||||
dec ml.y
|
||||
ml.x = ml.lineLen()
|
||||
ml.lines[ml.y].insert(cut, ml.x)
|
||||
|
||||
|
||||
proc insertline*(ml: Multiline) =
|
||||
# TODO split line support
|
||||
# the default behaviour of command mode o
|
||||
if ml.y == ml.lines.high():
|
||||
ml.lines.add(newLine())
|
||||
else:
|
||||
|
@ -80,9 +110,18 @@ proc insertline*(ml: Multiline) =
|
|||
inc ml.y
|
||||
ml.x = 0
|
||||
|
||||
proc enter*(ml: Multiline) =
|
||||
# the default behaviour of enter in normie editors
|
||||
if ml.x > ml.lineHigh():
|
||||
ml.insertline() # when end of line, it's just an insertline
|
||||
else:
|
||||
let cut = ml.lines[ml.y].range(ml.x, ml.lineHigh())
|
||||
ml.lines[ml.y].delete(ml.x, ml.lineHigh())
|
||||
ml.insertline()
|
||||
ml.lines[ml.y].insert(cut, 0)
|
||||
|
||||
proc clearline*(ml: Multiline) =
|
||||
ml.lines[ml.y].content = ""
|
||||
ml.lines[ml.y].clearLine()
|
||||
|
||||
proc removeline*(ml: Multiline) =
|
||||
ml.lines.delete(ml.y)
|
||||
|
|
|
@ -13,6 +13,8 @@ template bindKey*(editor: LineEditor, key: char, body: untyped) =
|
|||
body
|
||||
|
||||
template bindKey*(editor: LineEditor, key: string, body: untyped) =
|
||||
if not keysByName.hasKey(key):
|
||||
raise newException(Defect, "Invalid key " & key & ", it's not in the keycode table")
|
||||
editor.bindKey(keysByName[key]):
|
||||
body
|
||||
|
||||
|
|
Loading…
Reference in New Issue