about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-03-14 20:41:08 +0100
committerbptato <nincsnevem662@gmail.com>2024-03-14 20:41:57 +0100
commita8f05f18fdd64485c26b453e62e8073b50e271ef (patch)
tree855b2ba978707197c69338bd5ae6a937d05332a4
parentb5c7a63a3dccf0ea7490d635ee5a8d56d3d49ce1 (diff)
downloadchawan-a8f05f18fdd64485c26b453e62e8073b50e271ef.tar.gz
pager: add "open in editor" keybinding (sE)
only for source for now, rendered document is a bit more complicated

(also, get rid of useless extern/editor module)
-rw-r--r--README.md5
-rw-r--r--bonus/w3m.toml10
-rw-r--r--res/chawan.html1
-rw-r--r--res/config.toml8
-rw-r--r--src/extern/editor.nim57
-rw-r--r--src/loader/loader.nim28
-rw-r--r--src/local/client.nim5
-rw-r--r--src/local/container.nim6
-rw-r--r--src/local/pager.nim60
9 files changed, 103 insertions, 77 deletions
diff --git a/README.md b/README.md
index f479c7dd..234c481c 100644
--- a/README.md
+++ b/README.md
@@ -124,6 +124,11 @@ I thought it'd be a fun excercise to write one by myself, for myself.
 Generally, I'm happy if Chawan works on websites I use frequently. If it
 also works on other websites, that's a bonus.
 
+### Where are the keybindings?
+
+Please run `cha about:chawan` for a list of default keybindings. (By default,
+this is equivalent to `cha -V`.)
+
 ### Where are the w3m keybindings?
 
 At [bonus/w3m.toml](bonus/w3m.toml). Note that not every w3m feature is
diff --git a/bonus/w3m.toml b/bonus/w3m.toml
index 7cc07ef5..3ed27cf9 100644
--- a/bonus/w3m.toml
+++ b/bonus/w3m.toml
@@ -59,7 +59,15 @@ v = 'pager.toggleSource()'
 #TODO edit
 C-l = 'pager.redraw()'
 R = 'pager.reload()'
-#TODO save, save source, view in editor
+#TODO save, save source
+E = '''
+() => {
+	if (pager.url.protocol == "file:")
+		pager.extern(pager.getEditorCommand(pager.url.pathname))
+	else
+		pager.alert("Can't edit other than local file");
+}
+'''
 #TODO buffer selection mode
 'C-@' = '''
 () => {
diff --git a/res/chawan.html b/res/chawan.html
index 33f57ae4..402a46fe 100644
--- a/res/chawan.html
+++ b/res/chawan.html
@@ -85,6 +85,7 @@ beginning)
 beginning)
 <li><kbd>w</kbd>, <kbd>b</kbd>: move cursor to next/previous word
 <li><kbd>\</kbd>: toggle page source view
+<li><kbd>sE</kbd>: see source in editor
 <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 5b130762..618a3847 100644
--- a/res/config.toml
+++ b/res/config.toml
@@ -122,6 +122,14 @@ C-b = 'n => pager.pageUp(n)'
 '>' = 'n => pager.pageRight(n)'
 C-e = 'n => pager.scrollDown(n)'
 C-y = 'n => pager.scrollUp(n)'
+sE = '''
+() => {
+	const url = url.pathname;
+	pager.extern(pager.getEditorCommand(url.protocol == "file:" ?
+		url.pathname :
+		pager.cacheFile));
+}
+'''
 m = '''
 async () => {
 	const c = await pager.askChar("m");
diff --git a/src/extern/editor.nim b/src/extern/editor.nim
deleted file mode 100644
index f0abd4a9..00000000
--- a/src/extern/editor.nim
+++ /dev/null
@@ -1,57 +0,0 @@
-import std/os
-
-import config/config
-import display/term
-import extern/runproc
-import extern/tempfile
-
-func formatEditorName(editor, file: string, line: int): string =
-  result = newStringOfCap(editor.len + file.len)
-  var i = 0
-  var filefound = false
-  while i < editor.len:
-    if editor[i] == '%' and i < editor.high:
-      if editor[i + 1] == 's':
-        result &= file
-        filefound = true
-        i += 2
-        continue
-      elif editor[i + 1] == 'd':
-        result &= $line
-        i += 2
-        continue
-      elif editor[i + 1] == '%':
-        result &= '%'
-        i += 2
-        continue
-    result &= editor[i]
-    inc i
-  if not filefound:
-    if result[^1] != ' ':
-      result &= ' '
-    result &= file
-
-proc openEditor*(term: Terminal, config: Config, file: string, line = 1): bool =
-  var editor = config.external.editor
-  if editor == "":
-    editor = getEnv("EDITOR")
-    if editor == "":
-      editor = "vi %s +%d"
-  let cmd = formatEditorName(editor, file, line)
-  return runProcess(term, cmd)
-
-proc openInEditor*(term: Terminal, config: Config, tmpdir: string,
-    input: var string): bool =
-  try:
-    let tmpf = getTempFile(tmpdir)
-    if input != "":
-      writeFile(tmpf, input)
-    if openEditor(term, config, tmpf):
-      if fileExists(tmpf):
-        input = readFile(tmpf)
-        removeFile(tmpf)
-        return true
-      else:
-        return false
-  except IOError:
-    discard
diff --git a/src/loader/loader.nim b/src/loader/loader.nim
index 83212444..b1bcce38 100644
--- a/src/loader/loader.nim
+++ b/src/loader/loader.nim
@@ -201,25 +201,27 @@ proc getOutputId(ctx: LoaderContext): int =
   result = ctx.outputNum
   inc ctx.outputNum
 
+type AddCacheFileResult = tuple[outputId: int; cacheFile: string]
+
 proc addCacheFile(ctx: LoaderContext; client: ClientData; output: OutputHandle):
-    int =
+    AddCacheFileResult =
   if output.parent != nil and output.parent.cacheId != -1:
     # may happen e.g. if client tries to cache a `cache:' URL
-    return output.parent.cacheId
+    return (output.parent.cacheId, "") #TODO can we get the file name somehow?
   let tmpf = getTempFile(ctx.config.tmpdir)
   let ps = newPosixStream(tmpf, O_CREAT or O_WRONLY, 0o600)
   if unlikely(ps == nil):
-    return -1
+    return (-1, "")
   if output.currentBuffer != nil:
     let n = ps.sendData(output.currentBuffer, output.currentBufferIdx)
     if unlikely(n < output.currentBuffer.len - output.currentBufferIdx):
       ps.close()
-      return -1
+      return (-1, "")
   for buffer in output.buffers:
     let n = ps.sendData(buffer)
     if unlikely(n < buffer.len):
       ps.close()
-      return -1
+      return (-1, "")
   let cacheId = output.outputId
   if output.parent != nil:
     output.parent.cacheId = cacheId
@@ -230,7 +232,7 @@ proc addCacheFile(ctx: LoaderContext; client: ClientData; output: OutputHandle):
       outputId: ctx.getOutputId()
     ))
   client.cacheMap.add(CachedItem(id: cacheId, path: tmpf, refc: 1))
