mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-23 14:12:27 +01:00
Add the beginings of completion support to parseArgs
This commit is contained in:
@@ -318,104 +318,107 @@ liberator.Commands = function () //{{{
|
|||||||
// @param allowUnknownOptions: -foo won't result in an error, if -foo isn't
|
// @param allowUnknownOptions: -foo won't result in an error, if -foo isn't
|
||||||
// specified in "options"
|
// specified in "options"
|
||||||
// TODO: should it handle comments?
|
// TODO: should it handle comments?
|
||||||
parseArgs: function (str, options, argCount, allowUnknownOptions) //{{{
|
parseArgs: function (str, options, argCount, allowUnknownOptions, complete) //{{{
|
||||||
{
|
{
|
||||||
|
function quoteArg(quote)
|
||||||
|
{
|
||||||
|
switch (quote)
|
||||||
|
{
|
||||||
|
case "'":
|
||||||
|
return function (str) "'" + str.substitute(/[\\']/g, "\\$&") + "'";
|
||||||
|
case '"':
|
||||||
|
return function (str) '"' + str.substitute(/[\\"\t\n]/g,
|
||||||
|
function (c) (c == "\n" ? "\\n" : c == "\t" ? "\\t" : "\\" + c));
|
||||||
|
default:
|
||||||
|
return function (str) str.substitute(/[\\ ]/g, "\\$&");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// returns [count, parsed_argument]
|
// returns [count, parsed_argument]
|
||||||
function getNextArg(str) // {{{
|
function getNextArg(str) // {{{
|
||||||
{
|
{
|
||||||
var inSingleString = false;
|
var stringDelimiter = null;
|
||||||
var inDoubleString = false;
|
var escapeNext = false;
|
||||||
var inEscapeKey = false;
|
|
||||||
|
|
||||||
var arg = "";
|
var arg = "";
|
||||||
|
|
||||||
outer:
|
outer:
|
||||||
for (let i = 0; i < str.length; i++)
|
for (let i = 0; i < str.length; i++)
|
||||||
{
|
{
|
||||||
|
inner:
|
||||||
switch (str[i])
|
switch (str[i])
|
||||||
{
|
{
|
||||||
case "\"":
|
case '"':
|
||||||
if (inEscapeKey)
|
|
||||||
{
|
|
||||||
inEscapeKey = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!inSingleString)
|
|
||||||
{
|
|
||||||
inDoubleString = !inDoubleString;
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "'":
|
case "'":
|
||||||
if (inEscapeKey)
|
if (escapeNext)
|
||||||
{
|
{
|
||||||
inEscapeKey = false;
|
escapeNext = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!inDoubleString)
|
switch (stringDelimiter)
|
||||||
{
|
{
|
||||||
inSingleString = !inSingleString;
|
case str[i]:
|
||||||
continue outer;
|
stringDelimiter = null;
|
||||||
|
continue outer;
|
||||||
|
case null:
|
||||||
|
stringDelimiter = str[i];
|
||||||
|
continue outer;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// \ is an escape key for non quoted or "-quoted strings
|
// \ is an escape key for non quoted or "-quoted strings
|
||||||
// for '-quoted strings it is taken literally, apart from \' and \\
|
// for '-quoted strings it is taken literally, apart from \' and \\
|
||||||
case "\\":
|
case "\\":
|
||||||
if (inEscapeKey)
|
if (escapeNext)
|
||||||
{
|
{
|
||||||
inEscapeKey = false;
|
escapeNext = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// only escape "\\" and "\ " in non quoted strings
|
// in non-quoted strings, only escape "\\" and "\ ", otherwise drop "\\"
|
||||||
if (!inSingleString && !inDoubleString && str[i + 1] != "\\" && str[i + 1] != " ")
|
if (!stringDelimiter && str[i + 1] != "\\" && str[i + 1] != " ")
|
||||||
continue outer;
|
continue outer;
|
||||||
// only escape "\\" and "\'" in single quoted strings
|
// in single quoted strings, only escape "\\" and "\'", otherwise keep "\\"
|
||||||
else if (inSingleString && str[i + 1] != "\\" && str[i + 1] != "'")
|
if (stringDelimiter == "'" && str[i + 1] != "\\" && str[i + 1] != "'")
|
||||||
break;
|
break;
|
||||||
else
|
escapeNext = true;
|
||||||
{
|
continue outer;
|
||||||
inEscapeKey = true;
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (inSingleString)
|
if (stringDelimiter == "'")
|
||||||
{
|
{
|
||||||
inEscapeKey = false;
|
escapeNext = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (inEscapeKey)
|
if (escapeNext)
|
||||||
{
|
{
|
||||||
inEscapeKey = false;
|
escapeNext = false;
|
||||||
switch (str[i])
|
switch (str[i])
|
||||||
{
|
{
|
||||||
case "n": arg += "\n"; continue outer;
|
case "n": arg += "\n"; break;
|
||||||
case "t": arg += "\t"; continue outer;
|
case "t": arg += "\t"; break;
|
||||||
default:
|
default:
|
||||||
break; // this makes "a\fb" -> afb; wanted or should we return ab? --mst
|
break inner; // this makes "a\fb" -> afb; wanted or should we return ab? --mst
|
||||||
}
|
}
|
||||||
|
continue outer;
|
||||||
}
|
}
|
||||||
else if (!inDoubleString && /\s/.test(str[i]))
|
else if (stringDelimiter != '"' && /\s/.test(str[i]))
|
||||||
{
|
{
|
||||||
return [i, arg];
|
return [i, arg];
|
||||||
}
|
}
|
||||||
else // a normal charcter
|
break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
arg += str[i];
|
arg += str[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add parsing of a " comment here:
|
// TODO: add parsing of a " comment here:
|
||||||
if (inDoubleString || inSingleString)
|
if (stringDelimiter)
|
||||||
return [-1, "E114: Missing quote"];
|
return [str.length, arg, stringDelimiter];
|
||||||
if (inEscapeKey)
|
if (escapeNext)
|
||||||
return [-1, "trailing \\"];
|
return [str.length, arg, "\\"];
|
||||||
else
|
else
|
||||||
return [str.length, arg];
|
return [str.length, arg];
|
||||||
} // }}}
|
} // }}}
|
||||||
@@ -459,15 +462,15 @@ liberator.Commands = function () //{{{
|
|||||||
var optname = "";
|
var optname = "";
|
||||||
if (!onlyArgumentsRemaining) //{{{
|
if (!onlyArgumentsRemaining) //{{{
|
||||||
{
|
{
|
||||||
for (let opt = 0; opt < options.length; opt++)
|
for (let [,opt] in Iterator(options))
|
||||||
{
|
{
|
||||||
for (let name = 0; name < options[opt][0].length; name++)
|
for (let [,optname] in Iterator(opt[0]))
|
||||||
{
|
{
|
||||||
optname = options[opt][0][name];
|
|
||||||
if (sub.indexOf(optname) == 0)
|
if (sub.indexOf(optname) == 0)
|
||||||
{
|
{
|
||||||
invalid = false;
|
invalid = false;
|
||||||
arg = null;
|
arg = null;
|
||||||
|
quote = null;
|
||||||
// no value to the option
|
// no value to the option
|
||||||
if (optname.length >= sub.length)
|
if (optname.length >= sub.length)
|
||||||
{
|
{
|
||||||
@@ -475,28 +478,19 @@ liberator.Commands = function () //{{{
|
|||||||
}
|
}
|
||||||
else if (sub[optname.length] == "=")
|
else if (sub[optname.length] == "=")
|
||||||
{
|
{
|
||||||
[count, arg] = getNextArg(sub.substr(optname.length + 1));
|
[count, arg, quote] = getNextArg(sub.substr(optname.length + 1));
|
||||||
if (count == -1)
|
|
||||||
{
|
|
||||||
liberator.echoerr("Invalid argument for option " + optname);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
count++; // to compensate the "=" character
|
count++; // to compensate the "=" character
|
||||||
}
|
}
|
||||||
else if (/\s/.test(sub[optname.length]))
|
else if (/\s/.test(sub[optname.length]))
|
||||||
{
|
{
|
||||||
if (options[opt][1] != this.OPTION_NOARG)
|
if (opt[1] != this.OPTION_NOARG)
|
||||||
{
|
{
|
||||||
[count, arg] = getNextArg(sub.substr(optname.length + 1));
|
[count, arg, quote] = getNextArg(sub.substr(optname.length + 1));
|
||||||
if (count == -1)
|
if (count == -1)
|
||||||
{
|
|
||||||
liberator.echoerr("Invalid argument for option " + optname);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we add the argument to an option after a space, it MUST not be empty
|
// if we add the argument to an option after a space, it MUST not be empty
|
||||||
if (arg.length == 0)
|
if (!quote && arg.length == 0)
|
||||||
arg = null;
|
arg = null;
|
||||||
|
|
||||||
count++; // to compensate the " " character
|
count++; // to compensate the " " character
|
||||||
@@ -510,9 +504,23 @@ liberator.Commands = function () //{{{
|
|||||||
invalid = true;
|
invalid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quote)
|
||||||
|
{
|
||||||
|
if (!complete)
|
||||||
|
{
|
||||||
|
liberator.echoerr("Invalid argument for option " + optname);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let compl = opt[3] || [];
|
||||||
|
if (typeof compl == "function")
|
||||||
|
compl = compl();
|
||||||
|
let filter = quoteArg(sub[optname.length + 1])
|
||||||
|
return [i + optname.length + 1, liberator.completion.filter(compl.map(filter), filter(arg))];
|
||||||
|
}
|
||||||
|
|
||||||
if (!invalid)
|
if (!invalid)
|
||||||
{
|
{
|
||||||
switch (options[opt][1]) // type
|
switch (opt[1]) // type
|
||||||
{
|
{
|
||||||
case this.OPTION_NOARG:
|
case this.OPTION_NOARG:
|
||||||
if (arg != null)
|
if (arg != null)
|
||||||
@@ -566,16 +574,16 @@ liberator.Commands = function () //{{{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we have a validator function
|
// we have a validator function
|
||||||
if (typeof options[opt][2] == "function")
|
if (typeof opt[2] == "function")
|
||||||
{
|
{
|
||||||
if (options[opt][2].call(this, arg) == false)
|
if (opt[2].call(this, arg) == false)
|
||||||
{
|
{
|
||||||
liberator.echoerr("Invalid argument for option: " + optname);
|
liberator.echoerr("Invalid argument for option: " + optname);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
args[options[opt][0][0]] = arg; // always use the first name of the option
|
args[opt[0][0]] = arg; // always use the first name of the option
|
||||||
i += optname.length + count;
|
i += optname.length + count;
|
||||||
continue outer;
|
continue outer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ liberator.Completion = function () //{{{
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// liberator.dump("eval(" + liberator.util.escapeString(arg) + ")\n");
|
// liberator.dump("eval(" + liberator.util.escapeString(arg) + ")\n");
|
||||||
return window.eval(arg);
|
return liberator.eval(arg);
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user