about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-08-27 13:00:37 +0200
committerbptato <nincsnevem662@gmail.com>2023-08-27 13:00:37 +0200
commit9991bd3393483158ab0d1b9d995f695dee3c65dc (patch)
tree51de19187c968e6edccba587148322db33952cbe
parent48f1306f3a9cc5e190907c4a818fc62cad7d9024 (diff)
downloadchawan-9991bd3393483158ab0d1b9d995f695dee3c65dc.tar.gz
config: allow modification through JS
This used to be possible until I moved everything under separate
headers to their respective objects.
Now it works again, mostly; modification of some attributes is still
missing.
-rw-r--r--bonus/w3m.toml5
-rw-r--r--res/config.toml5
-rw-r--r--src/config/config.nim103
-rw-r--r--src/css/cascade.nim21
-rw-r--r--src/types/color.nim58
5 files changed, 133 insertions, 59 deletions
diff --git a/bonus/w3m.toml b/bonus/w3m.toml
index 55c792f0..3fad5fea 100644
--- a/bonus/w3m.toml
+++ b/bonus/w3m.toml
@@ -67,7 +67,10 @@ C-s = 'pager.searchForward()'
 C-r = 'pager.searchBackward()'
 n = 'pager.searchNext()'
 N = 'pager.searchPrev()'
-C-w = 'config.searchwrap = !config.searchwrap; pager.alert("Wrap search " + (config.searchwrap ? "on" : "off"))'
+C-w = '''
+config.search.wrap = !config.search.wrap;
+pager.alert("Wrap search " + (config.search.wrap ? "on" : "off"));
+'''
 # Misc
 #TODO shell out, help file, options, cookies
 C-c = 'pager.cancel()'
diff --git a/res/config.toml b/res/config.toml
index c8b0091a..36d694fb 100644
--- a/res/config.toml
+++ b/res/config.toml
@@ -123,7 +123,10 @@ n = 'pager.searchNext()'
 N = 'pager.searchPrev()'
 c = 'pager.peek()'
 u = 'pager.peekCursor()'
-C-w = 'config.searchwrap = !config.searchwrap; pager.alert("Wrap search " + (config.searchwrap ? "on" : "off"))'
+C-w = '''
+config.search.wrap = !config.search.wrap;
+pager.alert("Wrap search " + (config.search.wrap ? "on" : "off"));
+'''
 
 [line]
 C-m = 'line.submit()'
diff --git a/src/config/config.nim b/src/config/config.nim
index 0eb2911b..9c2cd0a8 100644
--- a/src/config/config.nim
+++ b/src/config/config.nim
@@ -66,62 +66,62 @@ type
     substitute_url*: (proc(s: string): Result[string, JSError])
 
   StartConfig = object
-    visual_home*: string
-    startup_script*: string
-    headless*: bool
+    visual_home* {.jsgetset.}: string
+    startup_script* {.jsgetset.}: string
+    headless* {.jsgetset.}: bool
 
   CSSConfig = object
-    stylesheet*: string
+    stylesheet* {.jsgetset.}: string
 
   SearchConfig = object
-    wrap*: bool
+    wrap* {.jsgetset.}: bool
 
   EncodingConfig = object
-    display_charset*: Opt[Charset]
-    document_charset*: seq[Charset]
+    display_charset* {.jsgetset.}: Opt[Charset]
+    document_charset* {.jsgetset.}: seq[Charset]
 
   ExternalConfig = object
-    tmpdir*: string
-    editor*: string
-    mailcap*: seq[string]
-    mime_types*: seq[string]
+    tmpdir* {.jsgetset.}: string
+    editor* {.jsgetset.}: string
+    mailcap* {.jsgetset.}: seq[string]
+    mime_types* {.jsgetset.}: seq[string]
 
   NetworkConfig = object
-    max_redirect*: int32
-    prepend_https*: bool
-    proxy*: Opt[string]
-    default_headers*: Table[string, string]
+    max_redirect* {.jsgetset.}: int32
+    prepend_https* {.jsgetset.}: bool
+    proxy* {.jsgetset.}: Opt[string]
+    default_headers* {.jsgetset.}: Table[string, string]
 
   DisplayConfig = object
