diff options
-rw-r--r-- | res/chawan.html | 5 | ||||
-rw-r--r-- | res/config.toml | 19 | ||||
-rw-r--r-- | src/bindings/quickjs.nim | 2 | ||||
-rw-r--r-- | src/config/config.nim | 2 | ||||
-rw-r--r-- | src/display/term.nim | 15 | ||||
-rw-r--r-- | src/extern/runproc.nim | 14 | ||||
-rw-r--r-- | src/js/fromjs.nim | 25 | ||||
-rw-r--r-- | src/js/opaque.nim | 1 | ||||
-rw-r--r-- | src/local/client.nim | 34 | ||||
-rw-r--r-- | src/local/container.nim | 203 | ||||
-rw-r--r-- | src/local/pager.nim | 33 |
11 files changed, 280 insertions, 73 deletions
diff --git a/res/chawan.html b/res/chawan.html index fcd054ff..271e3e6b 100644 --- a/res/chawan.html +++ b/res/chawan.html @@ -50,6 +50,9 @@ on your environment, the <kbd>Meta</kbd> key may be called <kbd>Alt</kbd> or up/down by one row <li><kbd>C-l</kbd>: location bar (to enter a URL, etc.) <li><kbd>C-k</kbd>: web search +<li><kbd>v</kbd>, <kbd>V</kbd>, <kbd>C-v</kbd>: select (normal), select +(line), select (block) +<li><kbd>y</kbd>: yank (copy) current selection to system clipboard (needs xsel) <li><kbd>U</kbd>: reload page <li><kbd>,</kbd> (comma), <kbd>.</kbd> (period): previous/next buffer <li><kbd>D</kbd>: discard current buffer @@ -75,7 +78,7 @@ beginning) <li><kbd>zb</kbd>, <kbd>zC-m</kbd>: center on current line (and move to beginning) <li><kbd>w</kbd>, <kbd>b</kbd>: move cursor to next/previous word -<li><kbd>v</kbd>: toggle page source view +<li><kbd>\</kbd>: toggle page source view <li><kbd>0</kbd>: cursor to first cell on line <li><kbd>^</kbd>: cursor to first non-whitespace on line <li><kbd>$</kbd>: cursor to last character on line diff --git a/res/config.toml b/res/config.toml index f821d694..a263fcb7 100644 --- a/res/config.toml +++ b/res/config.toml @@ -132,7 +132,24 @@ zz = 'n => pager.centerLine(n)' 'z+' = 'n => pager.nextPageBegin(n)' 'z^' = 'n => pager.previousPageBegin(n)' C-g = 'pager.lineInfo()' -v = 'pager.toggleSource()' +v = 'n => pager.cursorToggleSelection(n)' +V = 'n => pager.cursorToggleSelection(n, {selectionType: "line"})' +C-v = 'n => pager.cursorToggleSelection(n, {selectionType: "block"})' +y = ''' +async () => { + if (!pager.currentSelection) { + pager.alert("No selection to yank."); + return; + } + const text = await pager.getSelectionText(pager.currentSelection); + if (pager.externInto('xsel -bi', text)) + pager.alert("Copied selection to clipboard."); + else + pager.alert("Failed to copy selection to clipboard. (Is xsel installed?)"); + pager.cursorToggleSelection(); +} +''' +'\' = 'pager.toggleSource()' D = 'pager.discardBuffer()' M-d = 'pager.discardTree()' ',' = 'pager.prevBuffer()' diff --git a/src/bindings/quickjs.nim b/src/bindings/quickjs.nim index d573a1dd..c7c9554f 100644 --- a/src/bindings/quickjs.nim +++ b/src/bindings/quickjs.nim @@ -423,6 +423,8 @@ proc JS_Call*(ctx: JSContext, func_obj, this_obj: JSValue, argc: cint, argv: ptr JSValue): JSValue proc JS_NewObjectFromCtor*(ctx: JSContext, ctor: JSValue, class_id: JSClassID): JSValue +proc JS_Invoke*(ctx: JSContext, this_obj: JSValue, atom: JSAtom, argc: cint, + argv: ptr JSValue): JSValue proc JS_CallConstructor*(ctx: JSContext, func_obj: JSValue, argc: cint, argv: ptr JSValue): JSValue diff --git a/src/config/config.nim b/src/config/config.nim index b0e33685..3cb1d10e 100644 --- a/src/config/config.nim +++ b/src/config/config.nim @@ -322,6 +322,8 @@ func getRealKey(key: string): string = realk &= 'C' if meta == 1: realk &= 'M' + if skip: + realk &= '\\' return realk proc openFileExpand(dir, file: string): FileStream = diff --git a/src/display/term.nim b/src/display/term.nim index b4d4a61b..3a3a7773 100644 --- a/src/display/term.nim +++ b/src/display/term.nim @@ -410,11 +410,17 @@ proc generateSwapOutput(term: Terminal, grid, prev: FixedGrid): string = var change = false # scan for changes, and set cx to x of the first change var cx = 0 + # if there is a change, we have to start from the last x with + # a string (otherwise we might overwrite a double-width char) + var lastx = 0 for x in 0 ..< grid.width: - if grid[y * grid.width + x] != prev[y * grid.width + x]: + let i = y * grid.width + x + if grid[i].str != "": + lastx = x + if grid[i] != prev[i]: change = true - cx = x - w = x + cx = lastx + w = lastx break if change: if cx == 0 and vy != -1: @@ -427,6 +433,9 @@ proc generateSwapOutput(term: Terminal, grid, prev: FixedGrid): string = result &= term.resetFormat() var format = newFormat() for x in cx ..< grid.width: + while w < x: # if previous cell had no width, catch up with x + result &= ' ' + inc w let cell = grid[y * grid.width + x] result &= term.processFormat(format, cell.format) result &= term.processOutputString(cell.str, w) diff --git a/src/extern/runproc.nim b/src/extern/runproc.nim index fa1ed9bc..6cad73a0 100644 --- a/src/extern/runproc.nim +++ b/src/extern/runproc.nim @@ -30,7 +30,7 @@ proc runProcess*(term: Terminal, cmd: string, wait = false): bool = term.restart() # Run process, and capture its output. -proc runProcessCapture*(term: Terminal, cmd: string, outs: var string): bool = +proc runProcessCapture*(cmd: string, outs: var string): bool = let file = popen(cmd, "r") if file == nil: return false @@ -40,3 +40,15 @@ proc runProcessCapture*(term: Terminal, cmd: string, outs: var string): bool = if rv == -1: return false return rv == 0 + +# Run process, and write an arbitrary string into its standard input. +proc runProcessInto*(cmd, ins: string): bool = + let file = popen(cmd, "w") + if file == nil: + return false + let fs = newFileStream(file) + fs.write(ins) + let rv = pclose(file) + if rv == -1: + return false + return rv == 0 diff --git a/src/js/fromjs.nim b/src/js/fromjs.nim index 426b04b4..bddaa2e1 100644 --- a/src/js/fromjs.nim +++ b/src/js/fromjs.nim @@ -5,6 +5,7 @@ import tables import unicode import bindings/quickjs +import io/promise import js/arraybuffer import js/dict import js/error @@ -449,6 +450,28 @@ proc fromJSArrayBufferView(ctx: JSContext, val: JSValue): ) return ok(view) +proc promiseThenCallback(ctx: JSContext, this_val: JSValue, argc: cint, + argv: ptr JSValue, magic: cint, func_data: ptr JSValue): JSValue {.cdecl.} = + let op = JS_GetOpaque(func_data[], JS_GetClassID(func_data[])) + let p = cast[EmptyPromise](op) + p.resolve() + GC_unref(p) + return JS_UNDEFINED + +proc fromJSEmptyPromise(ctx: JSContext, val: JSValue): JSResult[EmptyPromise] = + if not JS_IsObject(val): + return err(newTypeError("Value is not an object")) + #TODO I have a feeling this leaks memory in some cases :( + var p = EmptyPromise() + GC_ref(p) + var tmp = JS_NewObject(ctx) + JS_SetOpaque(tmp, cast[pointer](p)) + var fun = JS_NewCFunctionData(ctx, promiseThenCallback, 0, 0, 1, addr tmp) + let res = JS_Invoke(ctx, val, ctx.getOpaque().str_refs[THEN], 1, addr fun) + if JS_IsException(res): + return err() + return ok(p) + type FromJSAllowedT = (object and not (Result|Option|Table|JSValue|JSDict| JSArrayBuffer|JSArrayBufferView|JSUint8Array)) @@ -482,6 +505,8 @@ proc fromJS*[T](ctx: JSContext, val: JSValue): JSResult[T] = return fromJSEnum[T](ctx, val) elif T is JSValue: return ok(val) + elif T is EmptyPromise: + return fromJSEmptyPromise(ctx, val) elif T is ref object: return fromJSObject[T](ctx, val) elif T is void: diff --git a/src/js/opaque.nim b/src/js/opaque.nim index ae79481b..96b8fa55 100644 --- a/src/js/opaque.nim +++ b/src/js/opaque.nim @@ -15,6 +15,7 @@ type VALUE = "value" NEXT = "next" PROTOTYPE = "prototype" + THEN = "then" JSContextOpaque* = ref object creg*: Table[string, JSClassID] diff --git a/src/local/client.nim b/src/local/client.nim index 4dc29f24..51055472 100644 --- a/src/local/client.nim +++ b/src/local/client.nim @@ -182,9 +182,11 @@ proc handlePagerEvents(client: Client) = if container != nil: client.pager.handleEvents(container) -proc evalAction(client: Client, action: string, arg0: int32) = +proc evalAction(client: Client, action: string, arg0: int32): EmptyPromise = var ret = client.evalJS(action, "<command>") let ctx = client.jsctx + var p = EmptyPromise() + p.resolve() if JS_IsFunction(ctx, ret): if arg0 != 0: var arg0 = toJS(ctx, arg0) @@ -199,7 +201,12 @@ proc evalAction(client: Client, action: string, arg0: int32) = ret = ret2 if JS_IsException(ret): client.jsctx.writeException(client.console.err) + if JS_IsObject(ret): + let maybep = fromJS[EmptyPromise](ctx, ret) + if maybep.isOk: + p = maybep.get JS_FreeValue(ctx, ret) + return p # The maximum number we are willing to accept. # This should be fine for 32-bit signed ints (which precnum currently is). @@ -207,7 +214,7 @@ proc evalAction(client: Client, action: string, arg0: int32) = # it proves to be too low. const MaxPrecNum = 100000000 -proc handleCommandInput(client: Client, c: char) = +proc handleCommandInput(client: Client, c: char): EmptyPromise = if client.config.input.vi_numeric_prefix and not client.pager.notnum: if client.pager.precnum != 0 and c == '0' or c in '1' .. '9': if client.pager.precnum < MaxPrecNum: # better ignore than eval... @@ -218,23 +225,23 @@ proc handleCommandInput(client: Client, c: char) = client.pager.notnum = true client.pager.inputBuffer &= c let action = getNormalAction(client.config, client.pager.inputBuffer) - client.evalAction(action, client.pager.precnum) + let p = client.evalAction(action, client.pager.precnum) if not client.feedNext: client.pager.precnum = 0 client.pager.notnum = false client.handlePagerEvents() + return p -proc input(client: Client) = +proc input(client: Client): EmptyPromise = + var p: EmptyPromise = nil client.pager.term.restoreStdin() while true: let c = client.readChar() if client.pager.askpromise != nil: if c == 'y': client.pager.fulfillAsk(true) - client.runJSJobs() elif c == 'n': client.pager.fulfillAsk(false) - client.runJSJobs() elif client.pager.lineedit.isSome: client.pager.inputBuffer &= c let edit = client.pager.lineedit.get @@ -250,11 +257,11 @@ proc input(client: Client) = else: client.feedNext = true elif not client.feednext: - client.evalAction(action, 0) + discard client.evalAction(action, 0) if not client.feedNext: client.pager.updateReadLine() else: - client.handleCommandInput(c) + p = client.handleCommandInput(c) if not client.feednext: client.pager.inputBuffer = "" client.pager.refreshStatusMsg() @@ -267,6 +274,10 @@ proc input(client: Client) = else: client.feednext = false client.pager.inputBuffer = "" + if p == nil: + p = EmptyPromise() + p.resolve() + return p proc setTimeout[T: JSValue|string](client: Client, handler: T, timeout = 0i32): int32 {.jsfunc.} = @@ -322,8 +333,9 @@ proc c_setvbuf(f: File, buf: pointer, mode: cint, size: csize_t): cint {. proc handleRead(client: Client, fd: int) = if client.pager.infile != nil and fd == client.pager.infile.getFileHandle(): - client.input() - client.handlePagerEvents() + client.input().then(proc() = + client.handlePagerEvents() + ) elif fd == client.forkserver.estream.fd: var nl = false const prefix = "STDERR: " @@ -405,9 +417,9 @@ proc inputLoop(client: Client) = if selectors.Event.Timer in event.events: let r = client.timeouts.runTimeoutFd(event.fd) assert r - client.runJSJobs() client.pager.container.requestLines().then(proc() = client.pager.container.cursorLastLine()) + client.runJSJobs() client.loader.unregistered.setLen(0) client.acceptBuffers() if client.pager.scommand != "": diff --git a/src/local/container.nim b/src/local/container.nim index fc113ad3..2a0051e6 100644 --- a/src/local/container.nim +++ b/src/local/container.nim @@ -11,6 +11,7 @@ import display/winattrs import extern/stdio import io/promise import io/serialize +import js/dict import js/javascript import js/regex import loader/request @@ -60,11 +61,21 @@ type force*: bool else: discard - Highlight = object - x, y: int - endy, endx: int - rect: bool - clear: bool + HighlightType = enum + HL_SEARCH, HL_SELECT + + SelectionType = enum + SEL_NORMAL = "normal" + SEL_BLOCK = "block" + SEL_LINE = "line" + + Highlight = ref object + case t: HighlightType + of HL_SEARCH: discard + of HL_SELECT: + selectionType {.jsget.}: SelectionType + x1, y1: int + x2, y2: int Container* = ref object parent* {.jsget.}: Container @@ -90,7 +101,6 @@ type retry*: seq[URL] hlon*: bool # highlight on? sourcepair*: Container # pointer to buffer with a source view (may be nil) - redraw*: bool needslines*: bool canceled: bool events*: Deque[ContainerEvent] @@ -100,7 +110,9 @@ type select*: Select canreinterpret*: bool cloned: bool + currentSelection {.jsget.}: Highlight +jsDestructor(Highlight) jsDestructor(Container) proc newBuffer*(forkserver: ForkServer, mainproc: Pid, config: BufferConfig, @@ -162,7 +174,6 @@ proc clone*(container: Container, newurl: URL): Promise[Container] = code: container.code, retry: container.retry, hlon: container.hlon, - redraw: container.redraw, #needslines: container.needslines, canceled: container.canceled, events: container.events, @@ -205,16 +216,18 @@ func lastVisibleLine(container: Container): int = min(container.fromy + containe func currentLine(container: Container): string = return container.getLine(container.cursory).str -func cursorBytes(container: Container, y: int, cc = container.cursorx): int = - let line = container.getLine(y).str - var w = 0 - var i = 0 - while i < line.len and w < cc: +func findColBytes(s: string, endx: int, startx = 0, starti = 0): int = + var w = startx + var i = starti + while i < s.len and w < endx: var r: Rune - fastRuneAt(line, i, r) + fastRuneAt(s, i, r) w += r.twidth(w) return i +func cursorBytes(container: Container, y: int, cc = container.cursorx): int = + return container.getLine(y).str.findColBytes(cc, 0, 0) + func currentCursorBytes(container: Container, cc = container.cursorx): int = return container.cursorBytes(container.cursory, cc) @@ -310,35 +323,63 @@ func lineWindow(container: Container): Slice[int] = x = 0 return x .. y -func contains*(hl: Highlight, x, y: int): bool = - if hl.rect: - let rx = hl.x .. hl.endx - let ry = hl.y .. hl.endy - return x in rx and y in ry +func startx(hl: Highlight): int = + if hl.y1 < hl.y2: + hl.x1 + elif hl.y2 < hl.y1: + hl.x2 else: - return (y > hl.y or y == hl.y and x >= hl.x) and - (y < hl.endy or y == hl.endy and x <= hl.endx) - -func contains*(hl: Highlight, y: int): bool = - return y in hl.y .. hl.endy - -func colorArea*(hl: Highlight, y: int, limitx: Slice[int]): Slice[int] = - if hl.rect: - if y in hl.y .. hl.endy: - return max(hl.x, limitx.a) .. min(hl.endx, limitx.b) + min(hl.x1, hl.x2) +func starty(hl: Highlight): int = min(hl.y1, hl.y2) +func endx(hl: Highlight): int = + if hl.y1 > hl.y2: + hl.x1 + elif hl.y2 > hl.y1: + hl.x2 + else: + max(hl.x1, hl.x2) +func endy(hl: Highlight): int = max(hl.y1, hl.y2) + +func colorNormal(container: Container, hl: Highlight, y: int, + limitx: Slice[int]): Slice[int] = + let starty = hl.starty + let endy = hl.endy + if y in starty + 1 .. endy - 1: + let w = container.getLine(y).str.width() + return min(limitx.a, w) .. min(limitx.b, w) + if y == starty and y == endy: + return max(hl.startx, limitx.a) .. min(hl.endx, limitx.b) + if y == starty: + let w = container.getLine(y).str.width() + return max(hl.startx, limitx.a) .. min(limitx.b, w) + if y == endy: + let w = container.getLine(y).str.width() + return min(limitx.a, w) .. min(hl.endx, limitx.b) + +func colorArea(container: Container, hl: Highlight, y: int, + limitx: Slice[int]): Slice[int] = + case hl.t + of HL_SELECT: + case hl.selectionType + of SEL_NORMAL: + return container.colorNormal(hl, y, limitx) + of SEL_BLOCK: + if y in hl.starty .. hl.endy: + let (x, endx) = if hl.x1 < hl.x2: + (hl.x1, hl.x2) + else: + (hl.x2, hl.x1) + return max(x, limitx.a) .. min(endx, limitx.b) + of SEL_LINE: + if y in hl.starty .. hl.endy: + let w = container.getLine(y).str.width() + return min(limitx.a, w) .. min(limitx.b, w) else: - if y in hl.y + 1 .. hl.endy - 1: - return limitx - if y == hl.y and y == hl.endy: - return max(hl.x, limitx.a) .. min(hl.endx, limitx.b) - if y == hl.y: - return max(hl.x, limitx.a) .. limitx.b - if y == hl.endy: - return limitx.a .. min(hl.endx, limitx.b) + return container.colorNormal(hl, y, limitx) func findHighlights*(container: Container, y: int): seq[Highlight] = for hl in container.highlights: - if y in hl: + if y in hl.starty .. hl.endy: result.add(hl) func getHoverText*(container: Container): string = @@ -449,6 +490,10 @@ proc setCursorX(container: Container, x: int, refresh = true, save = true) elif x < container.cursorx: container.setFromX(x, false) container.pos.cursorx = x + if container.cursorx == x and container.currentSelection != nil and + container.currentSelection.x2 != x: + container.currentSelection.x2 = x + container.triggerEvent(UPDATE) if refresh: container.sendCursorPosition() if save: @@ -469,6 +514,9 @@ proc setCursorY(container: Container, y: int, refresh = true) {.jsfunc.} = else: container.setFromY(y) container.pos.cursory = y + if container.currentSelection != nil and container.currentSelection.y2 != y: + container.triggerEvent(UPDATE) + container.currentSelection.y2 = y container.restoreCursorX() if refresh: container.sendCursorPosition() @@ -818,7 +866,7 @@ proc cursorRevNthLink*(container: Container, n = 1) {.jsfunc.} = proc clearSearchHighlights*(container: Container) = for i in countdown(container.highlights.high, 0): - if container.highlights[i].clear: + if container.highlights[i].t == HL_SEARCH: container.highlights.del(i) proc onMatch(container: Container, res: BufferMatch, refresh: bool) = @@ -827,7 +875,13 @@ proc onMatch(container: Container, res: BufferMatch, refresh: bool) = if container.hlon: container.clearSearchHighlights() let ex = res.x + res.str.twidth(res.x) - 1 - let hl = Highlight(x: res.x, y: res.y, endx: ex, endy: res.y, clear: true) + let hl = Highlight( + t: HL_SEARCH, + x1: res.x, + y1: res.y, + x2: ex, + y2: res.y + ) container.highlights.add(hl) container.triggerEvent(UPDATE) container.hlon = false @@ -863,6 +917,73 @@ proc cursorPrevMatch*(container: Container, regex: Regex, wrap, refresh: bool, .then(proc(res: BufferMatch) = container.onMatch(res, refresh)) +type + SelectionOptions = object of JSDict + selectionType: SelectionType + +proc cursorToggleSelection(container: Container, n = 1, + opts = SelectionOptions()): Highlight {.jsfunc.} = + if container.currentSelection != nil: + let i = container.highlights.find(container.currentSelection) + if i != -1: + container.highlights.delete(i) + container.currentSelection = nil + else: + let n = n - 1 + let hl = Highlight( + t: HL_SELECT, + selectionType: opts.selectionType, + x1: container.cursorx, + y1: container.cursory, + x2: container.cursorx + n, + y2: container.cursory + ) + container.highlights.add(hl) + container.currentSelection = hl + container.cursorRight(n) + container.triggerEvent(UPDATE) + return container.currentSelection + +#TODO I don't like this API +# maybe make selection a subclass of highlight? +proc getSelectionText(container: Container, hl: Highlight = nil): + Promise[string] {.jsfunc.} = + let hl = if hl == nil: container.currentSelection else: hl + if hl.t != HL_SELECT: + let p = newPromise[string]() + p.resolve("") + return p + let startx = hl.startx + let starty = hl.starty + let endx = hl.endx + let endy = hl.endy + let nw = starty .. endy + return container.iface.getLines(nw).then(proc(res: GetLinesResult): string = + var s = "" + case hl.selectionType + of SEL_NORMAL: + if starty == endy: + let si = res.lines[0].str.findColBytes(startx) + let ei = res.lines[0].str.findColBytes(endx, startx, si) + s = res.lines[0].str.substr(si, ei) + else: + let si = res.lines[0].str.findColBytes(startx) + s &= res.lines[0].str.substr(si) & '\n' + for i in 1 .. res.lines.high - 1: + s &= res.lines[i].str & '\n' + let ei = res.lines[^1].str.findColBytes(endx) + s &= res.lines[^1].str.substr(0, ei) + of SEL_BLOCK: + for line in res.lines: + let si = line.str.findColBytes(startx) + let ei = line.str.findColBytes(endx, startx, si) + s &= line.str.substr(si, ei) & '\n' + of SEL_LINE: + for line in res.lines: + s &= line.str & '\n' + return s + ) + proc setLoadInfo(container: Container, msg: string) = container.loadinfo = msg container.triggerEvent(STATUS) @@ -1169,7 +1290,8 @@ proc drawLines*(container: Container, display: var FixedGrid, let hls = container.findHighlights(container.fromy + by) let aw = container.width - (startw - container.fromx) # actual width for hl in hls: - let area = hl.colorArea(container.fromy + by, startw .. startw + aw) + let area = container.colorArea(hl, container.fromy + by, + startw .. startw + aw) for i in area: if i - startw >= container.width: break @@ -1185,4 +1307,5 @@ proc handleEvent*(container: Container) = container.needslines = false proc addContainerModule*(ctx: JSContext) = + ctx.registerType(Highlight) ctx.registerType(Container, name = "Buffer") diff --git a/src/local/pager.nim b/src/local/pager.nim index 005fefa5..11364ad5 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -243,10 +243,9 @@ proc refreshDisplay(pager: Pager, container = pager.container) = container.drawLines(pager.display, cellColor(pager.config.display.highlight_color)) -# Note: this function doesn't work if start < i of last written char -proc writeStatusMessage(pager: Pager, str: string, - format: Format = newFormat(), start = 0, - maxwidth = -1, clip = '$'): int {.discardable.} = +# Note: this function does not work correctly if start < i of last written char +proc writeStatusMessage(pager: Pager, str: string, format = newFormat(), + start = 0, maxwidth = -1, clip = '$'): int {.discardable.} = var maxwidth = maxwidth if maxwidth == -1: maxwidth = pager.statusgrid.len @@ -255,22 +254,20 @@ proc writeStatusMessage(pager: Pager, str: string, if i >= e: return i for r in str.runes: - let pi = i - i += r.width() - if i >= e: - if i >= pager.statusgrid.width: - i = pi + let w = r.width() + if i + w >= e: pager.statusgrid[i].format = format pager.statusgrid[i].str = $clip - inc i + inc i # Note: we assume `clip' is 1 cell wide break if r.isControlChar(): - pager.statusgrid[pi].str = "^" - pager.statusgrid[pi + 1].str = $getControlLetter(char(r)) - pager.statusgrid[pi + 1].format = format + pager.statusgrid[i].str = "^" + pager.statusgrid[i + 1].str = $getControlLetter(char(r)) + pager.statusgrid[i + 1].format = format else: - pager.statusgrid[pi].str = $r - pager.statusgrid[pi].format = format + pager.statusgrid[i].str = $r + pager.statusgrid[i].format = format + i += w result = i var def = newFormat() while i < e: @@ -883,10 +880,14 @@ proc extern(pager: Pager, cmd: string, t = ExternDict()): bool {.jsfunc.} = proc externCapture(pager: Pager, cmd: string): Opt[string] {.jsfunc.} = pager.setEnvVars() var s: string - if not runProcessCapture(pager.term, cmd, s): + if not runProcessCapture(cmd, s): return err() return ok(s) +proc externInto(pager: Pager, cmd, ins: string): bool {.jsfunc.} = + pager.setEnvVars() + return runProcessInto(cmd, ins) + proc authorize(pager: Pager) = pager.setLineEdit("Username: ", USERNAME) |