From 4e1f9003268b2e4243a1dd9166bbcea8d6aa6bc1 Mon Sep 17 00:00:00 2001 From: Pietro Brenna Date: Sat, 22 Feb 2020 12:23:08 +0100 Subject: [PATCH] Auto help --- command-parse.html | 27 ++++++++++- command-parse.js | 115 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 133 insertions(+), 9 deletions(-) diff --git a/command-parse.html b/command-parse.html index e79ef41..a7c67d6 100644 --- a/command-parse.html +++ b/command-parse.html @@ -136,6 +136,8 @@ whiteSpace: 'nowrap' }); var row = $('
').appendTo(container); + var row2 = $('
').appendTo(container); + var row3 = $('
').appendTo(container); var selectField = $('', { class: "node-input-rule-help", type: "text", style: "margin-left: 5px; width:250px", placeholder:"Help description" }).appendTo(row2); + desc.val(rule.help || ""); + var desc_label = $('', {style:"width:100px;display:inline-block;"}).text("Variable:").appendTo(row3); + var match_name = $('', { class: "node-input-rule-match_name", type: "text", style: "margin-left: 5px; width: 250px;", placeholder:"Matched variable name" }).appendTo(row3); + match_name.val(rule.match_name || ""); + if (rule.type !== "regex") { + row3.hide(); + } finalspan.append(' → ' + (i + 1) + ' '); selectField.on("change", function () { var type = selectField.val(); + if (type == "regex") { + row3.show(); + } else { + row3.hide(); + } valueField.typedInput('hide'); if (!valueField) { valueField = createValueField(); @@ -234,6 +250,10 @@ var r = { t: type }; r.v = rule.find(".node-input-rule-value").typedInput('value'); r.vt = rule.find(".node-input-rule-value").typedInput('type'); + r.help = rule.find(".node-input-rule-help").val(); + if(r.t == 'regex') { + r.match_name = rule.find(".node-input-rule-match_name").val(); + } node.rules.push(r); }); this.propertyType = $("#node-input-property").typedInput('type'); @@ -264,6 +284,10 @@
+
+ + +
@@ -279,7 +303,8 @@ defaults: { property: { value: "payload", required: true, validate: RED.validators.typedInput("propertyType") }, name: { value: "" }, - help_text: {value: ""} + help_text: {value: ""}, + help_keyword: {value: "help"}, }, inputs: 1, outputs: 2, diff --git a/command-parse.js b/command-parse.js index 504245b..a5c3dcf 100644 --- a/command-parse.js +++ b/command-parse.js @@ -2,10 +2,95 @@ module.exports = function (RED) { "use strict"; + function format_usage(usage, prefix) { + if (!prefix) { + prefix = []; + } + let out = ""; + for (let k in usage) { + if (!usage.hasOwnProperty(k)) + continue; + if(typeof usage[k] == "object"){ + out += format_usage(usage[k], prefix.concat([k])); + } else { + let ext = k != "" ? [k] : []; + out += prefix.concat(ext).join(" ") + ": " + usage[k] + "\n"; + } + } + return out; + } + function depth_first(n, path) { + if (!path) { + path = [n.id]; + } + if (!n) { + return null; + } + let usage = {}; + if (n.type == 'parser-consume') { + for (let r_idx in n.rules) { + let r = n.rules[r_idx]; + let key; + if (r.t == "regex") { + if (r.match_name){ + key = `[${r.match_name}]`; + } else { + key = `[espressione]`; + } + } else { + key = r.v; + } + if (r.help) { + usage[key] = r.help; + } else { + let sub_usage = {}; + for (let wire of n.wires[r_idx]) { + if (path.indexOf(wire) === -1) { + let sub_n = RED.nodes.getNode(wire); + let tmp_sub_usage = depth_first(sub_n, path + [wire]); + if (tmp_sub_usage !== null) { + Object.assign(sub_usage, tmp_sub_usage); + } + } + } + usage[key] = sub_usage; + } + } + } + return usage; + } + function ParserEntryPoint(n) { RED.nodes.createNode(this, n); this.property = n.property; this.help_text = n.help_text; + this.help_keyword = n.help_keyword; + this.genera_errore = function(msg) { + let out = ""; + if (msg && msg.token_parser && msg.token_parser.consumed_tokens.length > 0) { + let offending_token = msg.token_parser.consumed_tokens[msg.token_parser.consumed_tokens.length - 1]; + if (offending_token == "") { + offending_token = "(vuoto)"; + } + out += msg.token_parser.consumed_tokens.slice(0, -1).join(" ") +`: comando non riconosciuto: ${offending_token}.\n`; + if (this.help_keyword) { + let help_command = msg.token_parser.consumed_tokens.slice(0, -1).concat([this.help_keyword]).join(" "); + out += `Usa '${help_command}' per la guida.\n`; + } + } + return out; + } + this.genera_help = function (root, msg) { + let usages = {}; + if (!root) { + for (let wire of root.wires[0]) { + Object.assign(usages, depth_first(RED.nodes.getNode(wire))); + } + } else { + usages = depth_first(root); + } + return format_usage(usages, msg.token_parser.consumed_tokens.slice(0,-1)); + }; this.on("input", msg => { RED.util.evaluateNodeProperty(this.property, "msg", this, msg, (err, value) => { @@ -13,12 +98,17 @@ module.exports = function (RED) { this.error(`Can't evaluate msg.${this.property}`); } else { let tokens = value.trim().toLowerCase().split(" "); + msg.regex_matches = {}; msg.token_parser = { new_tokens: tokens, consumed_tokens: [], - help_message: this.help_text, - on_error: (maybe_error) => { - let err = maybe_error ? maybe_error : this.help_text; + help_keyword: this.help_keyword, + on_error: (msg, maybe_error) => { + let err = maybe_error ? maybe_error : this.genera_errore(msg); + this.send([undefined, err]); + }, + on_help: (help_node, msg) => { + let err = this.genera_help(help_node, msg); this.send([undefined, err]); } }; @@ -33,10 +123,10 @@ module.exports = function (RED) { function ConsumaToken(n) { RED.nodes.createNode(this, n); this.rules = n.rules; - this.behavior= n.behavior; - this.checkall= n.checkall; + this.behavior = n.behavior; + this.checkall = n.checkall; function check_rule(r, token) { - switch(r.t){ + switch (r.t) { case "eq": return token == r.v.toLowerCase(); case "regex": @@ -47,10 +137,18 @@ module.exports = function (RED) { this.on("input", msg => { let token = msg.token_parser.new_tokens.shift() || ""; msg.token_parser.consumed_tokens.push(token); + if(token == msg.token_parser.help_keyword) { + msg.token_parser.on_help(this, msg); + return; + } let out = []; let n_matches = 0; for (let r of this.rules) { - if(check_rule(r, token)){ + let matches = check_rule(r, token); + if (matches === true || matches.length > 0) { + if (matches.length > 0) { + msg.regex_matches[r.match_name] = matches; + } out.push(msg); n_matches += 1; if (!this.checkall) { @@ -61,7 +159,8 @@ module.exports = function (RED) { } } if (n_matches == 0) { - msg.token_parser.on_error(); + msg.token_parser.on_error(msg); + return; } this.send(out); });