diff options
author | bptato <nincsnevem662@gmail.com> | 2024-03-26 20:46:55 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-03-26 20:54:55 +0100 |
commit | 0d1a11cea0fa48837f4ae1ca869e9b01e1f159f4 (patch) | |
tree | e962a0a15d1bdd7b6b13cfceea49d486081cd5a4 /src/config | |
parent | 845445b4c4ab69cc2ffc564c7ab71eeaf8cc3c5e (diff) | |
download | chawan-0d1a11cea0fa48837f4ae1ca869e9b01e1f159f4.tar.gz |
config: improve input system
as described in <https://todo.sr.ht/~bptato/chawan/6>
Diffstat (limited to 'src/config')
-rw-r--r-- | src/config/config.nim | 94 | ||||
-rw-r--r-- | src/config/toml.nim | 3 |
2 files changed, 91 insertions, 6 deletions
diff --git a/src/config/config.nim b/src/config/config.nim index ebf36d49..287c2e54 100644 --- a/src/config/config.nim +++ b/src/config/config.nim @@ -1,8 +1,10 @@ import std/options import std/os import std/streams +import std/strutils import std/tables +import bindings/quickjs import config/chapath import config/mailcap import config/mimetypes @@ -12,6 +14,7 @@ import js/fromjs import js/javascript import js/propertyenumlist import js/regex +import js/tojs import loader/headers import types/cell import types/color @@ -70,6 +73,11 @@ type display_charset* {.jsgetset.}: Option[Charset] document_charset* {.jsgetset.}: seq[Charset] + CommandConfig = object + jsObj*: JSValue + init*: seq[tuple[k, cmd: string]] # initial k/v map + map*: Table[string, JSValue] # qualified name -> function + ExternalConfig = object tmpdir* {.jsgetset.}: ChaPathResolved editor* {.jsgetset.}: string @@ -130,6 +138,7 @@ type #TODO getset siteconf*: seq[SiteConfig] omnirule*: seq[OmniRule] + cmd*: CommandConfig page* {.jsget.}: ActionMap line* {.jsget.}: ActionMap @@ -331,6 +340,8 @@ proc parseConfigValue(ctx: var ConfigParser; x: var Mailcap; v: TomlValue; k: string) proc parseConfigValue(ctx: var ConfigParser; x: var URIMethodMap; v: TomlValue; k: string) +proc parseConfigValue(ctx: var ConfigParser; x: var CommandConfig; v: TomlValue; + k: string) proc typeCheck(v: TomlValue; t: TomlValueType; k: string) = if v.t != t: @@ -626,6 +637,28 @@ proc parseConfigValue(ctx: var ConfigParser; x: var URIMethodMap; v: TomlValue; x.parseURIMethodMap(f.readAll()) x.append(DefaultURIMethodMap) +func isCompatibleIdent(s: string): bool = + if s.len == 0 or s[0] notin AsciiAlpha + {'_', '$'}: + return false + for i in 1 ..< s.len: + if s[i] notin AsciiAlphaNumeric + {'_', '$'}: + return false + return true + +proc parseConfigValue(ctx: var ConfigParser; x: var CommandConfig; v: TomlValue; + k: string) = + typeCheck(v, tvtTable, k) + for kk, vv in v: + let kkk = k & "." & kk + typeCheck(vv, {tvtTable, tvtString}, kkk) + if not kk.isCompatibleIdent(): + raise newException(ValueError, "invalid command name: " & kkk) + if vv.t == tvtTable: + ctx.parseConfigValue(x, vv, kkk) + else: # tvtString + # skip initial "cmd.", we don't need it + x.init.add((kkk.substr("cmd.".len), vv.s)) + type ParseConfigResult* = object success*: bool warnings*: seq[string] @@ -700,15 +733,64 @@ proc getNormalAction*(config: Config; s: string): string = proc getLinedAction*(config: Config; s: string): string = return config.line.getOrDefault(s) -proc readConfig*(pathOverride: Option[string]; jsctx: JSContext): Config = - result = Config(jsctx: jsctx) - discard result.parseConfig("res", newStringStream(defaultConfig)) #TODO TODO TODO +type ReadConfigResult = tuple + config: Config + res: ParseConfigResult + +proc readConfig*(pathOverride: Option[string]; jsctx: JSContext): + ReadConfigResult = + let config = Config(jsctx: jsctx) + var res = config.parseConfig("res", newStringStream(defaultConfig)) + if not res.success: + return (nil, res) if pathOverride.isNone: when defined(debug): - discard result.readConfig(getCurrentDir() / "res", "config.toml") - discard result.readConfig(getConfigDir() / "chawan", "config.toml") + res = config.readConfig(getCurrentDir() / "res", "config.toml") + if not res.success: + return (nil, res) + res = config.readConfig(getConfigDir() / "chawan", "config.toml") else: - discard result.readConfig(getCurrentDir(), pathOverride.get) + res = config.readConfig(getCurrentDir(), pathOverride.get) + if not res.success: + return (nil, res) + return (config, res) + +# called after parseConfig returns +proc initCommands*(config: Config): Err[string] = + let ctx = config.jsctx + let obj = JS_NewObject(ctx) + defer: JS_FreeValue(ctx, obj) + if JS_IsException(obj): + return err(ctx.getExceptionStr()) + for i in countdown(config.cmd.init.high, 0): + let (k, cmd) = config.cmd.init[i] + if k in config.cmd.map: + # already in map; skip + continue + var objIt = obj + let name = k.afterLast('.') + if name.len < k.len: + for ss in k.substr(0, k.high - name.len - 1).split('.'): + var prop = JS_GetPropertyStr(ctx, objIt, cstring(ss)) + if JS_IsUndefined(prop): + prop = JS_NewObject(ctx) + ctx.definePropertyE(objIt, ss, prop) + if JS_IsException(prop): + return err(ctx.getExceptionStr()) + objIt = prop + if cmd == "": + config.cmd.map[k] = JS_UNDEFINED + continue + let fun = ctx.eval(cmd, "<" & k & ">", JS_EVAL_TYPE_GLOBAL) + if JS_IsException(fun): + return err(ctx.getExceptionStr()) + if not JS_IsFunction(ctx, fun): + return err(k & " is not a function") + ctx.definePropertyE(objIt, name, JS_DupValue(ctx, fun)) + config.cmd.map[k] = fun + config.cmd.jsObj = JS_DupValue(ctx, obj) + config.cmd.init = @[] + ok() proc addConfigModule*(ctx: JSContext) = ctx.registerType(ActionMap) diff --git a/src/config/toml.nim b/src/config/toml.nim index 5f471b27..760f335d 100644 --- a/src/config/toml.nim +++ b/src/config/toml.nim @@ -183,6 +183,7 @@ proc consumeString(state: var TomlParser, first: char): multiline = true state.seek(2) if multiline and state.peek(0) == '\n': + inc state.line discard state.consume() var escape = false var ml_trim = false @@ -223,6 +224,8 @@ proc consumeString(state: var TomlParser, first: char): if c notin {'\n', ' ', '\t'}: res &= c ml_trim = false + if c == '\n': + inc state.line else: if c == '\n': inc state.line |