-    color_mode*: Opt[ColorMode]
-    format_mode*: Opt[FormatMode]
-    no_format_mode*: FormatMode
-    emulate_overline*: bool
-    alt_screen*: Opt[bool]
-    highlight_color*: RGBAColor
-    double_width_ambiguous*: bool
-    minimum_contrast*: int32
-    force_clear*: bool
-    set_title*: bool
-    default_background_color*: RGBColor
-    default_foreground_color*: RGBColor
-
-  #TODO: add JS wrappers for objects
+    color_mode* {.jsgetset.}: Opt[ColorMode]
+    format_mode*: Opt[FormatMode] #TODO getset
+    no_format_mode*: FormatMode #TODO getset
+    emulate_overline* {.jsgetset.}: bool
+    alt_screen* {.jsgetset.}: Opt[bool]
+    highlight_color* {.jsgetset.}: RGBAColor
+    double_width_ambiguous* {.jsgetset.}: bool
+    minimum_contrast* {.jsgetset.}: int32
+    force_clear* {.jsgetset.}: bool
+    set_title* {.jsgetset.}: bool
+    default_background_color* {.jsgetset.}: RGBColor
+    default_foreground_color* {.jsgetset.}: RGBColor
+
   Config* = ref ConfigObj
   ConfigObj* = object
-    configdir: string
-    includes: seq[string]
-    start*: StartConfig
-    search*: SearchConfig
-    css*: CSSConfig
-    encoding*: EncodingConfig
-    external*: ExternalConfig
-    network*: NetworkConfig
-    display*: DisplayConfig
+    configdir {.jsget.}: string
+    includes {.jsget.}: seq[string]
+    start* {.jsget.}: StartConfig
+    search* {.jsget.}: SearchConfig
+    css* {.jsget.}: CSSConfig
+    encoding* {.jsget.}: EncodingConfig
+    external* {.jsget.}: ExternalConfig
+    network* {.jsget.}: NetworkConfig
+    display* {.jsget.}: DisplayConfig
+    #TODO getset
     siteconf: seq[StaticSiteConfig]
     omnirule: seq[StaticOmniRule]
-    page*: ActionMap
-    line*: ActionMap
+    page* {.jsget.}: ActionMap
+    line* {.jsget.}: ActionMap
 
   BufferConfig* = object
     userstyle*: string
@@ -140,6 +140,13 @@ type
     tmpdir*: string
     ambiguous_double*: bool
 
+jsDestructor(StartConfig)
+jsDestructor(CSSConfig)
+jsDestructor(SearchConfig)
+jsDestructor(EncodingConfig)
+jsDestructor(ExternalConfig)
+jsDestructor(NetworkConfig)
+jsDestructor(DisplayConfig)
 jsDestructor(Config)
 
 proc `[]=`(a: var ActionMap, b, c: string) {.borrow.}
@@ -147,6 +154,7 @@ proc `[]`*(a: ActionMap, b: string): string {.borrow.}
 proc contains*(a: ActionMap, b: string): bool {.borrow.}
 proc getOrDefault(a: ActionMap, b: string): string {.borrow.}
 proc hasKeyOrPut(a: var ActionMap, b, c: string): bool {.borrow.}
