1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-20 18:37:58 +01:00

New prototype ItemList implementation. Faster completion scrolling.

This commit is contained in:
Kris Maglione
2011-10-02 16:00:50 -04:00
parent c500936b2e
commit 7a0b0873ce
10 changed files with 423 additions and 73 deletions

View File

@@ -607,7 +607,7 @@ var CommandLine = Module("commandline", {
let elem = document.getElementById("dactyl-completions-" + node.id); let elem = document.getElementById("dactyl-completions-" + node.id);
util.waitFor(bind(this.widgets._ready, null, elem)); util.waitFor(bind(this.widgets._ready, null, elem));
node.completionList = ItemList(elem.id); node.completionList = ItemList(elem);
} }
return node.completionList; return node.completionList;
}, },
@@ -1038,7 +1038,7 @@ var CommandLine = Module("commandline", {
this.wildmode = options.get("wildmode"); this.wildmode = options.get("wildmode");
this.wildtypes = this.wildmode.value; this.wildtypes = this.wildmode.value;
this.itemList = commandline.completionList; this.itemList = commandline.completionList;
this.itemList.setItems(this.context); this.itemList.open(this.context);
dactyl.registerObserver("events.doneFeeding", this.closure.onDoneFeeding, true); dactyl.registerObserver("events.doneFeeding", this.closure.onDoneFeeding, true);
@@ -1051,10 +1051,14 @@ var CommandLine = Module("commandline", {
} }
}, this); }, this);
this.tabTimer = Timer(0, 0, function tabTell(event) { this.tabTimer = Timer(0, 0, function tabTell(event) {
this.tab(event.shiftKey, event.altKey && options["altwildmode"]); let tabCount = this.tabCount;
this.tabCount = 0;
this.tab(tabCount, event.altKey && options["altwildmode"]);
}, this); }, this);
}, },
tabCount: 0,
cleanup: function () { cleanup: function () {
dactyl.unregisterObserver("events.doneFeeding", this.closure.onDoneFeeding); dactyl.unregisterObserver("events.doneFeeding", this.closure.onDoneFeeding);
this.previewClear(); this.previewClear();
@@ -1071,8 +1075,15 @@ var CommandLine = Module("commandline", {
this.ignoredCount = 0; this.ignoredCount = 0;
}, },
onTab: function onTab(event) {
this.tabCount += event.shiftKey ? -1 : 1;
this.tabTimer.tell(event);
},
UP: {}, UP: {},
DOWN: {}, DOWN: {},
CTXT_UP: {},
CTXT_DOWN: {},
PAGE_UP: {}, PAGE_UP: {},
PAGE_DOWN: {}, PAGE_DOWN: {},
RESET: null, RESET: null,
@@ -1162,14 +1173,16 @@ var CommandLine = Module("commandline", {
let value = this.completion; let value = this.completion;
if (util.compareIgnoreCase(value, substring.substr(0, value.length))) if (util.compareIgnoreCase(value, substring.substr(0, value.length)))
return; return;
substring = substring.substr(value.length); substring = substring.substr(value.length);
this.removeSubstring = substring; this.removeSubstring = substring;
let node = util.xmlToDom(<span highlight="Preview">{substring}</span>, let node = DOM.fromXML(<span highlight="Preview">{substring}</span>,
document); document);
let start = this.caret;
this.withSavedValues(["caret"], function () {
this.editor.insertNode(node, this.editor.rootElement, 1); this.editor.insertNode(node, this.editor.rootElement, 1);
this.caret = start; });
}, },
previewClear: function previewClear() { previewClear: function previewClear() {
@@ -1199,7 +1212,7 @@ var CommandLine = Module("commandline", {
this.suffix = this.context.value.substring(this.caret); this.suffix = this.context.value.substring(this.caret);
if (show) { if (show) {
this.itemList.reset(); this.itemList.update();
if (this.haveType("list")) if (this.haveType("list"))
this.itemList.visible = true; this.itemList.visible = true;
this.selected = null; this.selected = null;
@@ -1215,25 +1228,25 @@ var CommandLine = Module("commandline", {
this.value = value.substring(this.start, this.caret); this.value = value.substring(this.start, this.caret);
this.suffix = value.substring(this.caret); this.suffix = value.substring(this.caret);
this.itemList.reset(); this.itemList.update();
this.itemList.selectItem(this.selected); this.itemList.selectItem(this.selected);
this.preview(); this.preview();
}, },
select: function select(idx) { select: function select(idx, count) {
switch (idx) { switch (idx) {
case this.UP: case this.UP:
if (this.selected == null) if (this.selected == null)
idx = -2; idx = -1 - count;
else else
idx = this.selected - 1; idx = this.selected - count;
break; break;
case this.DOWN: case this.DOWN:
if (this.selected == null) if (this.selected == null)
idx = 0; idx = count - 1;
else else
idx = this.selected + 1; idx = this.selected + count;
break; break;
case this.RESET: case this.RESET:
idx = null; idx = null;
@@ -1287,7 +1300,7 @@ var CommandLine = Module("commandline", {
tabs: [], tabs: [],
tab: function tab(reverse, wildmode) { tab: function tab(count, wildmode) {
this.autocompleteTimer.flush(); this.autocompleteTimer.flush();
this.ignoredCount = 0; this.ignoredCount = 0;
@@ -1299,12 +1312,12 @@ var CommandLine = Module("commandline", {
if (this.context.waitingForTab || this.wildIndex == -1) if (this.context.waitingForTab || this.wildIndex == -1)
this.complete(true, true); this.complete(true, true);
this.tabs.push([reverse, wildmode || options["wildmode"]]); this.tabs.push([count, wildmode || options["wildmode"]]);
if (this.waiting) if (this.waiting)
return; return;
while (this.tabs.length) { while (this.tabs.length) {
[reverse, this.wildtypes] = this.tabs.shift(); [count, this.wildtypes] = this.tabs.shift();
this.wildIndex = Math.min(this.wildIndex, this.wildtypes.length - 1); this.wildIndex = Math.min(this.wildIndex, this.wildtypes.length - 1);
switch (this.wildtype.replace(/.*:/, "")) { switch (this.wildtype.replace(/.*:/, "")) {
@@ -1319,7 +1332,7 @@ var CommandLine = Module("commandline", {
} }
// Fallthrough // Fallthrough
case "full": case "full":
this.select(reverse ? this.UP : this.DOWN); this.select(count < 0 ? this.UP : this.DOWN, Math.abs(count));
break; break;
} }
@@ -1510,13 +1523,13 @@ var CommandLine = Module("commandline", {
bind(["<A-Tab>", "<Tab>"], "Select the next matching completion item", bind(["<A-Tab>", "<Tab>"], "Select the next matching completion item",
function ({ keypressEvents, self }) { function ({ keypressEvents, self }) {
dactyl.assert(self.completions); dactyl.assert(self.completions);
self.completions.tabTimer.tell(keypressEvents[0]); self.completions.onTab(keypressEvents[0]);
}); });
bind(["<A-S-Tab>", "<S-Tab>"], "Select the previous matching completion item", bind(["<A-S-Tab>", "<S-Tab>"], "Select the previous matching completion item",
function ({ keypressEvents, self }) { function ({ keypressEvents, self }) {
dactyl.assert(self.completions); dactyl.assert(self.completions);
self.completions.tabTimer.tell(keypressEvents[0]); self.completions.onTab(keypressEvents[0]);
}); });
bind(["<BS>", "<C-h>"], "Delete the previous character", bind(["<BS>", "<C-h>"], "Delete the previous character",
@@ -1585,25 +1598,339 @@ var CommandLine = Module("commandline", {
}); });
/** /**
* The list which is used for the completion box (and QuickFix window in * The list which is used for the completion box.
* future).
* *
* @param {string} id The id of the iframe which will display the list. It * @param {string} id The id of the iframe which will display the list. It
* must be in its own container element, whose height it will update as * must be in its own container element, whose height it will update as
* necessary. * necessary.
*/ */
var ItemList = Class("ItemList", {
init: function init(id) {
this._completionElements = [];
var iframe = document.getElementById(id); var NewItemList = Class("ItemList", {
CONTEXT_LINES: 3,
init: function init(frame) {
this.frame = frame;
this.doc = frame.contentDocument;
this.win = frame.contentWindow;
this.body = this.doc.body;
this.container = frame.parentNode;
highlight.highlightNode(this.doc.body, "Comp");
this._resize = Timer(20, 400, function _resize() {
if (this.visible)
this.resize();
}, this);
},
get rootXML() <e4x>
<div highlight="Normal" style="white-space: nowrap">
<div key="wrapper">
<div highlight="Completions" key="noCompletions"><span highlight="Title">{_("completion.noCompletions")}</span></div>
<div key="completions"/>
</div>
<div highlight="Completions">{
template.map(util.range(0, options["maxitems"] * 2), function (i)
<div highlight="CompItem NonText"><li>~</li></div>)
}</div>
</div>
</e4x>.elements(),
get visible() !this.container.collapsed,
set visible(val) this.container.collapsed = !val,
get activeGroups() this.context.contextList
.filter(function (c) c.message || c.incomplete
|| c.hasItems && c.items.length)
.map(this.getGroup, this),
open: function open(context) {
util.dump("\n\n\n\n");
util.dump("OPEN()");
this.context = context;
this.nodes = {x:1};
this.maxItems = options["maxitems"];
DOM(this.rootXML, this.doc, this.nodes)
.appendTo(DOM(this.body).empty());
this.update();
this.visible = true;
},
update: function update() {
util.dump("\n\n");
util.dump("UPDATE()");
DOM(this.nodes.completions).empty();
let groups = this.activeGroups;
let container = DOM(this.nodes.completions);
for each (let group in groups) {
group.reset();
container.append(group.nodes.root);
}
DOM(this.nodes.noCompletions).toggle(!groups.length);
this.select(groups[0] && groups[0].context, null);
this._resize.tell();
},
draw: function draw() {
util.dump("DRAW()");
for each (let group in this.activeGroups)
group.draw();
// We need to collect all of the rescrolling functions in
// one go, as the height calculation that they need to do
// would force a reflow after each DOM modification.
this.activeGroups.filter(function (g) !g.collapsed)
.map(function (g) g.rescrollFunc)
.forEach(function (f) f());
},
minHeight: 0,
resize: function resize() {
util.dump("RESIZE()");
let { completions, root } = this.nodes;
if (!this.visible)
root.style.minWidth = document.getElementById("dactyl-commandline").scrollWidth + "px";
this.minHeight = Math.max(this.minHeight,
this.win.scrollY + DOM(completions).rect.bottom);
if (!this.visible)
root.style.minWidth = "";
// FIXME: Belongs elsewhere.
mow.resize(false, Math.max(0, this.minHeight - this.container.height));
this.container.height = this.minHeight;
this.container.height -= mow.spaceNeeded;
mow.resize(false);
this.timeout(function () {
this.container.height -= mow.spaceNeeded;
});
},
select: function select(context, index, position) {
util.dump("SELECT()");
let group = this.getGroup(context);
if (this.selectedGroup && (!group || group != this.selectedGroup))
this.selectedGroup.selectedIdx = null;
this.selectedGroup = group;
if (group)
group.selectedIdx = index;
if (position != null)
this.selectionPosition = position;
let groups = this.activeGroups;
if (groups.length) {
group = group || groups[0];
let idx = groups.indexOf(group);
let count = this.maxItems;
group.count = Math.min((group.selectedIdx || 0) + this.CONTEXT_LINES,
group.itemCount, count);
count -= group.count;
for (let i = idx - 1; i >= 0; i--) {
let group = groups[i];
group.count = Math.min(group.itemCount, count);
count -= group.count;
}
let n = group.count;
group.count = Math.min(group.count + count, group.itemCount);
count -= group.count - n;
for (let i = idx + 1; i < groups.length; i++) {
let group = groups[i];
group.count = Math.min(group.itemCount, count);
count -= group.count;
}
for (let [i, group] in Iterator(groups)) {
group.collapsed = group.count == 0;
if (i < idx)
group.range = ItemList.Range(group.itemCount - group.count,
group.itemCount);
else if (i > idx)
group.range = ItemList.Range(0, group.count);
else {
let end = Math.max(group.count,
Math.min(group.selectedIdx + this.CONTEXT_LINES,
group.itemCount));
group.range = ItemList.Range(end - group.count, end);
}
}
}
this.draw();
},
selectItem: function selectItem(idx) {
if (idx != null)
for each (var group in this.activeGroups) {
if (idx < group.itemCount)
break;
idx -= group.itemCount;
}
this.select(group && group.context, idx);
},
getGroup: function getGroup(context) context &&
context.getCache("itemlist-group", bind("Group", ItemList, this, context))
}, {
WAITING_MESSAGE: _("completion.generating"),
Group: Class("ItemList.Group", {
init: function init(parent, context) {
this.parent = parent;
this.context = context;
},
get rootXML()
<div key="root" highlight="CompGroup">
<div highlight="Completions">
{ this.context.createRow(this.context.title || [], "CompTitle") }
</div>
<div highlight="CompTitleSep"/>
<div key="contents">
<div key="up" highlight="CompLess"/>
<div key="message" highlight="CompMsg">{this.context.message}</div>
<div key="itemsContainer" class="completion-items-container">
<div key="items" highlight="Completions"/>
</div>
<div key="waiting" highlight="CompMsg">{ItemList.WAITING_MESSAGE}</div>
<div key="down" highlight="CompMore"/>
</div>
</div>,
get doc() this.parent.doc,
get win() this.parent.win,
get maxItems() this.parent.maxItems,
get itemCount() this.context.items.length,
get rescrollFunc() {
let container = this.nodes.itemsContainer;
let pos = DOM(container).rect.top;
let start = DOM(this.getItem(this.range.start)).rect.top;
let height = DOM(this.getItem(this.range.end - 1)).rect.bottom - start || 0;
let scroll = start + container.scrollTop - pos;
return function () {
container.scrollTop = scroll;
container.style.height = height + "px";
}
},
draw: function draw() {
util.dump("draw(" + [this.collapsed, this.itemCount, this.count] + ") [" +
(!this.collapsed && [this.range.start, this.range.end]) + ") [" +
[this.generatedRange.start,
this.generatedRange.end] + ") " +
(!this.collapsed && this.generatedRange.contains(this.range)));
DOM(this.nodes.contents).toggle(!this.collapsed);
if (this.collapsed)
return;
DOM(this.nodes.message).toggle(this.context.message && this.range.start == 0);
DOM(this.nodes.waiting).toggle(this.context.incomplete && this.range.end < this.itemCount);
DOM(this.nodes.up).toggle(this.range.start > 0);
DOM(this.nodes.down).toggle(this.range.end < this.itemCount);
if (!this.generatedRange.contains(this.range)) {
if (this.generatedRange.end == 0)
var [start, end] = this.range;
else {
start = this.range.start - (this.range.start <= this.generatedRange.start
? this.maxItems / 2 : 0);
end = this.range.end + (this.range.end > this.generatedRange.end
? this.maxItems / 2 : 0);
}
util.dump(" refill [" + [start,end] + ")");
let range = ItemList.Range(Math.max(0, start), Math.min(this.itemCount, end));
let first;
for (let [i, row] in this.context.getRows(this.generatedRange.start,
this.generatedRange.end,
this.doc))
if (!range.contains(i))
DOM(row).remove();
else if (!first)
first = row;
let container = DOM(this.nodes.items);
let before = first ? DOM(first).closure.before
: DOM(this.nodes.items).closure.append;
for (let [i, row] in this.context.getRows(range.start, range.end,
this.doc))
if (i < this.generatedRange.start)
before(row);
else if (i >= this.generatedRange.end)
container.append(row);
this.generatedRange = range;
}
},
reset: function reset() {
this.nodes = {};
this.generatedRange = ItemList.Range(0, 0);
DOM.fromXML(this.rootXML, this.doc, this.nodes);
},
getItem: function getItem(idx) this.context.getRow(idx),
get selectedItem() this.getItem(this._selectedIdx),
get selectedIdx() this._selectedIdx,
set selectedIdx(idx) {
if (this.selectedItem)
DOM(this.selectedItem).attr("selected", null);
this._selectedIdx = idx;
if (this.selectedItem)
DOM(this.selectedItem).attr("selected", true);
}
}),
Range: Class.Memoize(function () {
let Range = Struct("ItemList.Range", "start", "end");
update(Range.prototype, {
contains: function contains(idx)
typeof idx == "number" ? idx >= this.start && idx < this.end
: this.contains(idx.start) &&
idx.end >= this.start && idx.end <= this.end
});
return Range;
})
});
var ItemList = Class("ItemList", {
init: function init(iframe) {
this._completionElements = [];
this._doc = iframe.contentDocument; this._doc = iframe.contentDocument;
this._win = iframe.contentWindow; this._win = iframe.contentWindow;
this._container = iframe.parentNode; this._container = iframe.parentNode;
this._doc.documentElement.id = id + "-top"; this._doc.documentElement.id = iframe.id + "-top";
this._doc.body.id = id + "-content"; this._doc.body.id = iframe.id + "-content";
this._doc.body.className = iframe.className + "-content"; this._doc.body.className = iframe.className + "-content";
this._doc.body.appendChild(this._doc.createTextNode("")); this._doc.body.appendChild(this._doc.createTextNode(""));
this._doc.body.style.borderTop = "1px solid black"; // FIXME: For cases where completions/MOW are shown at once, or ls=0. Should use :highlight. this._doc.body.style.borderTop = "1px solid black"; // FIXME: For cases where completions/MOW are shown at once, or ls=0. Should use :highlight.
@@ -1647,7 +1974,7 @@ var ItemList = Class("ItemList", {
_init: function _init() { _init: function _init() {
this._div = this._dom( this._div = this._dom(
<div class="ex-command-output" highlight="Normal" style="white-space: nowrap"> <div highlight="CommandOutput Normal" style="white-space: nowrap">
<div key="wrapper"> <div key="wrapper">
<div highlight="Completions" key="noCompletions"><span highlight="Title">{_("completion.noCompletions")}</span></div> <div highlight="Completions" key="noCompletions"><span highlight="Title">{_("completion.noCompletions")}</span></div>
<div key="completions"/> <div key="completions"/>
@@ -1676,7 +2003,9 @@ var ItemList = Class("ItemList", {
<div highlight="CompTitleSep"/> <div highlight="CompTitleSep"/>
<div key="message" highlight="CompMsg"/> <div key="message" highlight="CompMsg"/>
<div key="up" highlight="CompLess"/> <div key="up" highlight="CompLess"/>
<div key="itemsContainer">
<div key="items" highlight="Completions"/> <div key="items" highlight="Completions"/>
</div>
<div key="waiting" highlight="CompMsg">{ItemList.WAITING_MESSAGE}</div> <div key="waiting" highlight="CompMsg">{ItemList.WAITING_MESSAGE}</div>
<div key="down" highlight="CompMore"/> <div key="down" highlight="CompMore"/>
</div>, context.cache.nodes); </div>, context.cache.nodes);
@@ -1776,7 +2105,7 @@ var ItemList = Class("ItemList", {
get visible() !this._container.collapsed, get visible() !this._container.collapsed,
set visible(val) this._container.collapsed = !val, set visible(val) this._container.collapsed = !val,
reset: function reset(brief) { update: function reset(brief) {
this._startIndex = this._endIndex = this._selIndex = -1; this._startIndex = this._endIndex = this._selIndex = -1;
this._div = null; this._div = null;
if (!brief) if (!brief)
@@ -1787,20 +2116,22 @@ var ItemList = Class("ItemList", {
setItems: function setItems(newItems, selectedItem) { setItems: function setItems(newItems, selectedItem) {
if (this._selItem > -1) if (this._selItem > -1)
this._getCompletion(this._selItem).removeAttribute("selected"); this._getCompletion(this._selItem).removeAttribute("selected");
if (this._container.collapsed) { if (this._container.collapsed) {
this._minHeight = 0; this._minHeight = 0;
this._container.height = 0; this._container.height = 0;
} }
this._startIndex = this._endIndex = this._selIndex = -1; this._startIndex = this._endIndex = this._selIndex = -1;
this._items = newItems; this._items = newItems;
this.reset(true); this.update(true);
if (typeof selectedItem == "number") { if (typeof selectedItem == "number") {
this.selectItem(selectedItem); this.selectItem(selectedItem);
this.visible = true; this.visible = true;
} }
}, },
get open() this.closure.setItems,
// select index, refill list if necessary
selectItem: function selectItem(index) { selectItem: function selectItem(index) {
//let now = Date.now(); //let now = Date.now();
@@ -1848,7 +2179,7 @@ var ItemList = Class("ItemList", {
onKeyPress: function onKeyPress(event) false onKeyPress: function onKeyPress(event) false
}, { }, {
WAITING_MESSAGE: _("completion.generating") WAITING_MESSAGE: _("completion.generating"),
}); });
// vim: set fdm=marker sw=4 ts=4 et: // vim: set fdm=marker sw=4 ts=4 et:

View File

@@ -21,11 +21,9 @@ var MOW = Module("mow", {
if (modes.have(modes.OUTPUT_MULTILINE)) { if (modes.have(modes.OUTPUT_MULTILINE)) {
this.resize(true); this.resize(true);
if (options["more"] && this.canScroll(1)) { if (options["more"] && this.canScroll(1))
// start the last executed command's output at the top of the screen // start the last executed command's output at the top of the screen
let elements = this.document.getElementsByClassName("ex-command-output"); DOM(this.document.body.lastElementChild).scrollIntoView(true);
DOM(elements[elements.length - 1]).scrollIntoView(true);
}
else else
this.body.scrollTop = this.body.scrollHeight; this.body.scrollTop = this.body.scrollHeight;
@@ -122,10 +120,12 @@ var MOW = Module("mow", {
// after interpolated data. // after interpolated data.
XML.ignoreWhitespace = XML.prettyPrinting = false; XML.ignoreWhitespace = XML.prettyPrinting = false;
highlightGroup = "CommandOutput " + (highlight || "");
if (isObject(data) && !isinstance(data, _)) { if (isObject(data) && !isinstance(data, _)) {
this.lastOutput = null; this.lastOutput = null;
var output = DOM(<div class="ex-command-output" style="white-space: nowrap" highlight={highlightGroup}/>, var output = DOM(<div style="white-space: nowrap" highlight={highlightGroup}/>,
this.document); this.document);
data.document = this.document; data.document = this.document;
try { try {
@@ -139,7 +139,7 @@ var MOW = Module("mow", {
} }
else { else {
let style = isString(data) ? "pre-wrap" : "nowrap"; let style = isString(data) ? "pre-wrap" : "nowrap";
this.lastOutput = <div class="ex-command-output" style={"white-space: " + style} highlight={highlightGroup}>{data}</div>; this.lastOutput = <div style={"white-space: " + style} highlight={highlightGroup}>{data}</div>;
var output = DOM(this.lastOutput, this.document); var output = DOM(this.lastOutput, this.document);
} }

View File

@@ -868,6 +868,7 @@ Class.Memoize = function Memoize(getter, wait)
let done = false; let done = false;
if (wait) if (wait)
// Crazy, yeah, I know. -- Kris
this.get = function replace() { this.get = function replace() {
let obj = this.instance || this; let obj = this.instance || this;
Object.defineProperty(obj, key, { Object.defineProperty(obj, key, {
@@ -892,7 +893,7 @@ Class.Memoize = function Memoize(getter, wait)
return this[key]; return this[key];
}; };
else else
this.get = function replace() { this.get = function g_Memoize() {
let obj = this.instance || this; let obj = this.instance || this;
try { try {
Class.replaceProperty(obj, key, null); Class.replaceProperty(obj, key, null);
@@ -903,7 +904,7 @@ Class.Memoize = function Memoize(getter, wait)
} }
}; };
this.set = function replace(val) Class.replaceProperty(this.instance || this, val); this.set = function s_Memoize(val) Class.replaceProperty(this.instance || this, key, val);
} }
}); });
@@ -1227,6 +1228,8 @@ var StructBase = Class("StructBase", Array, {
this[i] = arguments[i]; this[i] = arguments[i];
}, },
get toStringParams() this,
clone: function struct_clone() this.constructor.apply(null, this.slice()), clone: function struct_clone() this.constructor.apply(null, this.slice()),
closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")), closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),

View File

@@ -289,7 +289,7 @@ var Buffer = Module("Buffer", {
* @param {Node} elem The element to focus. * @param {Node} elem The element to focus.
*/ */
focusElement: function focusElement(elem) { focusElement: function focusElement(elem) {
let { dactyl } = this.modules; let { Editor, dactyl } = this.modules;
let win = elem.ownerDocument && elem.ownerDocument.defaultView || elem; let win = elem.ownerDocument && elem.ownerDocument.defaultView || elem;
overlay.setData(elem, "focus-allowed", true); overlay.setData(elem, "focus-allowed", true);

View File

@@ -633,24 +633,32 @@ var CompletionContext = Class("CompletionContext", {
return iter.map(util.range(start, end, step), function (i) items[i]); return iter.map(util.range(start, end, step), function (i) items[i]);
}, },
getRow: function getRow(idx) this.cache.rows && this.cache.rows[idx],
getRows: function getRows(start, end, doc) { getRows: function getRows(start, end, doc) {
let self = this; let self = this;
let items = this.items; let items = this.items;
let cache = this.cache.rows; let cache = this.cache.rows;
let step = start > end ? -1 : 1; let step = start > end ? -1 : 1;
start = Math.max(0, start || 0); start = Math.max(0, start || 0);
end = Math.min(items.length, end != null ? end : items.length); end = Math.min(items.length, end != null ? end : items.length);
for (let i in util.range(start, end, step))
for (let i in util.range(start, end, step)) {
if (!cache[i])
try { try {
yield [i, cache[i] = cache[i] || util.xmlToDom(self.createRow(items[i]), doc)]; cache[i] = util.xmlToDom(self.createRow(items[i]), doc);
} }
catch (e) { catch (e) {
util.reportError(e); util.reportError(e);
yield [i, cache[i] = cache[i] || util.xmlToDom( cache[i] = util.xmlToDom(
<div highlight="CompItem" style="white-space: nowrap"> <div highlight="CompItem" style="white-space: nowrap">
<li highlight="CompResult">{items[i].text}&#xa0;</li> <li highlight="CompResult">{items[i].text}&#xa0;</li>
<li highlight="CompDesc ErrorMsg">{e}&#xa0;</li> <li highlight="CompDesc ErrorMsg">{e}&#xa0;</li>
</div>, doc)]; </div>, doc);
}
yield [i, cache[i]];
} }
}, },

View File

@@ -94,7 +94,7 @@ var Contexts = Module("contexts", {
cleanup: function () { cleanup: function () {
for each (let module in this.pluginModules) for each (let module in this.pluginModules)
util.trapErrors("cleanup", module); util.trapErrors("unload", module);
this.pluginModules = {}; this.pluginModules = {};
}, },

View File

@@ -36,10 +36,13 @@ function BooleanAttribute(attr) ({
* change in the near future. * change in the near future.
*/ */
var DOM = Class("DOM", { var DOM = Class("DOM", {
init: function init(val, context) { init: function init(val, context, nodes) {
let self; let self;
let length = 0; let length = 0;
if (nodes)
this.nodes = nodes;
if (context instanceof Ci.nsIDOMDocument) if (context instanceof Ci.nsIDOMDocument)
this.document = context; this.document = context;
@@ -48,7 +51,7 @@ var DOM = Class("DOM", {
if (val == null) if (val == null)
; ;
else if (typeof val == "xml") else if (typeof val == "xml" && context instanceof Ci.nsIDOMDocument)
this[length++] = DOM.fromXML(val, context, this.nodes); this[length++] = DOM.fromXML(val, context, this.nodes);
else if (val instanceof Ci.nsIDOMNode || val instanceof Ci.nsIDOMWindow) else if (val instanceof Ci.nsIDOMNode || val instanceof Ci.nsIDOMWindow)
this[length++] = val; this[length++] = val;
@@ -58,6 +61,8 @@ var DOM = Class("DOM", {
else if ("__iterator__" in val || isinstance(val, ["Iterator", "Generator"])) else if ("__iterator__" in val || isinstance(val, ["Iterator", "Generator"]))
for (let elem in val) for (let elem in val)
this[length++] = elem; this[length++] = elem;
else
this[length++] = val;
this.length = length; this.length = length;
return self || this; return self || this;
@@ -130,19 +135,22 @@ var DOM = Class("DOM", {
}, self || this); }, self || this);
let dom = this; let dom = this;
function munge(val) { function munge(val, container, idx) {
if (val instanceof Ci.nsIDOMRange) if (val instanceof Ci.nsIDOMRange)
return val.extractContents(); return val.extractContents();
if (val instanceof Ci.nsIDOMNode) if (val instanceof Ci.nsIDOMNode)
return val; return val;
if (typeof val == "xml") if (typeof val == "xml") {
val = dom.constructor(val, dom.document); val = dom.constructor(val, dom.document);
if (container)
container[idx] = val[0];
}
if (isObject(val) && "length" in val) { if (isObject(val) && "length" in val) {
let frag = dom.document.createDocumentFragment(); let frag = dom.document.createDocumentFragment();
for (let i = 0; i < val.length; i++) for (let i = 0; i < val.length; i++)
frag.appendChild(munge(val[i])); frag.appendChild(munge(val[i], val, i));
return frag; return frag;
} }
return val; return val;

View File

@@ -28,7 +28,7 @@ var FailedAssertion = Class("FailedAssertion", ErrorBase, {
noTrace: true noTrace: true
}); });
var Point = Struct("x", "y"); var Point = Struct("Point", "x", "y");
var wrapCallback = function wrapCallback(fn, isEvent) { var wrapCallback = function wrapCallback(fn, isEvent) {
if (!fn.wrapper) if (!fn.wrapper)

View File

@@ -68,6 +68,10 @@ input[type=file][dactyl|highlight~=HintElem] {
line-height: 1.5em !important; line-height: 1.5em !important;
} }
.completion-items-container {
overflow: hidden;
}
.td-span { .td-span {
display: inline-block; display: inline-block;
overflow: visible; overflow: visible;
@@ -191,11 +195,9 @@ statusbarpanel {
/* MOW */ /* MOW */
.dactyl-completions, #dactyl-commandline-prompt *,
#dactyl-multiline-output, #dactyl-commandline-command {
#dactyl-multiline-input { font: inherit;
background-color: white;
color: black;
} }
.dactyl-completions-content, .dactyl-completions-content,
@@ -206,11 +208,6 @@ statusbarpanel {
margin: 0px; margin: 0px;
} }
#dactyl-commandline-prompt *,
#dactyl-commandline-command {
font: inherit;
}
.dactyl-completions-content table, .dactyl-completions-content table,
#dactyl-multiline-output-content table { #dactyl-multiline-output-content table {
white-space: inherit; white-space: inherit;

View File

@@ -84,6 +84,9 @@ CmdInput;.dactyl-commandline-command
CmdOutput /* The output of commands executed by <ex>:run</ex> */ \ CmdOutput /* The output of commands executed by <ex>:run</ex> */ \
white-space: pre; white-space: pre;
Comp;;;FontFixed,Normal /* The completion window */ \
margin: 0; border-top: 1px solid black;
CompGroup /* Item group in completion output */ CompGroup /* Item group in completion output */
CompGroup:not(:first-of-type) margin-top: .5em; CompGroup:not(:first-of-type) margin-top: .5em;
CompGroup:last-of-type padding-bottom: 1.5ex; CompGroup:last-of-type padding-bottom: 1.5ex;