about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2021-12-26 23:50:12 +0100
committerbptato <nincsnevem662@gmail.com>2021-12-26 23:50:12 +0100
commit2b1dac71b2886ede9950f4ef91e7a0eb7b3e5ed5 (patch)
tree0971b3625f6cbc9d978ccb1a01bf7fde815dd94b /src
parent647089a99e3c44c4115274a8822ca0a4dd947d67 (diff)
downloadchawan-2b1dac71b2886ede9950f4ef91e7a0eb7b3e5ed5.tar.gz
Basic content type implementation
Diffstat (limited to 'src')
-rw-r--r--src/client.nim52
-rw-r--r--src/css/parser.nim1
-rw-r--r--src/io/buffer.nim44
-rw-r--r--src/io/cell.nim2
-rw-r--r--src/render/rendertext.nim11
-rw-r--r--src/types/mime.nim25
6 files changed, 99 insertions, 36 deletions
diff --git a/src/client.nim b/src/client.nim
index d0956126..23ebb9f3 100644
--- a/src/client.nim
+++ b/src/client.nim
@@ -7,9 +7,9 @@ import os
 import io/buffer
 import io/lineedit
 import config/config
-import html/parser
 import utils/twtstr
 import css/sheet
+import types/mime
 import types/url
 
 type
@@ -91,27 +91,28 @@ proc discardBuffer(client: Client) =
 
 proc setupBuffer(client: Client) =
   let buffer = client.buffer
+  if not buffer.ispipe:
+    buffer.contenttype = guessContentType($buffer.location.path)
   buffer.userstyle = client.userstyle
-  buffer.document = parseHtml(newStringStream(buffer.source))
+  buffer.load()
   buffer.render()
   buffer.gotoAnchor()
   buffer.redraw = true
 
 proc readPipe(client: Client) =
   client.buffer = newBuffer()
-  client.buffer.showsource = true
-  try:
-    while true:
-      client.buffer.source &= stdin.readChar()
-  except EOFError:
-    #TODO is this portable at all?
-    if reopen(stdin, "/dev/tty", fmReadWrite):
-      client.setupBuffer()
-    else:
-      client.buffer.drawBuffer()
+  client.buffer.contenttype = "text/plain"
+  client.buffer.ispipe = true
+  client.buffer.istream = newFileStream(stdin)
+  client.buffer.load()
+  #TODO is this portable at all?
+  if reopen(stdin, "/dev/tty", fmReadWrite):
+    client.setupBuffer()
+  else:
+    client.buffer.drawBuffer()
 
 var g_client: Client
-proc gotoUrl(client: Client, url: string, prevurl = none(Url), force = false) =
+proc gotoUrl(client: Client, url: string, prevurl = none(Url), force = false, newbuf = true) =
   var oldurl = prevurl
   if oldurl.isnone and client.buffer != nil:
     oldurl = client.buffer.location.some
@@ -127,12 +128,14 @@ proc gotoUrl(client: Client, url: string, prevurl = none(Url), force = false) =
       try:
         let s = client.getPage(url)
         if s != nil:
-          client.addBuffer()
-          g_client = client
-          setControlCHook(proc() {.noconv.} =
-            g_client.discardBuffer()
-            interruptError())
-          client.buffer.source = s.readAll() #TODO
+          if newbuf:
+            client.addBuffer()
+            g_client = client
+            setControlCHook(proc() {.noconv.} =
+              g_client.discardBuffer()
+              interruptError())
+          client.buffer.istream = s
+          client.buffer.streamclosed = false
         else:
           loadError("Couldn't load " & $url)
       except IOError, OSError:
@@ -144,7 +147,6 @@ proc gotoUrl(client: Client, url: string, prevurl = none(Url), force = false) =
     client.setupBuffer()
   else:
     loadError("Couldn't parse URL " & url)
-    eprint "none"
 
 proc loadUrl(client: Client, url: string) =
   let firstparse = parseUrl(url)
@@ -160,10 +162,10 @@ proc loadUrl(client: Client, url: string) =
 
 proc reloadPage(client: Client) =
   let pbuffer = client.buffer
-  client.gotoUrl("", none(Url), true)
+  client.gotoUrl("", none(Url), true, false)
   client.buffer.setCursorXY(pbuffer.cursorx, pbuffer.cursory)
   client.buffer.setFromXY(pbuffer.fromx, pbuffer.fromy)
-  client.buffer.showsource = pbuffer.showsource
+  client.buffer.contenttype = pbuffer.contenttype
 
 proc changeLocation(client: Client) =
   let buffer = client.buffer
@@ -189,7 +191,11 @@ proc toggleSource*(client: Client) =
     client.buffer.sourcepair = client.buffer.prev
     client.buffer.sourcepair.sourcepair = client.buffer
     client.buffer.source = client.buffer.prev.source
-    client.buffer.showsource = not client.buffer.prev.showsource
+    let prevtype = client.buffer.prev.contenttype
+    if prevtype == "text/html":
+      client.buffer.contenttype = "text/plain"
+    else:
+      client.buffer.contenttype = "text/html"
     client.setupBuffer()
 
 proc input(client: Client) =
diff --git a/src/css/parser.nim b/src/css/parser.nim
index 5bd2cf8b..c13018de 100644
--- a/src/css/parser.nim
+++ b/src/css/parser.nim
@@ -588,6 +588,7 @@ proc parseStylesheet(state: var CSSParseState): CSSRawStylesheet =
 proc parseStylesheet(inputStream: Stream): CSSRawStylesheet =
   var state = CSSParseState()
   state.tokens = tokenizeCSS(inputStream)
+  inputStream.close()
   return state.parseStylesheet()
 
 proc parseListOfRules(state: var CSSParseState): seq[CSSRule] =
