diff options
author | bptato <nincsnevem662@gmail.com> | 2022-07-22 19:52:31 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-07-22 19:52:31 +0200 |
commit | 6f7bcc54ab03bc31be309352c73fd8d8153f9c91 (patch) | |
tree | dc4fd8a80ccc8a5a8f7c5a567fcbf80c3e66eccb /src/io/buffer.nim | |
parent | c69a8ab7576e2053afc5dfcee5c7152a07c31230 (diff) | |
download | chawan-6f7bcc54ab03bc31be309352c73fd8d8153f9c91.tar.gz |
Add search function
Uses libregexp from QuickJS. Incremental search is quite hacky for now, but overall it seems to work OK.
Diffstat (limited to 'src/io/buffer.nim')
-rw-r--r-- | src/io/buffer.nim | 230 |
1 files changed, 173 insertions, 57 deletions
diff --git a/src/io/buffer.nim b/src/io/buffer.nim index e7a5d881..065a85a8 100644 --- a/src/io/buffer.nim +++ b/src/io/buffer.nim @@ -16,13 +16,34 @@ import io/cell import io/lineedit import io/loader import io/term +import js/regex import layout/box import render/renderdocument import render/rendertext +import types/color import types/url import utils/twtstr type + CursorPosition* = object + cursorx*: int + cursory*: int + xend*: int + fromx*: int + fromy*: int + + BufferMatch* = object + success*: bool + x*: int + y*: int + str*: string + + Mark* = object + x: int + y: int + format: Format + grid: FixedGrid + Buffer* = ref object contenttype*: string title*: string @@ -33,11 +54,7 @@ type hovertext*: string width*: int height*: int - cursorx*: int - cursory*: int - xend*: int - fromx*: int - fromy*: int + cpos*: CursorPosition attrs*: TermAttributes document*: Document viewport*: Viewport @@ -53,9 +70,10 @@ type prevnode*: Node sourcepair*: Buffer prev*: Buffer - next* {.cursor.}: Buffer + next*: Buffer userstyle*: CSSStylesheet loader*: FileLoader + marks*: seq[Mark] proc newBuffer*(): Buffer = new(result) @@ -67,6 +85,12 @@ proc newBuffer*(): Buffer = result.prevdisplay = newFixedGrid(result.width, result.height) result.statusmsg = newFixedGrid(result.width) +func cursorx*(buffer: Buffer): int {.inline.} = buffer.cpos.cursorx +func cursory*(buffer: Buffer): int {.inline.} = buffer.cpos.cursory +func fromx*(buffer: Buffer): int {.inline.} = buffer.cpos.fromx +func fromy*(buffer: Buffer): int {.inline.} = buffer.cpos.fromy +func xend*(buffer: Buffer): int {.inline.} = buffer.cpos.xend + func generateFullOutput(buffer: Buffer): string = var x = 0 var w = 0 @@ -171,6 +195,14 @@ func generateStatusMessage*(buffer: Buffer): string = if w < buffer.width: result &= EL() +func generateMark*(buffer: Buffer, mark: Mark): string = + var format = newFormat() + var w = 0 + for cell in mark.grid: + result &= format.processFormat(cell.format) + result &= $cell.runes + w += cell.width() + func numLines(buffer: Buffer): int = buffer.lines.len func lastVisibleLine(buffer: Buffer): int = min(buffer.fromy + buffer.height, buffer.numLines) @@ -228,17 +260,20 @@ func getCursorClickable(buffer: Buffer): Element = func currentLine(buffer: Buffer): string = return buffer.lines[buffer.cursory].str -func currentCursorBytes(buffer: Buffer): int = - let line = buffer.currentLine +func cursorBytes(buffer: Buffer, y: int, cc = buffer.fromx + buffer.cursorx): int = + assert y < buffer.lines.len + let line = buffer.lines[y].str var w = 0 var i = 0 - let cc = buffer.fromx + buffer.cursorx while i < line.len and w < cc: var r: Rune fastRuneAt(line, i, r) w += r.width() return i +func currentCursorBytes(buffer: Buffer, cc = buffer.fromx + buffer.cursorx): int = + return buffer.cursorBytes(buffer.cursory, cc) + func currentWidth(buffer: Buffer): int = let line = buffer.currentLine if line.len == 0: return 0 @@ -333,20 +368,20 @@ proc refreshDisplay(buffer: Buffer) = proc setCursorX(buffer: Buffer, x: int, refresh = true, save = true) = if (not refresh) or (buffer.fromx <= x and x < buffer.fromx + buffer.width): - buffer.cursorx = x + buffer.cpos.cursorx = x else: if refresh and buffer.fromx > buffer.cursorx: - buffer.fromx = max(buffer.currentLineWidth() - 1, 0) - buffer.cursorx = buffer.fromx + buffer.cpos.fromx = max(buffer.currentLineWidth() - 1, 0) + buffer.cpos.cursorx = buffer.fromx elif x > buffer.cursorx: - buffer.fromx = max(x - buffer.width + 1, 0) - buffer.cursorx = x + buffer.cpos.fromx = max(x - buffer.width + 1, 0) + buffer.cpos.cursorx = x elif x < buffer.cursorx: - buffer.fromx = x - buffer.cursorx = x + buffer.cpos.fromx = x + buffer.cpos.cursorx = x buffer.redraw = true if save: - buffer.xend = buffer.cursorx + buffer.cpos.xend = buffer.cursorx proc restoreCursorX(buffer: Buffer) = buffer.setCursorX(max(min(buffer.currentLineWidth() - 1, buffer.xend), 0), false, false) @@ -355,23 +390,32 @@ proc setCursorY(buffer: Buffer, y: int) = if buffer.cursory == y: return if y - buffer.fromy >= 0 and y - buffer.height < buffer.fromy: - buffer.cursory = y + buffer.cpos.cursory = y else: if y > buffer.cursory: - buffer.fromy = max(y - buffer.height + 1, 0) + buffer.cpos.fromy = max(y - buffer.height + 1, 0) else: - buffer.fromy = min(y, buffer.maxfromy) - buffer.cursory = y + buffer.cpos.fromy = min(y, buffer.maxfromy) + buffer.cpos.cursory = y buffer.redraw = true buffer.restoreCursorX() +proc centerLine*(buffer: Buffer) = + let ny = max(min(buffer.cursory - buffer.height div 2, buffer.numLines - buffer.height), 0) + if ny != buffer.fromy: + buffer.cpos.fromy = ny + buffer.redraw = true + proc setCursorXY*(buffer: Buffer, x, y: int) = + let fy = buffer.fromy buffer.setCursorY(max(min(y, buffer.numLines - 1), 0)) buffer.setCursorX(max(min(buffer.currentLineWidth(), x), 0)) + if fy != buffer.fromy: + buffer.centerLine() proc setFromXY*(buffer: Buffer, x, y: int) = - buffer.fromy = max(min(y, buffer.maxfromy), 0) - buffer.fromx = max(min(x, buffer.maxfromx), 0) + buffer.cpos.fromy = max(min(y, buffer.maxfromy), 0) + buffer.cpos.fromx = max(min(x, buffer.maxfromx), 0) proc cursorDown*(buffer: Buffer) = if buffer.cursory < buffer.numLines - 1: @@ -563,61 +607,55 @@ proc cursorVertMiddle*(buffer: Buffer) = proc cursorRightEdge*(buffer: Buffer) = buffer.setCursorX(min(buffer.fromx + buffer.width - 1, buffer.currentLineWidth)) -proc centerLine*(buffer: Buffer) = - let ny = max(min(buffer.cursory - buffer.height div 2, buffer.numLines - buffer.height), 0) - if ny != buffer.fromy: - buffer.fromy = ny - buffer.redraw = true - proc halfPageUp*(buffer: Buffer) = - buffer.cursory = max(buffer.cursory - buffer.height div 2 + 1, 0) + buffer.cpos.cursory = max(buffer.cursory - buffer.height div 2 + 1, 0) let nfy = max(0, buffer.fromy - buffer.height div 2 + 1) if nfy != buffer.fromy: - buffer.fromy = nfy + buffer.cpos.fromy = nfy buffer.redraw = true buffer.restoreCursorX() proc halfPageDown*(buffer: Buffer) = - buffer.cursory = min(buffer.cursory + buffer.height div 2 - 1, buffer.numLines - 1) + buffer.cpos.cursory = min(buffer.cursory + buffer.height div 2 - 1, buffer.numLines - 1) let nfy = min(max(buffer.numLines - buffer.height, 0), buffer.fromy + buffer.height div 2 - 1) if nfy != buffer.fromy: - buffer.fromy = nfy + buffer.cpos.fromy = nfy buffer.redraw = true buffer.restoreCursorX() proc pageUp*(buffer: Buffer) = - buffer.cursory = max(buffer.cursory - buffer.height, 0) + buffer.cpos.cursory = max(buffer.cursory - buffer.height, 0) let nfy = max(0, buffer.fromy - buffer.height) if nfy != buffer.fromy: - buffer.fromy = nfy + buffer.cpos.fromy = nfy buffer.redraw = true buffer.restoreCursorX() proc pageDown*(buffer: Buffer) = - buffer.cursory = min(buffer.cursory + buffer.height, buffer.numLines - 1) + buffer.cpos.cursory = min(buffer.cursory + buffer.height, buffer.numLines - 1) let nfy = min(buffer.fromy + buffer.height, max(buffer.numLines - buffer.height, 0)) if nfy != buffer.fromy: - buffer.fromy = nfy + buffer.cpos.fromy = nfy buffer.redraw = true buffer.restoreCursorX() proc pageLeft*(buffer: Buffer) = - buffer.cursorx = max(buffer.cursorx - buffer.width, 0) + buffer.cpos.cursorx = max(buffer.cursorx - buffer.width, 0) let nfx = max(0, buffer.fromx - buffer.width) if nfx != buffer.fromx: - buffer.fromx = nfx + buffer.cpos.fromx = nfx buffer.redraw = true proc pageRight*(buffer: Buffer) = - buffer.cursorx = min(buffer.fromx, buffer.currentLineWidth()) + buffer.cpos.cursorx = min(buffer.fromx, buffer.currentLineWidth()) let nfx = min(max(buffer.maxScreenWidth() - buffer.width, 0), buffer.fromx + buffer.width) if nfx != buffer.fromx: - buffer.fromx = nfx + buffer.cpos.fromx = nfx buffer.redraw = true proc scrollDown*(buffer: Buffer) = if buffer.fromy + buffer.height < buffer.numLines: - inc buffer.fromy + inc buffer.cpos.fromy if buffer.fromy > buffer.cursory: buffer.cursorDown() buffer.redraw = true @@ -626,7 +664,7 @@ proc scrollDown*(buffer: Buffer) = proc scrollUp*(buffer: Buffer) = if buffer.fromy > 0: - dec buffer.fromy + dec buffer.cpos.fromy if buffer.fromy + buffer.height <= buffer.cursory: buffer.cursorUp() buffer.redraw = true @@ -635,12 +673,12 @@ proc scrollUp*(buffer: Buffer) = proc scrollRight*(buffer: Buffer) = if buffer.fromx + buffer.width < buffer.maxScreenWidth(): - inc buffer.fromx + inc buffer.cpos.fromx buffer.redraw = true proc scrollLeft*(buffer: Buffer) = if buffer.fromx > 0: - dec buffer.fromx + dec buffer.cpos.fromx if buffer.cursorx < buffer.fromx: buffer.setCursorX(max(buffer.currentLineWidth() - 1, 0)) buffer.redraw = true @@ -660,8 +698,78 @@ proc gotoAnchor*(buffer: Buffer) = return inc i -proc gotoLocation*(buffer: Buffer, s: string) = - discard parseUrl(s, buffer.location.some, buffer.location, true) +proc addMark*(buffer: Buffer, x, y: int, str: string) = + assert y < buffer.lines.len + var format = newFormat() + format.reverse = true + #TODO get rid of the string part; marks should only consist of a position and + # a length. + var grid = newFixedGrid(str.width()) + var i = 0 + for r in str.runes: + grid[i].runes.add(r) + grid[i].format = format + i += r.width() + buffer.marks.add(Mark(x: x, y: y, format: format, grid: grid)) + +proc cursorNextMatch(buffer: Buffer, regex: Regex, sy, ey: int, wrap = false): BufferMatch = + for y in sy..ey: + let s = if y == buffer.cursory and not wrap: + buffer.currentCursorBytes(buffer.fromx + buffer.cursorx + 1) + else: + 0 + let res = regex.exec(buffer.lines[y].str, s) + if res.success and res.captures.len > 0: + let cap = res.captures[0] + buffer.setCursorXY(cap.s, y) + result.success = true + result.y = y + result.x = buffer.cursorBytes(y, cap.s) + result.str = buffer.lines[y].str.substr(cap.s, cap.e - 1) + return + +proc cursorNextMatch*(buffer: Buffer, regex: Regex, wrap = true): BufferMatch = + let s = buffer.currentCursorBytes(buffer.fromx + buffer.cursorx + 1) + var low = buffer.cursory + if s == buffer.lines.len: + low += 1 + if low > buffer.lines.high: + low = 0 + let ret = buffer.cursorNextMatch(regex, low, buffer.lines.high) + if ret.success: + return ret + if wrap: + return buffer.cursorNextMatch(regex, 0, low, true) + +proc cursorPrevMatch*(buffer: Buffer, regex: Regex, sy, ey: int, wrap = false): BufferMatch = + for y in countdown(sy, ey): + let e = if y == buffer.cursory and not wrap: + buffer.currentCursorBytes() + else: + buffer.lines[y].str.len + 1 + let res = regex.exec(buffer.lines[y].str) + if res.success: + for i in countdown(res.captures.high, 0): + let cap = res.captures[i] + if cap.s < e: + buffer.setCursorXY(cap.s, y) + result.success = true + result.y = y + result.x = buffer.cursorBytes(y, cap.s) + result.str = buffer.lines[y].str.substr(cap.s, cap.e - 1) + return + +proc cursorPrevMatch*(buffer: Buffer, regex: Regex, wrap = true): BufferMatch = + var high = buffer.cursory + if buffer.fromx + buffer.cursorx - 1 < 0: + high -= 1 + if high < 0: + high = buffer.lines.high + let ret = buffer.cursorPrevMatch(regex, high, 0) + if ret.success: + return ret + if wrap: + return buffer.cursorPrevMatch(regex, buffer.lines.high, high) proc refreshTermAttrs*(buffer: Buffer): bool = let newAttrs = getTermAttributes() @@ -674,11 +782,11 @@ proc refreshTermAttrs*(buffer: Buffer): bool = proc updateCursor(buffer: Buffer) = if buffer.fromy > buffer.lastVisibleLine - 1: - buffer.fromy = 0 - buffer.cursory = buffer.lastVisibleLine - 1 + buffer.cpos.fromy = 0 + buffer.cpos.cursory = buffer.lastVisibleLine - 1 if buffer.lines.len == 0: - buffer.cursory = 0 + buffer.cpos.cursory = 0 proc updateHover(buffer: Buffer) = let thisnode = buffer.currentDisplayCell().node @@ -1086,7 +1194,7 @@ proc drawBuffer*(buffer: Buffer) = print(format.processFormat(newFormat())) print('\n') -proc refreshBuffer*(buffer: Buffer) = +proc refreshBuffer*(buffer: Buffer, peek = false) = buffer.title = buffer.getTitle() stdout.hideCursor() @@ -1106,10 +1214,18 @@ proc refreshBuffer*(buffer: Buffer) = buffer.refreshDisplay() buffer.displayBufferSwapOutput() - if not buffer.nostatus: - buffer.statusMsgForBuffer() - else: - buffer.nostatus = false - buffer.displayStatusMessage() - buffer.cursorBufferPos() + for mark in buffer.marks: + if mark.y in buffer.fromy..(buffer.fromy + buffer.height) and mark.x in buffer.fromx..(buffer.fromx + buffer.width): + print(HVP(mark.y - buffer.fromy + 1, mark.x - buffer.fromx + 1)) + print(SGR()) + print(buffer.generateMark(mark)) + print(SGR()) + + if not peek: + if not buffer.nostatus: + buffer.statusMsgForBuffer() + else: + buffer.nostatus = false + buffer.displayStatusMessage() + buffer.cursorBufferPos() stdout.showCursor() |