-  return cacheId
+  return (cacheId, tmpf)
 
 proc addFd(ctx: LoaderContext; handle: LoaderHandle) =
   let output = handle.output
@@ -485,8 +487,9 @@ proc addCacheFile(ctx: LoaderContext; stream: SocketStream) =
   let output = ctx.findOutput(outputId)
   assert output != nil
   let targetClient = ctx.clientData[targetPid]
-  let id = ctx.addCacheFile(targetClient, output)
+  let (id, file) = ctx.addCacheFile(targetClient, output)
   stream.swrite(id)
+  stream.swrite(file)
   stream.close()
 
 proc shareCachedItem(ctx: LoaderContext; stream: SocketStream) =
@@ -857,14 +860,19 @@ proc tee*(loader: FileLoader; sourceId, targetPid: int): (SocketStream, int) =
   stream.sread(outputId)
   return (stream, outputId)
 
-proc addCacheFile*(loader: FileLoader; outputId, targetPid: int): int =
+proc addCacheFile*(loader: FileLoader; outputId, targetPid: int):
+    AddCacheFileResult =
   let stream = loader.connect()
   if stream == nil:
-    return -1
+    return (-1, "")
   stream.swrite(lcAddCacheFile)
   stream.swrite(outputId)
   stream.swrite(targetPid)
-  stream.sread(result)
+  var outputId: int
+  var cacheFile: string
+  stream.sread(outputId)
+  stream.sread(cacheFile)
+  return (outputId, cacheFile)
 
 const BufferSize = 4096
 
diff --git a/src/local/client.nim b/src/local/client.nim
index 07f493e6..4f7d9f2b 100644
--- a/src/local/client.nim
+++ b/src/local/client.nim
@@ -486,14 +486,15 @@ proc acceptBuffers(client: Client) =
     if item.fdin != -1:
       let outputId = item.istreamOutputId
       if container.cacheId == -1:
-        container.cacheId = loader.addCacheFile(outputId, loader.clientPid)
+        (container.cacheId, container.cacheFile) = loader.addCacheFile(outputId,
+          loader.clientPid)
       var outCacheId = container.cacheId
       let pid = container.process
       if item.fdout == item.fdin:
         loader.shareCachedItem(container.cacheId, pid)
         loader.resume(@[item.istreamOutputId])
       else:
-        outCacheId = loader.addCacheFile(item.ostreamOutputId, pid)
+        outCacheId = loader.addCacheFile(item.ostreamOutputId, pid).outputId
         loader.resume(@[item.istreamOutputId, item.ostreamOutputId])
       # pass down fdout
       container.setStream(stream, registerFun, item.fdout, outCacheId)
diff --git a/src/local/container.nim b/src/local/container.nim
index a2b78aa5..7946200a 100644
--- a/src/local/container.nim
+++ b/src/local/container.nim
@@ -145,6 +145,7 @@ type
     filter*: BufferFilter
     bgcolor*: CellColor
     tailOnLoad*: bool