diff --git a/src/io/buffer.nim b/src/io/buffer.nim
index 2610d9b1..a2d44b8c 100644
--- a/src/io/buffer.nim
+++ b/src/io/buffer.nim
@@ -1,4 +1,5 @@
 import options
+import streams
 import terminal
 import unicode
 
@@ -6,6 +7,7 @@ import css/cascade
 import css/sheet
 import html/dom
 import html/tags
+import html/parser
 import io/term
 import io/cell
 import layout/box
@@ -16,6 +18,7 @@ import utils/twtstr
 
 type
   Buffer* = ref object
+    contenttype*: string
     title*: string
     lines*: FlexibleGrid
     display*: FixedGrid
@@ -35,9 +38,10 @@ type
     reshape*: bool
     nostatus*: bool
     location*: Url
-    target*: string
+    ispipe*: bool
+    istream*: Stream
+    streamclosed*: bool
     source*: string
-    showsource*: bool
     rootbox*: CSSBox
     prevnodes*: seq[Node]
     sourcepair*: Buffer
@@ -257,10 +261,14 @@ func hasAnchor*(buffer: Buffer, anchor: string): bool =
   return buffer.document.getElementById(anchor) != nil
 
 func getTitle(buffer: Buffer): string =
-  let titles = buffer.document.getElementsByTag(TAG_TITLE)
-  if titles.len > 0:
-    for text in titles[0].textNodes:
-      result &= text.data.strip().clearControls()
+  if buffer.document != nil:
+    let titles = buffer.document.getElementsByTag(TAG_TITLE)
+    if titles.len > 0:
+      for text in titles[0].textNodes:
+        result &= text.data.strip().clearControls()
+    return
+  if buffer.ispipe:
+    result = "*pipe*"
   else:
     result = $buffer.location
 
@@ -707,11 +715,29 @@ proc updateHover(buffer: Buffer) =
         elem.refreshStyle()
   buffer.prevnodes = nodes
 
-proc render*(buffer: Buffer) =
-  if buffer.showsource:
-    buffer.lines = renderPlainText(buffer.source)
+proc load*(buffer: Buffer) =
+  case buffer.contenttype
+  of "text/html":
+    if not buffer.streamclosed:
+      #TODO not sure what to do with this.
+      #Ideally we could just throw away the source data after parsing but then
+      #source view won't work. Well we could still generate it... best would be a
+      #config option like a) store source b) generate source
+      buffer.source = buffer.istream.readAll()
+      buffer.istream.close()
+      buffer.document = parseHtml(newStringStream(buffer.source))
+      buffer.streamclosed = true
   else:
+    if not buffer.streamclosed:
+      buffer.lines = renderStream(buffer.istream)
+      buffer.istream.close()
+      buffer.streamclosed = true
+
+proc render*(buffer: Buffer) =
+  case buffer.contenttype
+  of "text/html":
     buffer.lines = renderDocument(buffer.document, buffer.attrs, buffer.userstyle)
+  else: discard
   buffer.updateCursor()
 
 proc cursorBufferPos(buffer: Buffer) =
diff --git a/src/io/cell.nim b/src/io/cell.nim
index f5fb9721..fbe35268 100644
--- a/src/io/cell.nim
+++ b/src/io/cell.nim
@@ -244,7 +244,7 @@ proc parseAnsiCode*(formatting: var Formatting, buf: string, fi: int): int =
     inc_check i
   let params = buf.substr(sp, i - 1)
 
-  let si = i
+  #let si = i
   #intermediate bytes
   while 0x20 <= int(buf[i]) and int(buf[i]) <= 0x2F:
     inc_check i
diff --git a/src/render/rendertext.nim b/src/render/rendertext.nim
index 4c6b56ec..b5054e7b 100644
--- a/src/render/rendertext.nim
+++ b/src/render/rendertext.nim
@@ -16,9 +16,8 @@ proc renderPlainText*(text: string): FlexibleGrid =
   while i < text.len:
     case text[i]
     of '\n':
-      if i != text.len - 1:
-        add_format
-        result.addLine()
+      add_format
+      result.addLine()
     of '\r': discard
     of '\t':
       add_format
@@ -35,6 +34,9 @@ proc renderPlainText*(text: string): FlexibleGrid =
       result[^1].str &= text[i]
     inc i
 
+  if result.len > 1 and result[^1].str.len == 0 and result[^1].formats.len == 0:
+    discard result.pop()
+
 proc renderStream*(stream: Stream): FlexibleGrid =
   var format = newFormatting()
   template add_format() =
@@ -64,3 +66,6 @@ proc renderStream*(stream: Stream): FlexibleGrid =
     else:
       add_format
       result[^1].str &= c
+
+  if result.len > 1 and result[^1].str.len == 0 and result[^1].formats.len == 0:
+    discard result.pop()
diff --git a/src/types/mime.nim b/src/types/mime.nim
new file mode 100644
index 00000000..4dfe87e9
--- /dev/null
+++ b/src/types/mime.nim
@@ -0,0 +1,25 @@
+import tables
+
+const DefaultGuess = [
+  ("html", "text/html"),
+  ("htm", "text/html"),
+  ("xhtml", "application/xhtml+xml"),
+  ("xhtm", "application/xhtml+xml"),
+  ("xht", "application/xhtml+xml"),
+].toTable()
+
+proc guessContentType*(path: string): string =
+  var i = path.len - 1
+  var n = 0
+  while i > 0:
+    if path[i] == '/':
+      return "text/plain"
+    if path[i] == '.':
+      n = i
+      break
+    dec i
+  if n > 0:
+    let ext = path.substr(n + 1)
+    if ext in DefaultGuess:
+      return DefaultGuess[ext]
+  return "text/plain"