all repos — minline @ 0dca43255ab520a485a054b8c37ad1ce795252bf

A minimalist but highly-customizable line editing library.

Merge branch 'master' into multiline
h3rald h3rald@h3rald.com
Tue, 09 Feb 2021 06:12:02 +0000
commit

0dca43255ab520a485a054b8c37ad1ce795252bf

parent

3b7dbd7568f12c51201bdbb1f3eb57cb14014092

1 files changed, 37 insertions(+), 31 deletions(-)

jump to
M nimline.nimnimline.nim

@@ -45,12 +45,14 @@ ## Prints an ASCII character to stdout.

proc getchr*(): cint {.header: "<conio.h>", importc: "_getch".} ## Retrieves an ASCII character from stdin. else: - proc putchr*(c: cint) = + proc putchr*(c: cint) {.header: "stdio.h", importc: "putchar"} = ## Prints an ASCII character to stdout. stdout.write(c.chr) + stdout.flushFile() proc getchr*(): cint = ## Retrieves an ASCII character from stdin. + stdout.flushFile() return getch().ord.cint # Types

@@ -58,7 +60,7 @@

type Key* = int ## The ASCII code of a keyboard key. KeySeq* = seq[Key] ## A sequence of one or more Keys. - KeyCallback* = proc(ed: var LineEditor) ## A proc that can be bound to a key or a key sequence to access line editing functionalities. + KeyCallback* = proc(ed: var LineEditor) {.closure, gcsafe.} ## A proc that can be bound to a key or a key sequence to access line editing functionalities. LineError* = ref Exception ## A generic nimline error. LineEditorError* = ref Exception ## An error occured in the LineEditor. LineEditorMode* = enum ## The *mode* a LineEditor operates in (insert or replace).

@@ -74,8 +76,8 @@ position: int

queue: Deque[string] max: int LineEditor* = object ## An object representing a line editor, used to process text typed in the terminal. - completionCallback*: proc(ed: LineEditor): seq[string] - newLineCallback*: proc(ed: var LineEditor, prompt: string, c: int): string + completionCallback*: proc(ed: LineEditor): seq[string] {.closure, gcsafe.} + newLineCallback*: proc(ed: var LineEditor, prompt: string, c: int): string {.closure, gcsafe.} history: LineHistory lines: seq[Line] currentLine: int

@@ -500,7 +502,7 @@ const

ESCAPES* = {27} ## Escape characters. # Key Names -var KEYNAMES*: array[0..31, string] ## The following strings can be used in keymaps instead of the correspinding ASCII codes: +var KEYNAMES* {.threadvar.}: array[0..31, string] ## The following strings can be used in keymaps instead of the correspinding ASCII codes: ## ## .. code-block:: nim ## KEYNAMES[1] = "ctrl+a"

@@ -560,7 +562,7 @@ KEYNAMES[25] = "ctrl+y"

KEYNAMES[26] = "ctrl+z" # Key Sequences -var KEYSEQS*: CritBitTree[KeySeq] ## The following key sequences are defined and are used internally by **LineEditor**: +var KEYSEQS* {.threadvar.}: CritBitTree[KeySeq] ## The following key sequences are defined and are used internally by **LineEditor**: ## ## .. code-block:: nim ## KEYSEQS["up"] = @[27, 91, 65] # Windows: @[224, 72]

@@ -592,7 +594,7 @@ KEYSEQS["insert"] = @[27, 91, 50, 126]

KEYSEQS["delete"] = @[27, 91, 51, 126] # Key Mappings -var KEYMAP*: CritBitTree[KeyCallBack] ## The following key mappings are configured by default: +var KEYMAP* {.threadvar.}: CritBitTree[KeyCallBack] ## The following key mappings are configured by default: ## ## * backspace: **deletePrevious** ## * delete: **deleteNext**

@@ -613,53 +615,56 @@ ## * ctrl+e: **goToEnd**

