about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--bonus/w3m.toml2
-rw-r--r--doc/api.md8
-rw-r--r--doc/config.md11
-rw-r--r--res/chawan.html5
-rw-r--r--res/config.toml4
-rw-r--r--src/local/pager.nim173
6 files changed, 147 insertions, 56 deletions
diff --git a/bonus/w3m.toml b/bonus/w3m.toml
index a8ab54e0..4a3e6cdf 100644
--- a/bonus/w3m.toml
+++ b/bonus/w3m.toml
@@ -103,7 +103,7 @@ U = 'cmd.pager.load'
 V = 'cmd.pager.load' #TODO file only
 #TODO exec shell
 # Buffer operations
-B = 'cmd.pager.discardBuffer'
+B = 'cmd.pager.discardBufferPrev'
 v = 'cmd.pager.toggleSource'
 #TODO edit
 C-l = 'cmd.buffer.redraw'
diff --git a/doc/api.md b/doc/api.md
index 5d8f7b2f..ac928baf 100644
--- a/doc/api.md
+++ b/doc/api.md
@@ -122,10 +122,10 @@ Use this for loading automatically retrieved (i.e. non-user-provided) URLs.</td>
 </tr>
 
 <tr>
-<td>`discardBuffer()`</td>
-<td>Discard the current buffer, and move back to its previous sibling buffer,
-or if that doesn't exist, to its parent. If the current buffer is a root buffer
-(i.e. it has no parent), move to the next sibling buffer instead.</td>
+<td>`discardBuffer(buffer = pager.buffer, dir = pager.navDirection)`</td>
+<td>Discard `buffer`, then move back to the buffer opposite to `dir`.
+Possible values of `dir` are: "prev", "next", "prev-sibling", "next-sibling",
+"parent", "first-child", "any".</td>
 </tr>
 
 <tr>
diff --git a/doc/config.md b/doc/config.md
index 2177eba2..d7b92723 100644
--- a/doc/config.md
+++ b/doc/config.md
@@ -879,9 +879,14 @@ company or their product in any way.)</td>
 
 <tr>
 <td>`cmd.pager.discardBuffer`</td>
-<td>Discard the current buffer, and move back to its previous sibling buffer,
-or if that doesn't exist, to its parent. If the current buffer is a root buffer
-(i.e. it has no parent), move to the next sibling buffer instead.</td>
+<td>Discard the current buffer, and move back to the previous/next buffer
+depending on what the previously viewed buffer was.</td>
+</tr>
+
+<tr>
+<td>`cmd.pager.discardBufferPrev`, `cmd.pager.discardBufferNext`</td>
+<td>Discard the current buffer, and move back to the previous/next buffer, or
+open the link under the cursor.</td>
 </tr>
 
 <tr>
diff --git a/res/chawan.html b/res/chawan.html
index 1faf679e..43c093e8 100644
--- a/res/chawan.html
+++ b/res/chawan.html
@@ -57,7 +57,10 @@ up/down by one row
 <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 (delete) current buffer
+<li><kbd>D</kbd>: discard (delete) current buffer, then move back to where you
+came from
+<li><kbd>d,</kbd>, <kbd>d.</kbd>: discard (delete) current
+buffer, then move to previous/next buffer
 <li><kbd>M-y</kbd>: copy current buffer's URL to clipboard (needs xsel)
 <li><kbd>yu</kbd>: copy the link currently under the cursor to clipboard (needs
 xsel)
diff --git a/res/config.toml b/res/config.toml
index 7780db51..b1012a3c 100644
--- a/res/config.toml
+++ b/res/config.toml
@@ -62,6 +62,8 @@ reloadBuffer = '() => pager.reload()'
 lineInfo = '() => pager.lineInfo()'
 toggleSource = '() => pager.toggleSource()'
 discardBuffer = '() => pager.discardBuffer()'
+discardBufferPrev = '() => pager.discardBuffer(pager.buffer, "prev")'
+discardBufferNext = '() => pager.discardBuffer(pager.buffer, "next")'
 discardTree = '() => pager.discardTree()'
 prevBuffer = '() => pager.prevBuffer()'
 prevSiblingBuffer = '() => pager.prevSiblingBuffer()'
