diff options
-rw-r--r-- | src/display/pager.nim | 18 | ||||
-rw-r--r-- | src/io/lineedit.nim | 108 | ||||
-rw-r--r-- | src/io/term.nim | 83 | ||||
-rw-r--r-- | src/utils/eprint.nim | 13 |
4 files changed, 125 insertions, 97 deletions
diff --git a/src/display/pager.nim b/src/display/pager.nim index a7f4a1d6..119b5002 100644 --- a/src/display/pager.nim +++ b/src/display/pager.nim @@ -123,18 +123,18 @@ proc clearLineEdit(pager: Pager) = func attrs(pager: Pager): WindowAttributes = pager.term.attrs proc searchForward(pager: Pager) {.jsfunc.} = - pager.setLineEdit(readLine("/", pager.attrs.width, config = pager.config, tty = pager.tty), SEARCH_F) + pager.setLineEdit(readLine("/", pager.attrs.width, term = pager.term), SEARCH_F) proc searchBackward(pager: Pager) {.jsfunc.} = - pager.setLineEdit(readLine("?", pager.attrs.width, config = pager.config, tty = pager.tty), SEARCH_B) + pager.setLineEdit(readLine("?", pager.attrs.width, term = pager.term), SEARCH_B) proc isearchForward(pager: Pager) {.jsfunc.} = pager.container.pushCursorPos() - pager.setLineEdit(readLine("/", pager.attrs.width, config = pager.config, tty = pager.tty), ISEARCH_F) + pager.setLineEdit(readLine("/", pager.attrs.width, term = pager.term), ISEARCH_F) proc isearchBackward(pager: Pager) {.jsfunc.} = pager.container.pushCursorPos() - pager.setLineEdit(readLine("?", pager.attrs.width, config = pager.config, tty = pager.tty), ISEARCH_B) + pager.setLineEdit(readLine("?", pager.attrs.width, term = pager.term), ISEARCH_B) proc newPager*(config: Config, attrs: WindowAttributes, dispatcher: Dispatcher): Pager = let pager = Pager( @@ -481,7 +481,7 @@ proc readPipe*(pager: Pager, ctype: Option[string], fd: FileHandle) = pager.addContainer(container) proc command(pager: Pager) {.jsfunc.} = - pager.setLineEdit(readLine("COMMAND: ", pager.attrs.width, config = pager.config, tty = pager.tty), COMMAND) + pager.setLineEdit(readLine("COMMAND: ", pager.attrs.width, term = pager.term), COMMAND) proc commandMode(pager: Pager) {.jsfunc.} = pager.commandmode = true @@ -529,7 +529,7 @@ proc updateReadLine*(pager: Pager) = of LOCATION: pager.loadURL(s) of USERNAME: pager.username = s - pager.setLineEdit(readLine("Password: ", pager.attrs.width, hide = true, config = pager.config, tty = pager.tty), PASSWORD) + pager.setLineEdit(readLine("Password: ", pager.attrs.width, hide = true, term = pager.term), PASSWORD) of PASSWORD: let url = newURL(pager.container.source.location) url.username = pager.username @@ -568,7 +568,7 @@ proc updateReadLine*(pager: Pager) = # Open a URL prompt and visit the specified URL. proc changeLocation(pager: Pager) {.jsfunc.} = var url = pager.container.source.location.serialize() - pager.setLineEdit(readLine("URL: ", pager.attrs.width, current = url, config = pager.config, tty = pager.tty), LOCATION) + pager.setLineEdit(readLine("URL: ", pager.attrs.width, current = url, term = pager.term), LOCATION) # Reload the page in a new buffer, then kill the previous buffer. proc reload(pager: Pager) {.jsfunc.} = @@ -578,7 +578,7 @@ proc click(pager: Pager) {.jsfunc.} = pager.container.click() proc authorize*(pager: Pager) = - pager.setLineEdit(readLine("Username: ", pager.attrs.width, config = pager.config, tty = pager.tty), USERNAME) + pager.setLineEdit(readLine("Username: ", pager.attrs.width, term = pager.term), USERNAME) proc handleEvent*(pager: Pager, container: Container): bool = var event: ContainerEvent @@ -627,7 +627,7 @@ proc handleEvent*(pager: Pager, container: Container): bool = pager.redraw = true of READ_LINE: if container == pager.container: - pager.setLineEdit(readLine(event.prompt, pager.attrs.width, current = event.value, hide = event.password, config = pager.config, tty = pager.tty), BUFFER) + pager.setLineEdit(readLine(event.prompt, pager.attrs.width, current = event.value, hide = event.password, term = pager.term), BUFFER) of OPEN: pager.gotoURL(event.request, some(container.source.location)) of INVALID_COMMAND: discard diff --git a/src/io/lineedit.nim b/src/io/lineedit.nim index a12604cb..6673676d 100644 --- a/src/io/lineedit.nim +++ b/src/io/lineedit.nim @@ -1,4 +1,3 @@ -import terminal import unicode import strutils import sequtils @@ -6,8 +5,9 @@ import sugar import bindings/quickjs import buffer/cell -import config/config +import io/term import js/javascript +import types/color import utils/twtstr type @@ -28,8 +28,35 @@ type displen: int disallowed: set[char] hide: bool - config: Config #TODO get rid of this - tty: File + term: Terminal + +proc printesc(edit: LineEdit, rs: seq[Rune]) = + var s = "" + var format = newFormat() + var cformat = newFormat() + cformat.fgcolor = ColorsANSIFg[4] # blue + var dformat = newFormat() # reset + for r in rs: + if r.isControlChar(): + s &= edit.term.processFormat(format, cformat) + else: + s &= edit.term.processFormat(format, dformat) + s &= r + edit.term.write(s) + +proc printesc(edit: LineEdit, s: string) = + var s = "" + var format = newFormat() + var cformat = newFormat() + cformat.fgcolor = ColorsANSIFg[4] # blue + var dformat = newFormat() # reset + for r in s.runes: + if r.isControlChar(): + s &= edit.term.processFormat(format, cformat) + else: + s &= edit.term.processFormat(format, dformat) + s &= r + edit.term.write(s) func lwidth(r: Rune): int = if r.isControlChar(): @@ -66,25 +93,20 @@ template kill0(edit: LineEdit) = edit.kill0(w) proc backward0(state: LineEdit, i: int) = - if i > 0: - if i == 1: - print('\b') - else: - cursorBackward(i) + state.term.cursorBackward(i) proc forward0(state: LineEdit, i: int) = - if i > 0: - cursorForward(i) + state.term.cursorForward(i) -proc begin0(state: LineEdit) = - print('\r') - state.forward0(state.minlen) +proc begin0(edit: LineEdit) = + edit.term.cursorBegin() + edit.forward0(edit.minlen) proc space(edit: LineEdit, i: int) = - print(' '.repeat(i)) + edit.term.write(' '.repeat(i)) proc generateOutput*(edit: LineEdit): FixedGrid = - result = newFixedGrid(edit.maxlen) + result = newFixedGrid(edit.promptw + edit.maxlen) let os = edit.news.substr(edit.shift, edit.shift + edit.displen) var x = 0 for r in edit.prompt.runes(): @@ -94,12 +116,12 @@ proc generateOutput*(edit: LineEdit): FixedGrid = for r in os: result[x].str = "*" x += r.lwidth() - if x > result.width: break + if x >= result.width: break else: for r in os: result[x].str &= $r x += r.lwidth() - if x > result.width: break + if x >= result.width: break proc getCursorX*(edit: LineEdit): int = return edit.promptw + edit.news.lwidth(edit.shift, edit.cursor) @@ -114,9 +136,9 @@ proc redraw(state: LineEdit) = state.begin0() let os = state.news.substr(state.shift, state.shift + state.displen) if state.hide: - printesc('*'.repeat(os.lwidth())) + state.printesc('*'.repeat(os.lwidth())) else: - printesc($os) + state.printesc(os) state.space(max(state.maxlen - state.minlen - os.lwidth(), 0)) state.begin0() state.forward0(state.news.lwidth(state.shift, state.cursor)) @@ -148,9 +170,9 @@ proc insertCharseq(state: LineEdit, cs: var seq[Rune], disallowed: set[char]) = state.news &= cs state.cursor += cs.len if state.hide: - printesc('*'.repeat(cs.lwidth())) + state.printesc('*'.repeat(cs.lwidth())) else: - printesc($cs) + state.printesc(cs) else: state.news.insert(cs, state.cursor) state.cursor += cs.len @@ -290,32 +312,32 @@ proc `end`*(edit: LineEdit) {.jsfunc.} = edit.fullRedraw() edit.cursor = edit.news.len -proc writePrompt*(lineedit: LineEdit) = - printesc(lineedit.prompt) +proc writePrompt*(edit: LineEdit) = + edit.printesc(edit.prompt) -proc writeStart*(lineedit: LineEdit) = - lineedit.writePrompt() - if lineedit.hide: - printesc('*'.repeat(lineedit.current.lwidth())) +proc writeStart*(edit: LineEdit) = + edit.writePrompt() + if edit.hide: + edit.printesc('*'.repeat(edit.current.lwidth())) else: - printesc(lineedit.current) + edit.printesc(edit.current) proc readLine*(prompt: string, termwidth: int, current = "", - disallowed: set[char] = {}, hide = false, config: Config, - tty: File): LineEdit = - new(result) - result.prompt = prompt - result.promptw = prompt.lwidth() - result.current = current - result.news = current.toRunes() - result.cursor = result.news.len - result.minlen = prompt.lwidth() - result.maxlen = termwidth - prompt.len + disallowed: set[char] = {}, hide = false, + term: Terminal): LineEdit = + result = LineEdit( + prompt: prompt, + promptw: prompt.lwidth(), + current: current, + news: current.toRunes(), + minlen: prompt.lwidth(), + disallowed: disallowed, + hide: hide, + term: term + ) + result.cursor = result.news.lwidth() + result.maxlen = termwidth - result.promptw result.displen = result.maxlen - 1 - result.disallowed = disallowed - result.hide = hide - result.config = config - result.tty = tty proc addLineEditModule*(ctx: JSContext) = ctx.registerType(LineEdit) diff --git a/src/io/term.nim b/src/io/term.nim index 82cb19fd..7d3786e0 100644 --- a/src/io/term.nim +++ b/src/io/term.nim @@ -27,6 +27,8 @@ type ue # end underline mode se # end standout mode me # end all formatting modes + LE # cursor left %1 characters + RI # cursor right %1 characters Termcap = ref object bp: array[1024, uint8] @@ -63,38 +65,35 @@ template CSI*(s: varargs[string, `$`]): string = r &= x r -template DECSET(s: varargs[string, `$`]): string = - var r = "\e[?" - var first = true - for x in s: - if not first: - r &= ";" - first = false - r &= x - r & "h" - -template DECRST(s: varargs[string, `$`]): string = - var r = "\e[?" - var first = true - for x in s: - if not first: - r &= ";" - first = false - r &= x - r & "l" - -template SMCUP(): string = DECSET(1049) -template RMCUP(): string = DECRST(1049) +when not termcap_found: + template DECSET(s: varargs[string, `$`]): string = + var r = "\e[?" + var first = true + for x in s: + if not first: + r &= ";" + first = false + r &= x + r & "h" + template DECRST(s: varargs[string, `$`]): string = + var r = "\e[?" + var first = true + for x in s: + if not first: + r &= ";" + first = false + r &= x + r & "l" + template SMCUP(): string = DECSET(1049) + template RMCUP(): string = DECRST(1049) + template HVP(s: varargs[string, `$`]): string = + CSI(s) & "f" + template EL(s: varargs[string, `$`]): string = + CSI(s) & "K" template SGR*(s: varargs[string, `$`]): string = CSI(s) & "m" -template HVP(s: varargs[string, `$`]): string = - CSI(s) & "f" - -template EL*(s: varargs[string, `$`]): string = - CSI(s) & "K" - const ANSIColorMap = [ ColorsRGB["black"], ColorsRGB["red"], @@ -110,7 +109,7 @@ var goutfile: File proc putc(c: char): cint {.cdecl.} = goutfile.write(c) -proc write(term: Terminal, s: string) = +proc write*(term: Terminal, s: string) = when termcap_found: discard tputs(cstring(s), cint(s.len), putc) else: @@ -137,10 +136,30 @@ proc resetFormat(term: Terminal): string = else: return SGR() -#TODO get rid of this +#TODO get rid of these proc setCursor*(term: Terminal, x, y: int) = term.write(term.cursorGoto(x, y)) +proc cursorBackward*(term: Terminal, i: int) = + if i > 0: + if i == 1: + term.write("\b") + else: + when termcap_found: + term.write($tgoto(term.ccap LE, cint(i), 0)) + else: + term.outfile.cursorBackward(i) + +proc cursorForward*(term: Terminal, i: int) = + if i > 0: + when termcap_found: + term.write($tgoto(term.ccap RI, cint(i), 0)) + else: + term.outfile.cursorForward(i) + +proc cursorBegin*(term: Terminal) = + term.write("\r") + proc enableAltScreen(term: Terminal): string = when termcap_found: if term.hascap ti: @@ -251,7 +270,7 @@ proc processFormat*(term: Terminal, format: var Format, cellf: Format): string = let rgb = color.rgbcolor result &= SGR(48, 2, rgb.r, rgb.g, rgb.b) elif color == defaultColor: - result &= SGR() + result &= term.resetFormat() format = newFormat() else: result &= SGR(color.color) @@ -271,7 +290,7 @@ proc windowChange*(term: Terminal, attrs: WindowAttributes) = func generateFullOutput(term: Terminal, grid: FixedGrid): string = var format = newFormat() result &= term.cursorGoto(0, 0) - result &= SGR() + result &= term.resetFormat() for y in 0 ..< grid.height: for x in 0 ..< grid.width: let cell = grid[y * grid.width + x] diff --git a/src/utils/eprint.nim b/src/utils/eprint.nim index 6a76d250..c5936d2a 100644 --- a/src/utils/eprint.nim +++ b/src/utils/eprint.nim @@ -19,16 +19,3 @@ template eprint*(s: varargs[string, `$`]) = {.cast(noSideEffect), cast(tags: []) stderr.write(' ') stderr.write(x) stderr.write('\n') - -template print*(s: varargs[string, `$`]) = - for x in s: - stdout.write(x) - -template printesc*(s: string) = - for r in s.runes: - if r.isControlChar(): - stdout.write(('^' & $($r)[0].getControlLetter()) - .ansiFgColor(fgBlue).ansiStyle(styleBright).ansiReset()) - else: - stdout.write($r) - |