diff options
author | bptato <nincsnevem662@gmail.com> | 2023-07-11 21:08:13 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-07-11 21:13:33 +0200 |
commit | 607545c800f8d2d85943fc8bf2ee246e9901f3b2 (patch) | |
tree | 3a565bf97e7fb6cd41f8e36d96aa8afa87e8bbe3 | |
parent | 34cffe0b658511ac18429c41453c24505c47c6d4 (diff) | |
download | chawan-607545c800f8d2d85943fc8bf2ee246e9901f3b2.tar.gz |
Buffer search fixes & improvements
* Fix race condition in updateReadLineISearch * Disable reshape during isearch
-rw-r--r-- | src/buffer/container.nim | 43 | ||||
-rw-r--r-- | src/display/pager.nim | 66 | ||||
-rw-r--r-- | src/io/promise.nim | 26 |
3 files changed, 82 insertions, 53 deletions
diff --git a/src/buffer/container.nim b/src/buffer/container.nim index ab5bc6f7..9fdc0a69 100644 --- a/src/buffer/container.nim +++ b/src/buffer/container.nim @@ -35,6 +35,7 @@ type fromx*: int fromy*: int setx: int + setxrefresh: bool ContainerEventType* = enum NO_EVENT, FAIL, SUCCESS, NEEDS_AUTH, REDIRECT, ANCHOR, NO_ANCHOR, UPDATE, @@ -324,8 +325,9 @@ proc requestLines*(container: Container, w = container.lineWindow): auto {.disca proc redraw(container: Container) {.jsfunc.} = container.triggerEvent(ContainerEvent(t: UPDATE, force: true)) -proc sendCursorPosition(container: Container) = - container.iface.updateHover(container.cursorx, container.cursory).then(proc(res: UpdateHoverResult) = +proc sendCursorPosition*(container: Container) = + container.iface.updateHover(container.cursorx, container.cursory) + .then(proc(res: UpdateHoverResult) = if res.link.isSome: container.hovertext[HOVER_LINK] = res.link.get if res.title.isSome: @@ -357,6 +359,7 @@ proc setFromXY(container: Container, x, y: int) {.jsfunc.} = proc setCursorX(container: Container, x: int, refresh = true, save = true) {.jsfunc.} = if not container.lineLoaded(container.cursory): container.pos.setx = x + container.pos.setxrefresh = refresh return container.pos.setx = -1 let cw = container.currentLineWidth() @@ -380,9 +383,10 @@ proc setCursorX(container: Container, x: int, refresh = true, save = true) {.jsf container.pos.xend = container.cursorx proc restoreCursorX(container: Container) {.jsfunc.} = - container.setCursorX(max(min(container.currentLineWidth() - 1, container.xend), 0), false, false) + let x = clamp(container.currentLineWidth() - 1, 0, container.xend) + container.setCursorX(x, false, false) -proc setCursorY(container: Container, y: int) {.jsfunc.} = +proc setCursorY(container: Container, y: int, refresh = true) {.jsfunc.} = let y = max(min(y, container.numLines - 1), 0) if container.cursory == y: return if y - container.fromy >= 0 and y - container.height < container.fromy: @@ -394,7 +398,8 @@ proc setCursorY(container: Container, y: int) {.jsfunc.} = container.setFromY(y) container.pos.cursory = y container.restoreCursorX() - container.sendCursorPosition() + if refresh: + container.sendCursorPosition() proc centerLine(container: Container) {.jsfunc.} = container.setFromY(container.cursory - container.height div 2) @@ -402,10 +407,10 @@ proc centerLine(container: Container) {.jsfunc.} = proc centerColumn(container: Container) {.jsfunc.} = container.setFromX(container.cursorx - container.width div 2) -proc setCursorXY(container: Container, x, y: int) {.jsfunc.} = +proc setCursorXY(container: Container, x, y: int, refresh = true) {.jsfunc.} = let fy = container.fromy - container.setCursorY(y) - container.setCursorX(x) + container.setCursorY(y, refresh) + container.setCursorX(x, refresh) if fy != container.fromy: container.centerLine() @@ -588,7 +593,7 @@ proc lineInfo(container: Container) {.jsfunc.} = proc updateCursor(container: Container) = if container.pos.setx > -1: - container.setCursorX(container.pos.setx) + container.setCursorX(container.pos.setx, container.pos.setxrefresh) if container.fromy > container.maxfromy: container.setFromY(container.maxfromy) if container.cursory >= container.numLines: @@ -651,9 +656,9 @@ proc clearSearchHighlights*(container: Container) = if container.highlights[i].clear: container.highlights.del(i) -proc onMatch(container: Container, res: BufferMatch) = +proc onMatch(container: Container, res: BufferMatch, refresh: bool) = if res.success: - container.setCursorXY(res.x, res.y) + container.setCursorXY(res.x, res.y, refresh) if container.hlon: container.clearSearchHighlights() let ex = res.x + res.str.twidth(res.x) - 1 @@ -667,23 +672,27 @@ proc onMatch(container: Container, res: BufferMatch) = container.needslines = true container.hlon = false -proc cursorNextMatch*(container: Container, regex: Regex, wrap: bool) = +proc cursorNextMatch*(container: Container, regex: Regex, wrap, refresh: bool): + EmptyPromise {.discardable.} = if container.select.open: container.select.cursorNextMatch(regex, wrap) + return newResolvedPromise() else: - container.iface + return container.iface .findNextMatch(regex, container.cursorx, container.cursory, wrap) .then(proc(res: BufferMatch) = - container.onMatch(res)) + container.onMatch(res, refresh)) -proc cursorPrevMatch*(container: Container, regex: Regex, wrap: bool) = +proc cursorPrevMatch*(container: Container, regex: Regex, wrap, refresh: bool): + EmptyPromise {.discardable.} = if container.select.open: container.select.cursorPrevMatch(regex, wrap) + return newResolvedPromise() else: - container.iface + return container.iface .findPrevMatch(regex, container.cursorx, container.cursory, wrap) .then(proc(res: BufferMatch) = - container.onMatch(res)) + container.onMatch(res, refresh)) proc setLoadInfo(container: Container, msg: string) = container.loadinfo = msg diff --git a/src/display/pager.nim b/src/display/pager.nim index 0bbdd98f..45386376 100644 --- a/src/display/pager.nim +++ b/src/display/pager.nim @@ -52,6 +52,7 @@ type dispatcher*: Dispatcher display: FixedGrid iregex: Result[Regex, string] + isearchpromise: EmptyPromise lineedit*: Option[LineEdit] linehist: array[LineMode, LineHistory] linemode*: LineMode @@ -128,18 +129,20 @@ proc getter(ctx: JSContext, pager: Pager, s: string): Option[JSValue] return some(val) proc searchNext(pager: Pager) {.jsfunc.} = - if pager.regex.issome: + if pager.regex.isSome: + let wrap = pager.config.search.wrap if not pager.reverseSearch: - pager.container.cursorNextMatch(pager.regex.get, pager.config.search.wrap) + pager.container.cursorNextMatch(pager.regex.get, wrap, true) else: - pager.container.cursorPrevMatch(pager.regex.get, pager.config.search.wrap) + pager.container.cursorPrevMatch(pager.regex.get, wrap, true) proc searchPrev(pager: Pager) {.jsfunc.} = - if pager.regex.issome: + if pager.regex.isSome: + let wrap = pager.config.search.wrap if not pager.reverseSearch: - pager.container.cursorPrevMatch(pager.regex.get, pager.config.search.wrap) + pager.container.cursorPrevMatch(pager.regex.get, wrap, true) else: - pager.container.cursorNextMatch(pager.regex.get, pager.config.search.wrap) + pager.container.cursorNextMatch(pager.regex.get, wrap, true) proc getLineHist(pager: Pager, mode: LineMode): LineHistory = if pager.linehist[mode] == nil: @@ -161,10 +164,12 @@ proc searchBackward(pager: Pager) {.jsfunc.} = proc isearchForward(pager: Pager) {.jsfunc.} = pager.container.pushCursorPos() + pager.isearchpromise = newResolvedPromise() pager.setLineEdit("/", ISEARCH_F) proc isearchBackward(pager: Pager) {.jsfunc.} = pager.container.pushCursorPos() + pager.isearchpromise = newResolvedPromise() pager.setLineEdit("?", ISEARCH_B) proc gotoLine[T: string|int](pager: Pager, s: T = "") {.jsfunc.} = @@ -684,27 +689,34 @@ proc checkRegex(pager: Pager, regex: Result[Regex, string]): Opt[Regex] = proc updateReadLineISearch(pager: Pager, linemode: LineMode) = let lineedit = pager.lineedit.get - case lineedit.state - of CANCEL: - pager.iregex.err() - pager.container.popCursorPos() - pager.container.clearSearchHighlights() - of EDIT: - let x = $lineedit.news - if x != "": pager.iregex = compileSearchRegex(x) - pager.container.popCursorPos(true) - pager.container.pushCursorPos() - if pager.iregex.isSome: - pager.container.hlon = true - if linemode == ISEARCH_F: - pager.container.cursorNextMatch(pager.iregex.get, pager.config.search.wrap) - else: - pager.container.cursorPrevMatch(pager.iregex.get, pager.config.search.wrap) - of FINISH: - pager.regex = pager.checkRegex(pager.iregex) - pager.reverseSearch = linemode == ISEARCH_B - pager.container.clearSearchHighlights() - pager.redraw = true + pager.isearchpromise = pager.isearchpromise.then(proc(): EmptyPromise = + case lineedit.state + of CANCEL: + pager.iregex.err() + pager.container.popCursorPos() + pager.container.clearSearchHighlights() + pager.redraw = true + pager.isearchpromise = nil + of EDIT: + let x = $lineedit.news + if x != "": pager.iregex = compileSearchRegex(x) + pager.container.popCursorPos(true) + pager.container.pushCursorPos() + if pager.iregex.isSome: + pager.container.hlon = true + let wrap = pager.config.search.wrap + return if linemode == ISEARCH_F: + pager.container.cursorNextMatch(pager.iregex.get, wrap, false) + else: + pager.container.cursorPrevMatch(pager.iregex.get, wrap, false) + of FINISH: + pager.regex = pager.checkRegex(pager.iregex) + pager.reverseSearch = linemode == ISEARCH_B + pager.container.clearSearchHighlights() + pager.container.sendCursorPosition() + pager.redraw = true + pager.isearchpromise = nil + ) proc updateReadLine*(pager: Pager) = let lineedit = pager.lineedit.get diff --git a/src/io/promise.nim b/src/io/promise.nim index 27288be8..ba1dad07 100644 --- a/src/io/promise.nim +++ b/src/io/promise.nim @@ -62,21 +62,34 @@ proc resolve*(map: var PromiseMap, promiseid: int) = if map.tab.pop(promiseid, promise): promise.resolve() +proc newResolvedPromise*(): EmptyPromise = + let res = EmptyPromise() + res.resolve() + return res + func empty*(map: PromiseMap): bool = map.tab.len == 0 proc then*(promise: EmptyPromise, cb: (proc())): EmptyPromise {.discardable.} = - if promise == nil: - doAssert false - return promise.cb = cb promise.next = EmptyPromise() if promise.state == PROMISE_FULFILLED: promise.resolve() return promise.next +proc then*(promise: EmptyPromise, cb: (proc(): EmptyPromise)): EmptyPromise + {.discardable.} = + let next = EmptyPromise() + promise.then(proc() = + var p2 = cb() + if p2 != nil: + p2.then(proc() = + next.resolve()) + else: + next.resolve()) + return next + proc then*[T](promise: Promise[T], cb: (proc(x: T))): EmptyPromise {.discardable.} = - doAssert promise != nil return promise.then(proc() = if promise.get != nil: promise.get(promise.opaque, promise.res) @@ -84,7 +97,6 @@ proc then*[T](promise: Promise[T], cb: (proc(x: T))): EmptyPromise {.discardable cb(promise.res)) proc then*[T](promise: EmptyPromise, cb: (proc(): Promise[T])): Promise[T] {.discardable.} = - doAssert promise != nil let next = Promise[T]() promise.then(proc() = var p2 = cb() @@ -97,7 +109,6 @@ proc then*[T](promise: EmptyPromise, cb: (proc(): Promise[T])): Promise[T] {.dis return next proc then*[T](promise: Promise[T], cb: (proc(x: T): EmptyPromise)): EmptyPromise {.discardable.} = - doAssert promise != nil let next = EmptyPromise() promise.then(proc(x: T) = let p2 = cb(x) @@ -109,7 +120,6 @@ proc then*[T](promise: Promise[T], cb: (proc(x: T): EmptyPromise)): EmptyPromise return next proc then*[T, U](promise: Promise[T], cb: (proc(x: T): U)): Promise[U] {.discardable.} = - doAssert promise != nil let next = Promise[U]() promise.then(proc(x: T) = next.res = cb(x) @@ -117,7 +127,6 @@ proc then*[T, U](promise: Promise[T], cb: (proc(x: T): U)): Promise[U] {.discard return next proc then*[T, U](promise: Promise[T], cb: (proc(x: T): Promise[U])): Promise[U] {.discardable.} = - doAssert promise != nil let next = Promise[U]() promise.then(proc(x: T) = let p2 = cb(x) @@ -131,7 +140,6 @@ proc then*[T, U](promise: Promise[T], cb: (proc(x: T): Promise[U])): Promise[U] proc then*[T, U](promise: Promise[T], cb: (proc(x: T): Opt[Promise[U]])): Promise[Opt[U]] {.discardable.} = - doAssert promise != nil let next = Promise[Opt[U]]() promise.then(proc(x: T) = let p2 = cb(x) |