@@ -378,6 +380,8 @@ U = 'cmd.pager.reloadBuffer'
 C-g = 'cmd.pager.lineInfo'
 '\' = 'cmd.pager.toggleSource'
 D = 'cmd.pager.discardBuffer'
+'d,' = 'cmd.pager.discardBufferPrev'
+'d.' = 'cmd.pager.discardBufferNext'
 M-d = 'cmd.pager.discardTree'
 ',' = 'cmd.pager.prevBuffer'
 'M-,' = 'cmd.pager.prevSiblingBuffer'
diff --git a/src/local/pager.nim b/src/local/pager.nim
index 3a30a8f8..367802d9 100644
--- a/src/local/pager.nim
+++ b/src/local/pager.nim
@@ -99,6 +99,15 @@ type
     url: URL
     username: string
 
+  NavDirection = enum
+    ndPrev = "prev"
+    ndNext = "next"
+    ndPrevSibling = "prev-sibling"
+    ndNextSibling = "next-sibling"
+    ndParent = "parent"
+    ndFirstChild
+    ndAny = "any"
+
   Pager* = ref object
     alertState: PagerAlertState
     alerts*: seq[string]
@@ -124,6 +133,8 @@ type
     linehist: array[LineMode, LineHistory]
     linemode: LineMode
     loader*: FileLoader
+    luctx: LUContext
+    navDirection {.jsget.}: NavDirection
     notnum*: bool # has a non-numeric character been input already?
     numload*: int # number of pages currently being loaded
     precnum*: int32 # current number prefix (when vi-numeric-prefix is true)
@@ -136,7 +147,6 @@ type
     statusgrid*: FixedGrid
     term*: Terminal
     unreg*: seq[Container]
-    luctx: LUContext
 
 jsDestructor(Pager)
 
@@ -607,49 +617,118 @@ proc dupeBuffer(pager: Pager; container: Container; url: URL) =
 proc dupeBuffer(pager: Pager) {.jsfunc.} =
   pager.dupeBuffer(pager.container, pager.container.url)
 
+func findPrev(container: Container): Container =
+  if container.parent == nil:
+    return nil
+  let n = container.parent.children.find(container)
+  assert n != -1, "Container not a child of its parent"
+  if n == 0:
+    return container.parent
+  var container = container.parent.children[n - 1]
+  while container.children.len > 0:
+    container = container.children[^1]
+  return container
+
+func findNext(container: Container): Container =
+  if container.children.len > 0:
+    return container.children[0]
+  var container = container
+  while container.parent != nil:
+    let n = container.parent.children.find(container)
+    assert n != -1, "Container not a child of its parent"
+    if n < container.parent.children.high:
+      return container.parent.children[n + 1]
+    container = container.parent
+  return nil
+
+func findPrevSibling(container: Container): Container =
+  if container.parent == nil:
+    return nil
+  var n = container.parent.children.find(container)
+  assert n != -1, "Container not a child of its parent"
+  if n == 0:
+    n = container.parent.children.len
+  return container.parent.children[n - 1]
+
+func findNextSibling(container: Container): Container =
+  if container.parent == nil:
+    return nil
+  var n = container.parent.children.find(container)
+  assert n != -1, "Container not a child of its parent"
+  if n == container.parent.children.high:
+    n = -1
+  return container.parent.children[n + 1]
+
+func findParent(container: Container): Container =
+  return container.parent
+
+func findFirstChild(container: Container): Container =
+  if container.children.len == 0:
+    return nil
+  return container.children[0]
+
+func findAny(container: Container): Container =
+  let prev = container.findPrev()
+  if prev != nil:
+    return prev
+  return container.findNext()
+
+func opposite(dir: NavDirection): NavDirection =
+  const Map = [
+    ndPrev: ndNext,
+    ndNext: ndPrev,
+    ndPrevSibling: ndNextSibling,
+    ndNextSibling: ndPrevSibling,
+    ndParent: ndFirstChild,
+    ndFirstChild: ndParent,
+    ndAny: ndAny
+  ]
+  return Map[dir]
+
+func find(container: Container; dir: NavDirection): Container =
+  return case dir
+  of ndPrev: container.findPrev()
+  of ndNext: container.findNext()
+  of ndPrevSibling: container.findPrevSibling()
+  of ndNextSibling: container.findNextSibling()
+  of ndParent: container.findParent()
+  of ndFirstChild: container.findFirstChild()
+  of ndAny: container.findAny()
+
 # The prevBuffer and nextBuffer procedures emulate w3m's PREV and NEXT
 # commands by traversing the container tree in a depth-first order.
 proc prevBuffer*(pager: Pager): bool {.jsfunc.} =