+    cacheFile* {.jsget.}: string
 
 jsDestructor(Highlight)
 jsDestructor(Container)
@@ -152,7 +153,7 @@ jsDestructor(Container)
 proc newContainer*(config: BufferConfig; url: URL; request: Request;
     attrs: WindowAttributes; title: string; redirectdepth: int;
     canreinterpret: bool; contentType: Option[string];
-    charsetStack: seq[Charset]; cacheId: int): Container =
+    charsetStack: seq[Charset]; cacheId: int; cacheFile: string): Container =
   return Container(
     url: url,
     request: request,
@@ -167,7 +168,8 @@ proc newContainer*(config: BufferConfig; url: URL; request: Request;
     ),
     canreinterpret: canreinterpret,
     loadinfo: "Connecting to " & request.url.host & "...",
-    cacheId: cacheId
+    cacheId: cacheId,
+    cacheFile: cacheFile
   )
 
 func location(container: Container): URL {.jsfget.} =
diff --git a/src/local/pager.nim b/src/local/pager.nim
index de80b9b6..bf9d3168 100644
--- a/src/local/pager.nim
+++ b/src/local/pager.nim
@@ -18,7 +18,6 @@ import config/mailcap
 import config/mimetypes
 import display/lineedit
 import display/term
-import extern/editor
 import extern/runproc
 import extern/stdio
 import extern/tempfile
@@ -523,7 +522,7 @@ proc onSetLoadInfo(pager: Pager; container: Container) =
 proc newContainer(pager: Pager; bufferConfig: BufferConfig; request: Request;
     title = ""; redirectdepth = 0; canreinterpret = true;
     contentType = none(string); charsetStack: seq[Charset] = @[];
-    url: URL = request.url; cacheId = -1): Container =
+    url: URL = request.url; cacheId = -1; cacheFile = ""): Container =
   request.suspended = true
   if bufferConfig.loaderConfig.cookieJar != nil:
     # loader stores cookie jars per client, but we have no client yet.
@@ -549,7 +548,8 @@ proc newContainer(pager: Pager; bufferConfig: BufferConfig; request: Request;
     canreinterpret,
     contentType,
     charsetStack,
-    cacheId
+    cacheId,
+    cacheFile
   )
   pager.connectingContainers.add(ConnectingContainerItem(
     state: ccsBeforeResult,
@@ -568,7 +568,8 @@ proc newContainerFrom(pager: Pager; container: Container; contentType: string):
     contentType = some(contentType),
     charsetStack = container.charsetStack,
     url = container.url,
-    cacheId = container.cacheId
+    cacheId = container.cacheId,
+    cacheFile = container.cacheFile
   )
 
 func findConnectingContainer*(pager: Pager; fd: int): int =
@@ -762,6 +763,55 @@ proc toggleSource(pager: Pager) {.jsfunc.} =
       pager.container.sourcepair = container
       pager.addContainer(container)
 
+func formatEditorName(editor, file: string; line: int): string =
+  result = newStringOfCap(editor.len + file.len)
+  var i = 0
+  var filefound = false
+  while i < editor.len:
+    if editor[i] == '%' and i < editor.high:
+      if editor[i + 1] == 's':
+        result &= file
+        filefound = true
+        i += 2
+        continue
+      elif editor[i + 1] == 'd':
+        result &= $line
+        i += 2
+        continue
+      elif editor[i + 1] == '%':
+        result &= '%'
+        i += 2
+        continue
+    result &= editor[i]
+    inc i
+  if not filefound:
+    if result[^1] != ' ':
+      result &= ' '
+    result &= file
+
+func getEditorCommand(pager: Pager; file: string; line = 1): string {.jsfunc.} =
+  var editor = pager.config.external.editor
+  if editor == "":
+    editor = getEnv("EDITOR")
+    if editor == "":
+      editor = "vi %s +%d"
+  return formatEditorName(editor, file, line)
+
+proc openInEditor(pager: Pager; input: var string): bool =
+  try:
+    let tmpf = getTempFile(pager.tmpdir)
+    if input != "":
+      writeFile(tmpf, input)
+    let cmd = pager.getEditorCommand(tmpf)
+    if pager.term.runProcess(cmd):
+      if fileExists(tmpf):
+        input = readFile(tmpf)
+        removeFile(tmpf)
+        return true
+  except IOError:
+    discard
+  return false
+
 proc windowChange*(pager: Pager) =
   let oldAttrs = pager.attrs
   pager.term.windowChange()
@@ -1531,7 +1581,7 @@ proc handleEvent0(pager: Pager; container: Container; event: ContainerEvent):
   of cetReadArea:
     if container == pager.container:
       var s = event.tvalue
-      if openInEditor(pager.term, pager.config, pager.tmpdir, s):
+      if pager.openInEditor(s):
         pager.container.readSuccess(s)
       else:
         pager.container.readCanceled()