diff options
Diffstat (limited to 'src/display/lineedit.nim')
-rw-r--r-- | src/display/lineedit.nim | 328 |
1 files changed, 0 insertions, 328 deletions
diff --git a/src/display/lineedit.nim b/src/display/lineedit.nim deleted file mode 100644 index a69465a6..00000000 --- a/src/display/lineedit.nim +++ /dev/null @@ -1,328 +0,0 @@ -import std/strutils -import std/unicode - -import bindings/quickjs -import display/term -import js/javascript -import types/cell -import types/opt -import utils/strwidth -import utils/twtstr - -import chagashi/charset -import chagashi/validator -import chagashi/decoder - -type - LineEditState* = enum - EDIT, FINISH, CANCEL - - LineHistory* = ref object - lines: seq[string] - - LineEdit* = ref object - news*: string - prompt*: string - promptw: int - state*: LineEditState - escNext*: bool - cursorx: int # 0 ..< news.notwidth - cursori: int # 0 ..< news.len - shiftx: int # 0 ..< news.notwidth - shifti: int # 0 ..< news.len - padding: int # 0 or 1 - maxwidth: int - disallowed: set[char] - hide: bool - hist: LineHistory - histindex: int - histtmp: string - invalid*: bool - -jsDestructor(LineEdit) - -func newLineHistory*(): LineHistory = - return LineHistory() - -# Note: capped at edit.maxwidth. -func getDisplayWidth(edit: LineEdit): int = - var dispw = 0 - var i = edit.shifti - var r: Rune - while i < edit.news.len and dispw < edit.maxwidth: - fastRuneAt(edit.news, i, r) - dispw += r.width() - return dispw - -proc shiftView(edit: LineEdit) = - # Shift view so it contains the cursor. - if edit.cursorx < edit.shiftx: - edit.shiftx = edit.cursorx - edit.shifti = edit.cursori - # Shift view so it is completely filled. - if edit.shiftx > 0: - let dispw = edit.getDisplayWidth() - if dispw < edit.maxwidth: - let targetx = edit.shiftx - edit.maxwidth + dispw - if targetx <= 0: - edit.shiftx = 0 - edit.shifti = 0 - else: - while edit.shiftx > targetx: - let (r, len) = edit.news.lastRune(edit.shifti - 1) - edit.shiftx -= r.width() - edit.shifti -= len - edit.padding = 0 - # Shift view so it contains the cursor. (act 2) - if edit.shiftx < edit.cursorx - edit.maxwidth: - while edit.shiftx < edit.cursorx - edit.maxwidth and - edit.shifti < edit.news.len: - var r: Rune - fastRuneAt(edit.news, edit.shifti, r) - edit.shiftx += r.width() - if edit.shiftx > edit.cursorx - edit.maxwidth: - # skipped over a cell because of a double-width char - edit.padding = 1 - -proc generateOutput*(edit: LineEdit): FixedGrid = - edit.shiftView() - # Make the output grid +1 cell wide, so it covers the whole input area. - result = newFixedGrid(edit.promptw + edit.maxwidth + 1) - var x = 0 - for r in edit.prompt.runes: - result[x].str &= $r - x += r.width() - if x >= result.width: break - for i in 0 ..< edit.padding: - if x < result.width: - result[x].str = " " - inc x - var i = edit.shifti - while i < edit.news.len: - var r: Rune - fastRuneAt(edit.news, i, r) - if not edit.hide: - let w = r.width() - if x + w > result.width: break - if r.isControlChar(): - result[x].str &= '^' - inc x - result[x].str &= char(r).getControlLetter() - inc x - else: - result[x].str &= $r - x += w - else: - if x + 1 > result.width: break - result[x].str &= '*' - inc x - -proc getCursorX*(edit: LineEdit): int = - return edit.promptw + edit.cursorx + edit.padding - edit.shiftx - -proc insertCharseq(edit: LineEdit, s: string) = - let s = if edit.escNext: - s - else: - deleteChars(s, edit.disallowed) - edit.escNext = false - if s.len == 0: - return - let rem = edit.news.substr(edit.cursori) - edit.news.setLen(edit.cursori) - edit.news &= s - edit.news &= rem - edit.cursori += s.len - edit.cursorx += s.notwidth() - edit.invalid = true - -proc cancel(edit: LineEdit) {.jsfunc.} = - edit.state = CANCEL - -proc submit(edit: LineEdit) {.jsfunc.} = - if edit.hist.lines.len == 0 or edit.news != edit.hist.lines[^1]: - edit.hist.lines.add(edit.news) - edit.state = FINISH - -proc backspace(edit: LineEdit) {.jsfunc.} = - if edit.cursori > 0: - let (r, len) = edit.news.lastRune(edit.cursori - 1) - edit.news.delete(edit.cursori - len .. edit.cursori - 1) - edit.cursori -= len - edit.cursorx -= r.width() - edit.invalid = true - -proc write*(edit: LineEdit, s: string, cs: Charset): bool = - if cs == CHARSET_UTF_8: - if s.validateUTF8Surr() != -1: - return false - edit.insertCharseq(s) - else: - let td = newTextDecoder(cs) - var success = false - let s = td.decodeAll(s, success) - if not success: - return false - edit.insertCharseq(s) - return true - -proc write(edit: LineEdit, s: string): bool {.jsfunc.} = - edit.write(s, CHARSET_UTF_8) - -proc delete(edit: LineEdit) {.jsfunc.} = - if edit.cursori < edit.news.len: - let len = edit.news.runeLenAt(edit.cursori) - edit.news.delete(edit.cursori ..< edit.cursori + len) - edit.invalid = true - -proc escape(edit: LineEdit) {.jsfunc.} = - edit.escNext = true - -proc clear(edit: LineEdit) {.jsfunc.} = - if edit.cursori > 0: - edit.news.delete(0..edit.cursori - 1) - edit.cursori = 0 - edit.cursorx = 0 - edit.invalid = true - -proc kill(edit: LineEdit) {.jsfunc.} = - if edit.cursori < edit.news.len: - edit.news.setLen(edit.cursori) - edit.invalid = true - -proc backward(edit: LineEdit) {.jsfunc.} = - if edit.cursori > 0: - let (r, len) = edit.news.lastRune(edit.cursori - 1) - edit.cursori -= len - edit.cursorx -= r.width() - if edit.cursorx < edit.shiftx: - edit.invalid = true - -proc forward(edit: LineEdit) {.jsfunc.} = - if edit.cursori < edit.news.len: - var r: Rune - fastRuneAt(edit.news, edit.cursori, r) - edit.cursorx += r.width() - if edit.cursorx >= edit.shiftx + edit.maxwidth: - edit.invalid = true - -proc prevWord(edit: LineEdit) {.jsfunc.} = - if edit.cursori == 0: - return - let (r, len) = edit.news.lastRune(edit.cursori - 1) - if r.breaksWord(): - edit.cursori -= len - edit.cursorx -= r.width() - while edit.cursori > 0: - let (r, len) = edit.news.lastRune(edit.cursori - 1) - if r.breaksWord(): - break - edit.cursori -= len - edit.cursorx -= r.width() - if edit.cursorx < edit.shiftx: - edit.invalid = true - -proc nextWord(edit: LineEdit) {.jsfunc.} = - if edit.cursori >= edit.news.len: - return - let oc = edit.cursori - var r: Rune - fastRuneAt(edit.news, edit.cursori, r) - if r.breaksWord(): - edit.cursorx += r.width() - else: - edit.cursori = oc - while edit.cursori < edit.news.len: - let pc = edit.cursori - fastRuneAt(edit.news, edit.cursori, r) - if r.breaksWord(): - edit.cursori = pc - break - edit.cursorx += r.width() - if edit.cursorx >= edit.shiftx + edit.maxwidth: - edit.invalid = true - -proc clearWord(edit: LineEdit) {.jsfunc.} = - let oc = edit.cursori - edit.prevWord() - if oc != edit.cursori: - edit.news.delete(edit.cursori .. oc - 1) - edit.invalid = true - -proc killWord(edit: LineEdit) {.jsfunc.} = - if edit.cursori >= edit.news.len: - return - let oc = edit.cursori - let ox = edit.cursorx - edit.nextWord() - if edit.cursori != oc: - if edit.cursori < edit.news.len: - let len = edit.news.runeLenAt(edit.cursori) - edit.news.delete(oc ..< edit.cursori + len) - else: - edit.news.delete(oc ..< edit.cursori) - edit.cursori = oc - edit.cursorx = ox - edit.invalid = true - -proc begin(edit: LineEdit) {.jsfunc.} = - edit.cursori = 0 - edit.cursorx = 0 - if edit.shiftx > 0: - edit.invalid = true - -proc `end`(edit: LineEdit) {.jsfunc.} = - if edit.cursori < edit.news.len: - edit.cursori = edit.news.len - edit.cursorx = edit.news.notwidth() - if edit.cursorx >= edit.shiftx + edit.maxwidth: - edit.invalid = true - -proc prevHist(edit: LineEdit) {.jsfunc.} = - if edit.histindex > 0: - if edit.news.len > 0: - edit.histtmp = $edit.news - dec edit.histindex - edit.news = edit.hist.lines[edit.histindex] - # The begin call is needed so the cursor doesn't get lost outside - # the string. - edit.begin() - edit.end() - edit.invalid = true - -proc nextHist(edit: LineEdit) {.jsfunc.} = - if edit.histindex + 1 < edit.hist.lines.len: - inc edit.histindex - edit.news = edit.hist.lines[edit.histindex] - edit.begin() - edit.end() - edit.invalid = true - elif edit.histindex < edit.hist.lines.len: - inc edit.histindex - edit.news = edit.histtmp - edit.begin() - edit.end() - edit.histtmp = "" - -proc windowChange*(edit: LineEdit, attrs: WindowAttributes) = - edit.maxwidth = attrs.width - edit.promptw - 1 - -proc readLine*(prompt: string, termwidth: int, current = "", - disallowed: set[char] = {}, hide = false, hist: LineHistory): LineEdit = - result = LineEdit( - prompt: prompt, - promptw: prompt.width(), - news: current, - disallowed: disallowed, - hide: hide, - invalid: true - ) - result.cursori = result.news.len - result.cursorx = result.news.notwidth() - # - 1, so that the cursor always has place - result.maxwidth = termwidth - result.promptw - 1 - result.hist = hist - result.histindex = result.hist.lines.len - -proc addLineEditModule*(ctx: JSContext) = - ctx.registerType(LineEdit) |