+  pager.navDirection = ndPrev
   if pager.container == nil:
     return false
-  if pager.container.parent == nil:
+  let prev = pager.container.findPrev()
+  if prev == nil:
     return false
-  let n = pager.container.parent.children.find(pager.container)
-  assert n != -1, "Container not a child of its parent"
-  if n > 0:
-    var container = pager.container.parent.children[n - 1]
-    while container.children.len > 0:
-      container = container.children[^1]
-    pager.setContainer(container)
-  else:
-    pager.setContainer(pager.container.parent)
+  pager.setContainer(prev)
   return true
 
 proc nextBuffer*(pager: Pager): bool {.jsfunc.} =
+  pager.navDirection = ndNext
   if pager.container == nil:
     return false
-  if pager.container.children.len > 0:
-    pager.setContainer(pager.container.children[0])
-    return true
-  var container = pager.container
-  while container.parent != nil:
-    let n = container.parent.children.find(container)
-    assert n != -1, "Container not a child of its parent"
-    if n < container.parent.children.high:
-      pager.setContainer(container.parent.children[n + 1])
-      return true
-    container = container.parent
-  return false
+  let next = pager.container.findNext()
+  if next == nil:
+    return false
+  pager.setContainer(next)
+  return true
 
 proc parentBuffer(pager: Pager): bool {.jsfunc.} =
+  pager.navDirection = ndParent
   if pager.container == nil:
     return false
-  if pager.container.parent == nil:
+  let parent = pager.container.findParent()
+  if parent == nil:
     return false
-  pager.setContainer(pager.container.parent)
+  pager.setContainer(parent)
   return true
 
 proc prevSiblingBuffer(pager: Pager): bool {.jsfunc.} =
+  pager.navDirection = ndPrevSibling
   if pager.container == nil:
     return false
   if pager.container.parent == nil:
@@ -662,6 +741,7 @@ proc prevSiblingBuffer(pager: Pager): bool {.jsfunc.} =
   return true
 
 proc nextSiblingBuffer(pager: Pager): bool {.jsfunc.} =
+  pager.navDirection = ndNextSibling
   if pager.container == nil:
     return false
   if pager.container.parent == nil:
@@ -699,13 +779,12 @@ proc replace*(pager: Pager; target, container: Container) =
   if pager.container == target:
     pager.setContainer(container)
 
-proc deleteContainer(pager: Pager; container: Container) =
+proc deleteContainer(pager: Pager; container, setTarget: Container) =
   if container.loadState == lsLoading:
     container.cancel()
   if container.sourcepair != nil:
     container.sourcepair.sourcepair = nil
     container.sourcepair = nil
-  var setTarget: Container = nil
   if container.parent != nil:
     let parent = container.parent
     let n = parent.children.find(container)
@@ -715,20 +794,12 @@ proc deleteContainer(pager: Pager; container: Container) =
       child.parent = container.parent
       parent.children.insert(child, n + 1)
     parent.children.delete(n)
-    if n == 0:
-      setTarget = parent
-    else:
-      setTarget = parent.children[n - 1]
   elif container.children.len > 0:
     let parent = container.children[0]
     parent.parent = nil
     for i in 1..container.children.high:
       container.children[i].parent = parent
       parent.children.add(container.children[i])
-    setTarget = parent
-  else:
-    for child in container.children:
-      child.parent = nil
   container.parent = nil
   container.children.setLen(0)
   if container.replace != nil:
@@ -741,18 +812,23 @@ proc deleteContainer(pager: Pager; container: Container) =
     pager.forkserver.removeChild(container.process)
     pager.loader.removeClient(container.process)
 
