diff --git a/content/buffer.js b/content/buffer.js index ae5da47d..e38e098e 100644 --- a/content/buffer.js +++ b/content/buffer.js @@ -34,11 +34,11 @@ liberator.Buffer = function () //{{{ ////////////////////// PRIVATE SECTION ///////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ - const highlightClasses = ["Boolean", "ErrorMsg", "Function", "InfoMsg", "Keyword", + const highlightClasses = ["Boolean", "ErrorMsg", "Filter", "Function", "InfoMsg", "Keyword", "LineNr", "ModeMsg", "MoreMsg", "Normal", "Null", "Number", "Question", "StatusLine", "StatusLineBroken", "StatusLineSecure", "String", "Tag", "Title", "URL", "WarningMsg", - ["Hint", ".liberator-hint", "*"] + ["Hint", ".liberator-hint", "*"], ["Search", ".__liberator-search", "*"], ["Bell", "#liberator-visualbell"], ]; @@ -806,7 +806,10 @@ liberator.Buffer = function () //{{{ }, { // TODO: add this as a standard highlight completion function? - completer: function (filter) [0, liberator.completion.filter([[v, ""] for ([k, v] in Iterator(highlightClasses))], filter)], + // I agree. It could (should) be much more sophisticated. --Kris + completer: function (filter) [0, + liberator.completion.filter([[v instanceof Array ? v[0] : v, ""] for ([k, v] in Iterator(highlightClasses))], filter) + ], hereDoc: true, bang: true, }); diff --git a/content/completion.js b/content/completion.js index 7239c199..b31c763d 100644 --- a/content/completion.js +++ b/content/completion.js @@ -62,6 +62,8 @@ liberator.Completion = function () //{{{ function Javascript() { + let json = Components.classes["@mozilla.org/dom/json;1"] + .createInstance(Components.interfaces.nsIJSON); const OFFSET = 0, CHAR = 1, STATEMENTS = 2, DOTS = 3, FULL_STATEMENTS = 4, FUNCTIONS = 5; let stack = []; let top = []; /* The element on the top of the stack. */ @@ -93,6 +95,13 @@ liberator.Completion = function () //{{{ })(); try { + // The point of 'for k in obj' is to get keys + // that are accessible via . or [] notation. + // Iterators quite often return values of no + // use whatsoever for this purpose, so, we try + // this rather dirty hack of getting a standard + // object iterator for any object that defines its + // own. if ("__iterator__" in obj) { let oldIter = obj.__iterator__; @@ -167,7 +176,7 @@ liberator.Completion = function () //{{{ { try { - // liberator.dump("eval(" + liberator.util.escapeString(arg) + ")"); + // liberator.dump("eval(" + liberator.util.escapeString(arg) + ")\n"); return window.eval(arg); } catch (e) @@ -194,9 +203,7 @@ liberator.Completion = function () //{{{ /* Push and pop the stack, maintaining references to 'top' and 'last'. */ let push = function (arg) { - top = [i, arg, [], [], [], []]; - if (arg) - top[STATEMENTS].push(firstNonwhite()); + top = [i, arg, [i], [], [], []]; last = top[CHAR]; stack.push(top); } @@ -204,6 +211,8 @@ liberator.Completion = function () //{{{ { if (top[CHAR] != arg) throw new Error("Invalid JS"); + if (top[STATEMENTS][top[STATEMENTS].length - 1] == i) + top[STATEMENTS].pop(); top = get(-2); last = top[CHAR]; let ret = stack.pop(); @@ -225,6 +234,12 @@ liberator.Completion = function () //{{{ stack = []; push(""); } + else + { + let s = top[STATEMENTS]; + if (s[s.length - 1] == start) + s.pop(); + } /* Build a parse stack, discarding entries opening characters * match closing characters. The last open entry is used to @@ -246,6 +261,15 @@ liberator.Completion = function () //{{{ } else { + if (/[\w$]/.test(c) && !/[\w\d$]/.test(lastChar) || !/[\w\d\s]/.test(c)) + top[STATEMENTS].push(i); + + // A "." or a "[" dereferences the last "statement" and effectively + // joins it to this logical statement. + if ((c == "." || c == "[") && /[\w\d\])"']/.test(lastNonwhite) + || lastNonwhite == "." && /[\w$]/.test(c)) + top[STATEMENTS].pop(); + switch (c) { case "(": @@ -259,10 +283,11 @@ liberator.Completion = function () //{{{ push(c); break; case "[": - if (/[\])"']/.test(lastNonwhite)) - top[STATEMENTS].pop(); push(c); break; + case ".": + top[DOTS].push(i); + break; case ")": pop("("); break; case "]": pop("["); break; case "}": pop("{"); /* Fallthrough */ @@ -270,20 +295,16 @@ liberator.Completion = function () //{{{ case ",": top[FULL_STATEMENTS].push(i); break; - case ".": - top[DOTS].push(i); - if (/[\])"']/.test(lastNonwhite)) - top[STATEMENTS].pop(); - break; } - /* Could do better. */ - if (!/[\w\s.([]/.test(c)) - top[STATEMENTS].push(i); + if (/\S/.test(c)) lastNonwhite = c; } } + if (!/[\w\d$]/.test(lastChar) && lastNonwhite != ".") + top[STATEMENTS].push(i); + lastIdx = i; } @@ -291,12 +312,14 @@ liberator.Completion = function () //{{{ { try { - continuing = string.indexOf(str) == 0; + continuing = lastIdx && string.indexOf(str) == 0; str = string; buildStack(continuing ? lastIdx : 0); } catch (e) { + liberator.dump(liberator.util.escapeString(string) + ": " + e + "\n" + e.stack + "\n"); + lastIdx = 0; return [0, []]; } @@ -307,6 +330,7 @@ liberator.Completion = function () //{{{ let preEval = str.substring(0, end) + ";"; /* In a string. */ + // TODO: Make this work with unquoted integers. if (last == "'" || last == '"') { /* Stack: @@ -316,9 +340,11 @@ liberator.Completion = function () //{{{ */ /* Is this an object accessor? */ - if (get(-2)[CHAR] != "[" /* Are inside of []? */ - || get(-3, 0, STATEMENTS) == get(-2)[OFFSET]) /* Okay. Is it an array literal? */ - return [0, []]; /* No. Nothing to do. */ + if (get(-2)[CHAR] != "[" // Are we inside of []? + // Okay, if the [ starts at the begining of a logical + // statement, we're in an array literal, and we're done. + || get(-3, 0, STATEMENTS) == get(-2)[OFFSET]) + return [0, []]; /* * str = "foo[bar + 'baz" @@ -343,12 +369,12 @@ liberator.Completion = function () //{{{ * obj = "foo.bar" * key = "baz" */ - let key = str.substring(dot + 1); + let [, space, key] = str.substring(dot + 1).match(/^(\s*)(.*)/); let obj = preEval + str.substring(get(-1, 0, STATEMENTS), dot); if (!/^(?:\w[\w\d]*)?$/.test(key)) return [0, []]; /* Not a word. Forget it. Can this even happen? */ - return [dot + 1, objectKeys(obj, key)]; + return [dot + 1 + space.length, objectKeys(obj, key)]; } /* Okay, assume it's an identifier and try to complete it from the window @@ -383,6 +409,7 @@ liberator.Completion = function () //{{{ // list = [ [['com1', 'com2'], 'text'], [['com3', 'com4'], 'text'] ] function buildLongestCommonSubstring(list, filter, favicon) { + liberator.completion.filterString = filter; var filtered = []; var ignorecase = false; @@ -767,7 +794,11 @@ liberator.Completion = function () //{{{ javascript: function (str) { - return javascript.complete(str); + try + { + return javascript.complete(str); + } + catch (e) {} }, macro: function (filter) @@ -789,6 +820,7 @@ liberator.Completion = function () //{{{ // XXX: Move to bookmarks.js? searchEngineSuggest: function (filter, engineAliases) { + this.filterString = filter; if (!filter) return [0, []]; @@ -875,6 +907,7 @@ liberator.Completion = function () //{{{ // if the 'complete' argument is passed like "h", it temporarily overrides the complete option url: function (filter, complete) { + this.filterString = filter; var completions = []; var start = 0; var skip = filter.match("^(.*" + liberator.options["urlseparator"] + ")(.*)"); // start after the last 'urlseparator' diff --git a/content/io.js b/content/io.js index ab61f4de..b586961f 100644 --- a/content/io.js +++ b/content/io.js @@ -737,7 +737,6 @@ lookup: // no need (actually forbidden) to add: js <; let start = 0; let i; - while ((i = lca.indexOf(lcfilter, start)) > -1) + while ((i = lcstr.indexOf(lcfilter, start)) > -1) { s += <>{str.substring(start, i)}; - s += {str.substr(i, filter.length)}; + s += {str.substr(i, filter.length)}; start = i + filter.length; } return s + <>{str.substr(start)}; diff --git a/content/ui.js b/content/ui.js index 4534def7..6af34c3b 100644 --- a/content/ui.js +++ b/content/ui.js @@ -1218,17 +1218,23 @@ liberator.ItemList = function (id) //{{{ // TODO: temporary, to be changed/removed function createRow(b, c, a, dom) { - let row = - - - {b} - {c} - + /* Obviously, ItemList shouldn't know or care about this. */ + let filter = liberator.completion.filterString; + if (filter) + { + b = liberator.template.highlightFilter(String(b), filter); + c = liberator.template.highlightFilter(String(c), filter); + } if (typeof a == "function") a = a(); - if (a) - row.td[0].* = ; + + let row = + + {a ? : } + {b} + {c} + ; if (dom) return liberator.util.xmlToDom(row, doc); diff --git a/skin/vimperator.css b/skin/vimperator.css index cea964c3..701ad1ad 100644 --- a/skin/vimperator.css +++ b/skin/vimperator.css @@ -42,6 +42,35 @@ the terms of any one of the MPL, the GPL or the LGPL. right: 0; } +.__liberator-search { + display: inline; + font-size: inherit; + padding: 0; + color: black; + background-color: yellow; + padding: 0; + display: inline; +} + +.liberator-hint { + z-index:5000; + font-family:monospace; + font-size:10px; + font-weight: bold; + color:white; + background-color:red; + border-color:ButtonShadow; + border-width:0px; + border-style:solid; + padding:0px 1px 0px 1px; + position:absolute; +} + +#liberator-visualbell { + border: none; + background-color: black; +} + /* Applied only to completion buffer and MOW */ @-moz-document url-prefix(chrome://vimperator/), @@ -51,7 +80,7 @@ the terms of any one of the MPL, the GPL or the LGPL. .compitem[selected=true] { background-color: yellow; } .compitem > .favicon { width: 16px; } -.compitem > .favicon > img { width: 16px; height: 16px; } +.compitem > .favicon > * { width: 16px; height: 16px; display: block; } .compitem > .completion { width: 45%; overflow: hidden; } .compitem > .description { color: gray; } @@ -70,6 +99,8 @@ the terms of any one of the MPL, the GPL or the LGPL. .hl-Keyword { color: red; } .hl-Tag { color: blue; } +.hl-Filter { font-weight: bold; } + } /* Applied to completion buffer, MOW, browser window */