diff --git a/content/commands.js b/content/commands.js
index f3f0fdd7..25df755d 100644
--- a/content/commands.js
+++ b/content/commands.js
@@ -483,18 +483,62 @@ function Commands() //{{{
"Here, downloads can be paused, canceled and resumed."
}
));
- addDefaultCommand(new Command(["ec[ho]"],
- function(args) { vimperator.echo(args); } ,
+
+ function argToString(arg, color)
+ {
+ if (!arg)
+ return "";
+
+ try
{
+ // TODO: move to vimperator.eval()?
+ arg = eval(arg);
+ }
+ catch (e)
+ {
+ vimperator.echoerr(e.toString());
+ return null;
+ }
+
+ if (typeof arg === "object")
+ arg = vimperator.objectToString(arg, color);
+ else if (typeof arg === "function")
+ arg = arg.toString().replace(//, ">");
+ else if (typeof arg === "number" || typeof arg === "boolean")
+ arg = "" + arg;
+ else if (typeof arg === "undefined")
+ arg = "undefined";
+
+ return arg;
+ }
+ addDefaultCommand(new Command(["ec[ho]"],
+ function(args)
+ {
+ var res = argToString(args, true);
+ if (res != null)
+ vimperator.echo(res);
+ },
+ {
+ usage: ["ec[ho] {expr}"],
short_help: "Display a string at the bottom of the window",
- help: "Echo all arguments of this command. Useful for showing informational messages.
Multiple lines can be separated by \\n."
+ help: "Useful for showing informational messages. Multiple lines can be separated by \\n.
" +
+ "{expr} can either be a quoted string, or any expression which can be fed to eval() like 4+5. " +
+ "You can also view the source code of objects and functions if the return value of {expr} is an object or function.",
+ completer: function(filter) { return vimperator.completion.javascript(filter); }
}
));
addDefaultCommand(new Command(["echoe[rr]"],
- function(args) { vimperator.echoerr(args); } ,
+ function(args)
{
+ var res = argToString(args, false);
+ if (res != null)
+ vimperator.echoerr(res);
+ },
+ {
+ usage: ["echoe[rr] {expr}"],
short_help: "Display an error string at the bottom of the window",
- help: "Echo all arguments of this command highlighted in red. Useful for showing important messages."
+ help: "Just like :ec[ho], but echoes the result highlighted in red. Useful for showing important messages.",
+ completer: function(filter) { return vimperator.completion.javascript(filter); }
}
));
addDefaultCommand(new Command(["exe[cute]"],
@@ -601,7 +645,11 @@ function Commands() //{{{
help: "Acts as a JavaScript interpreter by passing the argument to eval().
" +
":javascript alert('Hello world') would show a dialog box with the text \"Hello world\".
" +
":javascript <<EOF would read all the lines until a line starting with 'EOF' is found, and will eval() them.
" +
- "The special version :javascript! will open the JavaScript console of Firefox."
+ "The special version :javascript! will open the JavaScript console of Firefox.
" +
+ "Rudimentary <Tab> completion is available for :javascript {cmd}<Tab> (but not yet for the " +
+ ":js <<EOF multiline widget). Be aware that Vimperator needs to run {cmd} through eval() " +
+ "to get the completions, which could have unwanted side effects.",
+ completer: function(filter) { return vimperator.completion.javascript(filter); }
}
));
addDefaultCommand(new Command(["let"],
@@ -915,7 +963,7 @@ function Commands() //{{{
"" +
"You WILL be able to use :open [-T \"linux\"] torvalds<Tab> to complete bookmarks " +
"with tag \"linux\" and which contain \"torvalds\". Note that -T support is only available for tab completion, not for the actual command.
" +
- "The items which are completed on <Tab> are specified in the 'complete' option.
" +
+ "The items which are completed on <Tab> are specified in the 'complete' option.
" +
"Without argument, reloads the current page.
" +
"Without argument but with !, reloads the current page skipping the cache.",
completer: function(filter) { return vimperator.completion.get_url_completions(filter); }
diff --git a/content/completion.js b/content/completion.js
index a827ac57..0e0f9972 100644
--- a/content/completion.js
+++ b/content/completion.js
@@ -473,6 +473,73 @@ vimperator.completion = (function() // {{{
return build_longest_common_substring(mapped, filter);
}, //}}}
+ javascript: function(str)
+ {
+ g_substrings = [];
+ var matches = str.match(/^(.*?)(\s*\.\s*)?(\w*)$/);
+ var object = "window";
+ var filter = matches[3] || "";
+ var start = matches[1].length-1;
+ if (matches[2])
+ {
+ var brackets = 0, parentheses = 0;
+ outer:
+ for (; start >= 0; start--)
+ {
+ switch (matches[1][start])
+ {
+ case ";":
+ case "{":
+ break outer;
+
+ case "]":
+ brackets--;
+ break;
+ case "[":
+ brackets++;
+ break;
+ case ")":
+ parentheses--;
+ break;
+ case "(":
+ parentheses++;
+ break;
+ }
+ if (brackets > 0 || parentheses > 0)
+ break outer;
+ }
+ }
+ object = matches[1].substr(start+1) || "window";
+
+ var completions = [];
+ try
+ {
+ completions = eval(
+ "var comp = [];" +
+ "var type = '';" +
+ "var value = '';" +
+ "var obj = eval(" + object + ");" +
+ "for (var i in obj) {" +
+ " try { type = typeof(obj[i]); } catch (e) { type = 'unknown type'; };" +
+ " if (type == 'number' || type == 'string' || type == 'boolean') {" +
+ " value = obj[i];" +
+ " comp.push([[i], type + ': ' + value]); }" +
+ // The problem with that is that you complete vimperator.
+ // but can't press to complete sub items
+ // so it's better to complete vimperator and the user can do
+ // . to get the sub items
+ //" else if (type == 'function') {" +
+ //" comp.push([[i+'('], type]); }" +
+ //" else if (type == 'object') {" +
+ //" comp.push([[i+'.'], type]); }" +
+ " else {" +
+ " comp.push([[i], type]); }" +
+ "} comp;");
+ } catch (e) { completions = []; };
+
+ return build_longest_starting_substring(completions, filter);
+ },
+
exTabCompletion: function(str) //{{{
{
var [count, cmd, special, args] = vimperator.commands.parseCommand(str);
diff --git a/content/vimperator.js b/content/vimperator.js
index 6d192f4d..c2ca5035 100644
--- a/content/vimperator.js
+++ b/content/vimperator.js
@@ -434,36 +434,87 @@ const vimperator = (function() //{{{
}
},
- // logs a message to the javascript error console
- log: function(msg, level)
+ // if color = true it uses HTML markup to color certain items
+ objectToString: function(object, color)
{
- // if (Options.getPref("verbose") >= level) // FIXME: hangs vimperator, probably timing issue --mst
- console_service.logStringMessage('vimperator: ' + msg);
- },
-
- // logs an object to the javascript error console also prints all
- // properties of the object
- logObject: function(object, level)
- {
- if (typeof object != 'object')
+ if (object === null)
+ return "null";
+ if (typeof object != "object")
return false;
- var string = object + '::\n';
+ var string = "";
+ var obj = "";
+ try { // for window.JSON
+ obj = object.toString();
+ } catch (e) {
+ obj = "<Object>";
+ }
+
+ if (color)
+ string += "" + obj + "::\n";
+ else
+ string += obj + "::\n";
+
for (var i in object)
{
var value;
try
{
- var value = object[i];
+ if (i == "JSON") // without this ugly hack, ":echo window" does not work
+ value = "[object JSON]";
+ else
+ value = object[i];
}
catch (e)
{
value = "";
}
- string += i + ": " + value + "\n";
+ if (color)
+ {
+ // syntax highlighting for special items
+ if (typeof value === "number")
+ value = "" + value + "";
+ else if (typeof value === "string")
+ {
+ value = value.replace(/\n/, "\\n").replace(/, "<");
+ value = "\"" + value + "\"";
+ }
+ else if (typeof value === "boolean")
+ value = "" + value + "";
+ else if (value == null || value == "undefined")
+ value = "" + value + "";
+ else if (typeof value === "object" || typeof value === "function")
+ {
+ // for java packages value.toString() would crash so badly
+ // that we cannot even try/catch it
+ if (/^\[JavaPackage.*\]$/.test(value))
+ value = "[JavaPackage]";
+ else
+ {
+ var str = value.toString();
+ if (typeof str == "string") // can be "undefined"
+ value = str.replace(//g, ">");
+ }
+ }
+
+ string += "" + i + ": " + value + "\n";
+ }
+ else
+ string += i + ": " + value + "\n";
}
- vimperator.log(string, level);
+ return string;
+ },
+
+ // logs a message to the javascript error console
+ // if msg is an object, it is beautified
+ log: function(msg, level)
+ {
+ //if (Options.getPref("verbose") >= level) // FIXME: hangs vimperator, probably timing issue --mst
+ if (typeof msg == "object")
+ msg = this.objectToString(msg, false);
+
+ console_service.logStringMessage('vimperator: ' + msg);
},
// open one or more URLs