-proc discardBuffer*(pager: Pager; container = none(Container)) {.jsfunc.} =
-  let c = container.get(pager.container)
-  if c == nil or c.parent == nil and c.children.len == 0:
-    pager.alert("Cannot discard last buffer!")
+proc discardBuffer*(pager: Pager; container = none(Container);
+    dir = none(NavDirection)) {.jsfunc.} =
+  if dir.isSome:
+    pager.navDirection = dir.get.opposite()
+  let container = container.get(pager.container)
+  let dir = pager.navDirection.opposite()
+  let setTarget = container.find(dir)
+  if container == nil or setTarget == nil:
+    pager.alert("No buffer in direction: " & $dir)
   else:
-    pager.deleteContainer(c)
+    pager.deleteContainer(container, setTarget)
 
 proc discardTree(pager: Pager; container = none(Container)) {.jsfunc.} =
   let container = container.get(pager.container)
   if container != nil:
     for c in container.descendants:
-      pager.deleteContainer(c)
+      pager.deleteContainer(container, nil)
   else:
     pager.alert("Buffer has no children!")
 
@@ -956,6 +1032,7 @@ proc gotoURL(pager: Pager; request: Request; prevurl = none(URL);
     contentType = none(string); cs = CHARSET_UNKNOWN; replace: Container = nil;
     redirectDepth = 0; referrer: Container = nil; save = false;
     url: URL = nil) =
+  pager.navDirection = ndNext
   if referrer != nil and referrer.config.referer_from:
     request.referrer = referrer.url
   let url = if url != nil: url else: request.url
@@ -1579,7 +1656,7 @@ proc redirectTo(pager: Pager; container: Container; request: Request) =
 
 proc fail(pager: Pager; container: Container; errorMessage: string) =
   dec pager.numload
-  pager.deleteContainer(container)
+  pager.deleteContainer(container, container.find(ndAny))
   if container.retry.len > 0:
     pager.gotoURL(newRequest(container.retry.pop()),
       contentType = container.contentType)
@@ -1588,6 +1665,8 @@ proc fail(pager: Pager; container: Container; errorMessage: string) =
 
 proc redirect(pager: Pager; container: Container; response: Response;
     request: Request) =
+  # if redirection fails, then we need some other container to move to...
+  let failTarget = container.find(ndAny)
   # still need to apply response, or we lose cookie jars.
   container.applyResponse(response, pager.config.external.mime_types)
   if container.redirectDepth < pager.config.network.max_redirect:
@@ -1604,7 +1683,7 @@ proc redirect(pager: Pager; container: Container; response: Response;
       )
   else:
     pager.alert("Error: maximum redirection depth reached")
-    pager.deleteContainer(container)
+    pager.deleteContainer(container, failTarget)
 
 proc askDownloadPath(pager: Pager; container: Container; response: Response) =
   var buf = pager.config.external.download_dir
@@ -1618,7 +1697,7 @@ proc askDownloadPath(pager: Pager; container: Container; response: Response) =
     outputId: response.outputId,
     stream: response.body
   )
-  pager.deleteContainer(container)
+  pager.deleteContainer(container, container.find(ndAny))
   pager.redraw = true
   pager.refreshStatusMsg()
   dec pager.numload
@@ -1681,11 +1760,11 @@ proc connected(pager: Pager; container: Container; response: Response) =
       istreamOutputId: response.outputId
     ))
     if container.replace != nil:
-      pager.deleteContainer(container.replace)
+      pager.deleteContainer(container.replace, container.find(ndAny))
       container.replace = nil
   else:
     dec pager.numload
-    pager.deleteContainer(container)
+    pager.deleteContainer(container, container.find(ndAny))
     pager.redraw = true
     pager.refreshStatusMsg()
 
@@ -1816,7 +1895,7 @@ proc handleEvent0(pager: Pager; container: Container; event: ContainerEvent):
     else:
       let item = pager.connectingContainers[i]
       dec pager.numload
-      pager.deleteContainer(container)
+      pager.deleteContainer(container, container.find(ndAny))
       pager.connectingContainers.del(i)
       pager.selector.unregister(item.stream.fd)
       pager.loader.unregistered.add(item.stream.fd)