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 + "\""; + } + 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