about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-07-11 21:08:13 +0200
committerbptato <nincsnevem662@gmail.com>2023-07-11 21:13:33 +0200
commit607545c800f8d2d85943fc8bf2ee246e9901f3b2 (patch)
tree3a565bf97e7fb6cd41f8e36d96aa8afa87e8bbe3 /src
parent34cffe0b658511ac18429c41453c24505c47c6d4 (diff)
downloadchawan-607545c800f8d2d85943fc8bf2ee246e9901f3b2.tar.gz
Buffer search fixes & improvements
* Fix race condition in updateReadLineISearch
* Disable reshape during isearch
Diffstat (limited to 'src')
-rw-r--r--src/buffer/container.nim43
-rw-r--r--src/display/pager.nim66
-rw-r--r--src/io/promise.nim26
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)