+proc toJS(ctx: JSContext, a: ActionMap): JSValue {.borrow.}
 
 func getForkServerConfig*(config: Config): ForkServerConfig =
   return ForkServerConfig(
@@ -469,16 +477,16 @@ proc parseConfigValue(x: var RGBAColor, v: TomlValue, k: string) =
   typeCheck(v, VALUE_STRING, k)
   let c = parseRGBAColor(v.s)
   if c.isNone:
-      raise newException(ValueError, "invalid color '" & v.s &
-        "' for key " & k)
+    raise newException(ValueError, "invalid color '" & v.s &
+      "' for key " & k)
   x = c.get
 
 proc parseConfigValue(x: var RGBColor, v: TomlValue, k: string) =
   typeCheck(v, VALUE_STRING, k)
   let c = parseLegacyColor(v.s)
   if c.isNone:
-      raise newException(ValueError, "invalid color '" & v.s &
-        "' for key " & k)
+    raise newException(ValueError, "invalid color '" & v.s &
+      "' for key " & k)
   x = c.get
 
 proc parseConfigValue[T](x: var Opt[T], v: TomlValue, k: string) =
@@ -576,4 +584,11 @@ proc readConfig*(): Config =
   result.readConfig(getConfigDir() / "chawan")
 
 proc addConfigModule*(ctx: JSContext) =
+  ctx.registerType(StartConfig)
+  ctx.registerType(CSSConfig)
+  ctx.registerType(SearchConfig)
+  ctx.registerType(EncodingConfig)
+  ctx.registerType(ExternalConfig)
+  ctx.registerType(NetworkConfig)
+  ctx.registerType(DisplayConfig)
   ctx.registerType(Config)
diff --git a/src/css/cascade.nim b/src/css/cascade.nim
index 53eff5cf..598e359f 100644
--- a/src/css/cascade.nim
+++ b/src/css/cascade.nim
@@ -122,9 +122,10 @@ func calcPresentationalHints(element: Element): CSSComputedValues =
     if s.isSome and s.get.num != 0:
       set_cv "height", s.get
   template map_bgcolor =
-    let c = parseLegacyColor(element.attr("bgcolor"))
-    if c.isSome:
-      set_cv "background-color", c.get
+    let s = element.attr("bgcolor")
+    if s != "":
+      let c = parseLegacyColor0(s)
+      set_cv "background-color", c
   template map_size =
     let s = element.attrul("size")
     if s.isSome:
@@ -150,13 +151,15 @@ func calcPresentationalHints(element: Element): CSSComputedValues =
       set_cv "margin-left", CSSLengthAuto #TODO should be inline-start
       set_cv "margin-right", CSSLengthAuto #TODO should be inline-end
   template map_text =
-    let c = parseLegacyColor(element.attr("text"))
-    if c.isSome:
-      set_cv "color", c.get
+    let s = element.attr("text")
+    if s != "":
+      let c = parseLegacyColor0(s)
+      set_cv "color", c
   template map_color =
-    let c = parseLegacyColor(element.attr("color"))
-    if c.isSome:
-      set_cv "color", c.get
+    let s = element.attr("color")
+    if s != "":
+      let c = parseLegacyColor0(s)
+      set_cv "color", c
   template map_colspan =
     let colspan = element.attrulgz("colspan")
     if colspan.isSome:
diff --git a/src/types/color.nim b/src/types/color.nim
index e628b9ad..f96bfc49 100644
--- a/src/types/color.nim
+++ b/src/types/color.nim
@@ -2,6 +2,9 @@ import options
 import strutils
 import tables
 
+import bindings/quickjs
+import js/exception
+import js/javascript
 import utils/twtstr
 
 type
@@ -455,12 +458,12 @@ func parseRGBAColor*(s: string): Option[RGBAColor] =
     return parseHexColor(s[2..^1])
   return parseHexColor(s)
 
-func parseLegacyColor*(s: string): Option[RGBColor] =
+func parseLegacyColor0*(s: string): RGBColor =
   if s == "": return
   let s = s.strip(chars = AsciiWhitespace)
   if s == "transparent": return
   if s in ColorsRGB:
-    return some(ColorsRGB[s])
+    return ColorsRGB[s]
   block hex:
     if s.len == 4:
       for c in s:
@@ -469,7 +472,7 @@ func parseLegacyColor*(s: string): Option[RGBColor] =
       let c = (hexValue(s[0]) * 17 shl 16) or
         (hexValue(s[1]) * 17 shl 8) or
         (hexValue(s[2]) * 17)
-      return some(RGBColor(c))
+      return RGBColor(c)
   # Seriously, what the hell.
   var s2 = if s[0] == '#':
     s.substr(1)
@@ -492,4 +495,51 @@ func parseLegacyColor*(s: string): Option[RGBColor] =
     (hexValue(c1[0]) shl 20) or (hexValue(c1[1]) shl 16) or
     (hexValue(c2[0]) shl 12) or (hexValue(c2[1]) shl 8) or
     (hexValue(c3[0]) shl 4) or hexValue(c3[1])
-  return some(RGBColor(c))
+  return RGBColor(c)
+
+func parseLegacyColor*(s: string): JSResult[RGBColor] =
+  if s == "":
+    return err(newTypeError("Color value must not be the empty string"))
+  return ok(parseLegacyColor0(s))
+
+proc toJS*(ctx: JSContext, rgb: RGBColor): JSValue =
+  var res = "#"
+  res.pushHex(rgb.r)
+  res.pushHex(rgb.g)
+  res.pushHex(rgb.b)
+  return toJS(ctx, res)
+
+proc fromJS2*(ctx: JSContext, val: JSValue, o: var JSResult[RGBColor]) =
+  let s = fromJS[string](ctx, val)
+  if s.isSome:
+    o = parseLegacyColor(s.get)
+  else:
+    o.err(s.error)
+
+proc toJS*(ctx: JSContext, rgba: RGBAColor): JSValue =
+  var res = "#"
+  res.pushHex(rgba.r)
+  res.pushHex(rgba.g)
+  res.pushHex(rgba.b)
+  res.pushHex(rgba.a)
+  return toJS(ctx, res)
+
+proc fromJS2*(ctx: JSContext, val: JSValue, o: var JSResult[RGBAColor]) =
+  if JS_IsNumber(val):
+    # as hex
+    let x = fromJS[uint32](ctx, val)
+    if x.isSome:
+      o.ok(RGBAColor(x.get))
+    else:
+      o.err(x.error)
+  else:
+    # parse
+    let s = fromJS[string](ctx, val)
+    if s.isSome:
+      let x = parseHexColor(s.get)
+      if x.isSome:
+        o.ok(x.get)
+      else:
+        o.err(newTypeError("Unrecognized color"))
+    else:
+      o.err(s.error)