## * home: **goToStart** ## * end: **goToEnd** -KEYMAP["backspace"] = proc(ed: var LineEditor) = +KEYMAP["backspace"] = proc(ed: var LineEditor) {.gcsafe.}= ed.deletePrevious() -KEYMAP["delete"] = proc(ed: var LineEditor) = +KEYMAP["delete"] = proc(ed: var LineEditor) {.gcsafe.}= ed.deleteNext() -KEYMAP["insert"] = proc(ed: var LineEditor) = +KEYMAP["insert"] = proc(ed: var LineEditor) {.gcsafe.}= if ed.mode == mdInsert: ed.mode = mdReplace else: ed.mode = mdInsert -KEYMAP["down"] = proc(ed: var LineEditor) = + +KEYMAP["down"] = proc(ed: var LineEditor) {.gcsafe.}= if ed.currentLine >= ed.lines.len-1: ed.historyNext() else: - ed.down() -KEYMAP["up"] = proc(ed: var LineEditor) = - if ed.currentLine <= 0: + ed.down() +KEYMAP["up"] = proc(ed: var LineEditor) {.gcsafe.}= + if ed.currentLine <= 0: ed.historyPrevious() else: ed.up() -KEYMAP["ctrl+n"] = proc(ed: var LineEditor) = +KEYMAP["ctrl+n"] = proc(ed: var LineEditor) {.gcsafe.}= ed.historyNext() -KEYMAP["ctrl+p"] = proc(ed: var LineEditor) = +KEYMAP["ctrl+p"] = proc(ed: var LineEditor) {.gcsafe.}= ed.historyPrevious() -KEYMAP["left"] = proc(ed: var LineEditor) = +KEYMAP["left"] = proc(ed: var LineEditor) {.gcsafe.}= ed.back() -KEYMAP["right"] = proc(ed: var LineEditor) = +KEYMAP["right"] = proc(ed: var LineEditor) {.gcsafe.}= ed.forward() -KEYMAP["ctrl+b"] = proc(ed: var LineEditor) = +KEYMAP["ctrl+b"] = proc(ed: var LineEditor) {.gcsafe.}= ed.back() -KEYMAP["ctrl+f"] = proc(ed: var LineEditor) = +KEYMAP["ctrl+f"] = proc(ed: var LineEditor) {.gcsafe.}= ed.forward() -KEYMAP["ctrl+c"] = proc(ed: var LineEditor) = +KEYMAP["ctrl+c"] = proc(ed: var LineEditor) {.gcsafe.}= quit(0) -KEYMAP["ctrl+d"] = proc(ed: var LineEditor) = +KEYMAP["ctrl+d"] = proc(ed: var LineEditor) {.gcsafe.}= quit(0) -KEYMAP["ctrl+u"] = proc(ed: var LineEditor) = +KEYMAP["ctrl+u"] = proc(ed: var LineEditor) {.gcsafe.}= ed.clearLine() -KEYMAP["ctrl+a"] = proc(ed: var LineEditor) = +KEYMAP["ctrl+a"] = proc(ed: var LineEditor) {.gcsafe.}= ed.goToStart() -KEYMAP["ctrl+e"] = proc(ed: var LineEditor) = +KEYMAP["ctrl+e"] = proc(ed: var LineEditor) {.gcsafe.}= ed.goToEnd() -KEYMAP["home"] = proc(ed: var LineEditor) = +KEYMAP["home"] = proc(ed: var LineEditor) {.gcsafe.}= ed.goToStart() -KEYMAP["end"] = proc(ed: var LineEditor) = +KEYMAP["end"] = proc(ed: var LineEditor) {.gcsafe.}= ed.goToEnd() -proc readLine*(ed: var LineEditor, prompt="", hidechars = false): string = +var keyMapProc {.threadvar.}: proc(ed: var LineEditor) {.gcsafe.} + +proc readLine*(ed: var LineEditor, prompt="", hidechars = false): string {.gcsafe.} = ## High-level proc to be used instead of **stdin.readLine** to read a line from standard input using the specified **LineEditor** object. ## ## Note that:

@@ -667,6 +672,7 @@ ## * **prompt** is a string (that *cannot* contain escape codes, so it cannot be colored) that will be prepended at the start of the line and

## not included in the contents of the line itself. ## * If **hidechars** is set to **true**, asterisks will be printed to stdout instead of the characters entered by the user. stdout.write(prompt) + stdout.flushFile() ed.prompt = prompt ed.lines = newSeq[Line](0) ed.newLine()

@@ -747,7 +753,8 @@ KEYMAP["insert"](ed)

elif c4 == 126 and c3 == 51: KEYMAP["delete"](ed) elif c1 in CTRL and KEYMAP.hasKey(KEYNAMES[c1]): - KEYMAP[KEYNAMES[c1]](ed) + keyMapProc = KEYMAP[KEYNAMES[c1]] + keyMapProc(ed) else: # Assuming unhandled two-values escape sequence; do nothing. if esc:

@@ -770,7 +777,7 @@ # if a == 3:

# quit(0) # #testChar() - #proc testLineEditor() = + var ed = initEditor(historyFile = "") proc resetLine() =

@@ -805,4 +812,3 @@ return text

while true: echo ed.readLine("-> ") - #testLineEditor()