mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-20 15:37:59 +01:00
Major documentation updates and formatting fixes, and many, many other changes thanks to an MQ glitch, including:
* Significant completion speed improvements * Significantly improve startup speed, in large part by lazily instantiating Options and Commands, lazily installing highlight stylesheets, etc. * Update logos and icons, fix atrocious about page * Fix Teledactyl * JavaScript completion now avoids accessing property values * Add Option#persist to define which options are saved with :mkp * Add new Dactyl component which holds add-on-specific configuration information and removes need for separate components for each dactyl host * Several fixes for latest nightlies * Significant code cleanup and many bug fixes --HG-- rename : muttator/AUTHORS => teledactyl/AUTHORS rename : muttator/Donors => teledactyl/Donors rename : muttator/Makefile => teledactyl/Makefile rename : muttator/NEWS => teledactyl/NEWS rename : muttator/TODO => teledactyl/TODO rename : muttator/chrome.manifest => teledactyl/chrome.manifest rename : muttator/components/commandline-handler.js => teledactyl/components/commandline-handler.js rename : muttator/components/protocols.js => teledactyl/components/protocols.js rename : muttator/content/addressbook.js => teledactyl/content/addressbook.js rename : muttator/content/compose/compose.js => teledactyl/content/compose/compose.js rename : muttator/content/compose/compose.xul => teledactyl/content/compose/compose.xul rename : muttator/content/compose/dactyl.dtd => teledactyl/content/compose/dactyl.dtd rename : muttator/content/compose/dactyl.xul => teledactyl/content/compose/dactyl.xul rename : muttator/content/config.js => teledactyl/content/config.js rename : muttator/content/dactyl.dtd => teledactyl/content/dactyl.dtd rename : muttator/content/logo.png => teledactyl/content/logo.png rename : muttator/content/mail.js => teledactyl/content/mail.js rename : muttator/content/muttator.xul => teledactyl/content/pentadactyl.xul rename : muttator/contrib/vim/Makefile => teledactyl/contrib/vim/Makefile rename : muttator/contrib/vim/ftdetect/muttator.vim => teledactyl/contrib/vim/ftdetect/muttator.vim rename : muttator/contrib/vim/mkvimball.txt => teledactyl/contrib/vim/mkvimball.txt rename : muttator/contrib/vim/syntax/muttator.vim => teledactyl/contrib/vim/syntax/muttator.vim rename : muttator/install.rdf => teledactyl/install.rdf rename : muttator/locale/en-US/Makefile => teledactyl/locale/en-US/Makefile rename : muttator/locale/en-US/all.xml => teledactyl/locale/en-US/all.xml rename : muttator/locale/en-US/autocommands.xml => teledactyl/locale/en-US/autocommands.xml rename : muttator/locale/en-US/gui.xml => teledactyl/locale/en-US/gui.xml rename : muttator/locale/en-US/intro.xml => teledactyl/locale/en-US/intro.xml rename : muttator/skin/icon.png => teledactyl/skin/icon.png
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
@@ -54,7 +54,7 @@ const CompletionContext = Class("CompletionContext", {
|
||||
|
||||
["filters", "keys", "title", "quote"].forEach(function (key)
|
||||
self[key] = parent[key] && util.cloneObject(parent[key]));
|
||||
["anchored", "compare", "editor", "_filter", "filterFunc", "keys", "_process", "top"].forEach(function (key)
|
||||
["anchored", "compare", "editor", "_filter", "filterFunc", "keys", "process", "top"].forEach(function (key)
|
||||
self[key] = parent[key]);
|
||||
|
||||
self.__defineGetter__("value", function () this.top.value);
|
||||
@@ -90,14 +90,34 @@ const CompletionContext = Class("CompletionContext", {
|
||||
this._value = editor;
|
||||
else
|
||||
this.editor = editor;
|
||||
this.compare = function (a, b) String.localeCompare(a.text, b.text);
|
||||
/**
|
||||
* @property {boolean} Specifies whether this context results must
|
||||
* match the filter at the beginning of the string.
|
||||
* @default true
|
||||
*/
|
||||
this.anchored = true;
|
||||
|
||||
this.compare = function (a, b) String.localeCompare(a.text, b.text);
|
||||
/**
|
||||
* @property {function} This function is called when we close
|
||||
* a completion window with Esc or Ctrl-c. Usually this callback
|
||||
* is only needed for long, asynchronous completions
|
||||
*/
|
||||
this.cancel = null;
|
||||
/**
|
||||
* @property {[CompletionContext]} A list of active
|
||||
* completion contexts, in the order in which they were
|
||||
* instantiated.
|
||||
*/
|
||||
this.contextList = [];
|
||||
/**
|
||||
* @property {Object} A map of all contexts, keyed on their names.
|
||||
* Names are assigned when a context is forked, with its specified
|
||||
* name appended, after a '/', to its parent's name. May
|
||||
* contain inactive contexts. For active contexts, see
|
||||
* {@link #contextList}.
|
||||
*/
|
||||
this.contexts = { "": this };
|
||||
/**
|
||||
* @property {function} The function used to filter the results.
|
||||
* @default Selects all results which match every predicate in the
|
||||
@@ -114,20 +134,6 @@ const CompletionContext = Class("CompletionContext", {
|
||||
* results.
|
||||
*/
|
||||
this.filters = [CompletionContext.Filter.text];
|
||||
/**
|
||||
* @property {boolean} Specifies whether this context results must
|
||||
* match the filter at the beginning of the string.
|
||||
* @default true
|
||||
*/
|
||||
this.anchored = true;
|
||||
/**
|
||||
* @property {Object} A map of all contexts, keyed on their names.
|
||||
* Names are assigned when a context is forked, with its specified
|
||||
* name appended, after a '/', to its parent's name. May
|
||||
* contain inactive contexts. For active contexts, see
|
||||
* {@link #contextList}.
|
||||
*/
|
||||
this.contexts = { "": this };
|
||||
/**
|
||||
* @property {Object} A mapping of keys, for {@link #getKey}. Given
|
||||
* { key: value }, getKey(item, key) will return values as such:
|
||||
@@ -146,6 +152,9 @@ const CompletionContext = Class("CompletionContext", {
|
||||
* {@link #updateAsync} is true.
|
||||
*/
|
||||
this.onUpdate = function () true;
|
||||
|
||||
this.runCount = 0;
|
||||
|
||||
/**
|
||||
* @property {CompletionContext} The top-level completion context.
|
||||
*/
|
||||
@@ -190,6 +199,7 @@ const CompletionContext = Class("CompletionContext", {
|
||||
: item.item[key];
|
||||
return this;
|
||||
},
|
||||
|
||||
// Temporary
|
||||
/**
|
||||
* @property {Object}
|
||||
@@ -202,7 +212,7 @@ const CompletionContext = Class("CompletionContext", {
|
||||
get allItems() {
|
||||
try {
|
||||
let self = this;
|
||||
let minStart = Math.min.apply(Math, [context.offset for ([k, context] in Iterator(this.contexts)) if (context.items.length && context.hasItems)]);
|
||||
let minStart = Math.min.apply(Math, [context.offset for ([k, context] in Iterator(this.contexts)) if (context.hasItems && context.items.length)]);
|
||||
if (minStart == Infinity)
|
||||
minStart = 0;
|
||||
let items = this.contextList.map(function (context) {
|
||||
@@ -214,7 +224,7 @@ const CompletionContext = Class("CompletionContext", {
|
||||
__proto__: item
|
||||
}));
|
||||
});
|
||||
return { start: minStart, items: util.Array.flatten(items), longestSubstring: this.longestAllSubstring };
|
||||
return { start: minStart, items: array.flatten(items), longestSubstring: this.longestAllSubstring };
|
||||
}
|
||||
catch (e) {
|
||||
dactyl.reportError(e);
|
||||
@@ -235,7 +245,7 @@ const CompletionContext = Class("CompletionContext", {
|
||||
lists.pop());
|
||||
if (!substrings) // FIXME: How is this undefined?
|
||||
return [];
|
||||
return util.Array.uniq(Array.slice(substrings));
|
||||
return array.uniq(Array.slice(substrings));
|
||||
},
|
||||
// Temporary
|
||||
get longestAllSubstring() {
|
||||
@@ -253,14 +263,16 @@ const CompletionContext = Class("CompletionContext", {
|
||||
// Accept a generator
|
||||
if (!isarray(items))
|
||||
items = [x for (x in Iterator(items))];
|
||||
delete this.cache.filtered;
|
||||
delete this.cache.filter;
|
||||
this.cache.rows = [];
|
||||
this.hasItems = items.length > 0;
|
||||
this._completions = items;
|
||||
let self = this;
|
||||
if (this._completions !== items) {
|
||||
delete this.cache.filtered;
|
||||
delete this.cache.filter;
|
||||
this.cache.rows = [];
|
||||
this.hasItems = items.length > 0;
|
||||
this._completions = items;
|
||||
this.itemCache[this.key] = items;
|
||||
}
|
||||
if (this.updateAsync && !this.noUpdate)
|
||||
util.callInMainThread(function () { self.onUpdate.call(self); });
|
||||
util.callInMainThread(function () { this.onUpdate(); }, this);
|
||||
},
|
||||
|
||||
get createRow() this._createRow || template.completionRow, // XXX
|
||||
@@ -293,14 +305,18 @@ const CompletionContext = Class("CompletionContext", {
|
||||
|
||||
get proto() {
|
||||
let res = {};
|
||||
for (let i in Iterator(this.keys)) {
|
||||
function result(quote) {
|
||||
yield ["result", quote ? function () quote[0] + quote[1](this.text) + quote[2]
|
||||
: function () this.text]
|
||||
};
|
||||
for (let i in iterall(this.keys, result(this.quote))) {
|
||||
let [k, v] = i;
|
||||
if (typeof v == "string" && /^[.[]/.test(v))
|
||||
// This is only allowed to be a simple accessor, and shouldn't
|
||||
// reference any variables. Don't bother with eval context.
|
||||
v = Function("i", "return i" + v);
|
||||
if (typeof v == "function")
|
||||
res.__defineGetter__(k, function () Class.replaceProperty(this, k, v(this.item)));
|
||||
res.__defineGetter__(k, function () Class.replaceProperty(this, k, v.call(this, this.item)));
|
||||
else
|
||||
res.__defineGetter__(k, function () Class.replaceProperty(this, k, this.item[v]));
|
||||
res.__defineSetter__(k, function (val) Class.replaceProperty(this, k, val));
|
||||
@@ -312,11 +328,16 @@ const CompletionContext = Class("CompletionContext", {
|
||||
set regenerate(val) { if (val) delete this.itemCache[this.key]; },
|
||||
|
||||
get generate() !this._generate ? null : function () {
|
||||
if (this.offset != this.cache.offset)
|
||||
if (this.offset != this.cache.offset || this.lastActivated != this.top.runCount) {
|
||||
this.itemCache = {};
|
||||
this.cache.offset = this.offset;
|
||||
if (!this.itemCache[this.key])
|
||||
this.itemCache[this.key] = this._generate.call(this) || [];
|
||||
this.cache.offset = this.offset;
|
||||
this.lastActivated = this.top.runCount;
|
||||
}
|
||||
if (!this.itemCache[this.key]) {
|
||||
let res = this._generate.call(this) || [];
|
||||
if (res != null)
|
||||
this.itemCache[this.key] = res;
|
||||
}
|
||||
return this.itemCache[this.key];
|
||||
},
|
||||
set generate(arg) {
|
||||
@@ -354,16 +375,27 @@ const CompletionContext = Class("CompletionContext", {
|
||||
get items() {
|
||||
if (!this.hasItems || this.backgroundLock)
|
||||
return [];
|
||||
if (this.cache.filtered && this.cache.filter == this.filter)
|
||||
return this.cache.filtered;
|
||||
this.cache.rows = [];
|
||||
let items = this.completions;
|
||||
|
||||
// Regenerate completions if we must
|
||||
if (this.generate && !this.background) {
|
||||
// XXX
|
||||
this.noUpdate = true;
|
||||
this.completions = items = this.generate();
|
||||
this.completions = this.generate();
|
||||
this.noUpdate = false;
|
||||
}
|
||||
let items = this.completions;
|
||||
|
||||
// Check for cache miss
|
||||
if (this.cache.completions !== this.completions) {
|
||||
this.cache.completions = this.completions;
|
||||
this.cache.constructed = null;
|
||||
this.cache.filtered = null;
|
||||
}
|
||||
|
||||
if (this.cache.filtered && this.cache.filter == this.filter)
|
||||
return this.cache.filtered;
|
||||
|
||||
this.cache.rows = [];
|
||||
this.cache.filter = this.filter;
|
||||
if (items == null)
|
||||
return items;
|
||||
@@ -371,6 +403,7 @@ const CompletionContext = Class("CompletionContext", {
|
||||
let self = this;
|
||||
delete this._substrings;
|
||||
|
||||
// Item matchers
|
||||
if (this.ignoreCase)
|
||||
this.matchString = this.anchored ?
|
||||
function (filter, str) String.toLowerCase(str).indexOf(filter.toLowerCase()) == 0 :
|
||||
@@ -380,36 +413,28 @@ const CompletionContext = Class("CompletionContext", {
|
||||
function (filter, str) String.indexOf(str, filter) == 0 :
|
||||
function (filter, str) String.indexOf(str, filter) >= 0;
|
||||
|
||||
// Item formatters
|
||||
this.processor = Array.slice(this.process);
|
||||
if (!this.anchored)
|
||||
this.processor[0] = function (item, text) self.process[0].call(self, item,
|
||||
template.highlightFilter(item.text, self.filter));
|
||||
|
||||
// Item prototypes
|
||||
let proto = this.proto;
|
||||
let filtered = this.filterFunc(items.map(function (item) ({ __proto__: proto, item: item })));
|
||||
if (!this.cache.constructed)
|
||||
this.cache.constructed = items.map(function (item) Object.create(proto, { item: { value: item, enumerable: true } }));
|
||||
|
||||
// Filters
|
||||
let filtered = this.filterFunc(this.cache.constructed);
|
||||
if (this.maxItems)
|
||||
filtered = filtered.slice(0, this.maxItems);
|
||||
|
||||
// Sorting
|
||||
if (this.sortResults && this.compare)
|
||||
filtered.sort(this.compare);
|
||||
let quote = this.quote;
|
||||
if (quote)
|
||||
filtered.forEach(function (item) {
|
||||
item.unquoted = item.text;
|
||||
item.text = quote[0] + quote[1](item.text) + quote[2];
|
||||
});
|
||||
return this.cache.filtered = filtered;
|
||||
},
|
||||
|
||||
get process() { // FIXME
|
||||
let self = this;
|
||||
let process = this._process;
|
||||
process = [process[0] || template.icon, process[1] || function (item, k) k];
|
||||
let first = process[0];
|
||||
let filter = this.filter;
|
||||
if (!this.anchored)
|
||||
process[0] = function (item, text) first.call(self, item, template.highlightFilter(item.text, filter));
|
||||
return process;
|
||||
},
|
||||
set process(process) {
|
||||
this._process = process;
|
||||
},
|
||||
|
||||
get substrings() {
|
||||
let items = this.items;
|
||||
if (items.length == 0 || !this.hasItems)
|
||||
@@ -418,7 +443,10 @@ const CompletionContext = Class("CompletionContext", {
|
||||
return this._substrings;
|
||||
|
||||
let fixCase = this.ignoreCase ? String.toLowerCase : util.identity;
|
||||
let text = fixCase(items[0].unquoted || items[0].text);
|
||||
let text = fixCase(items[0].text);
|
||||
// Exceedingly long substrings cause Gecko to go into convulsions
|
||||
if (text.length > 100)
|
||||
text = text.substr(0, 100);
|
||||
let filter = fixCase(this.filter);
|
||||
if (this.anchored) {
|
||||
var compare = function compare(text, s) text.substr(0, s.length) == s;
|
||||
@@ -521,7 +549,6 @@ const CompletionContext = Class("CompletionContext", {
|
||||
context.waitingForTab = true;
|
||||
else if (completer)
|
||||
return completer.apply(self || this, [context].concat(Array.slice(arguments, fork.length)));
|
||||
|
||||
if (completer)
|
||||
return null;
|
||||
return context;
|
||||
@@ -535,20 +562,24 @@ const CompletionContext = Class("CompletionContext", {
|
||||
},
|
||||
|
||||
highlight: function highlight(start, length, type) {
|
||||
try { // Gecko < 1.9.1 doesn't have repaintSelection
|
||||
this.selectionTypes[type] = null;
|
||||
if (arguments.length == 0) {
|
||||
for (let type in this.selectionTypes)
|
||||
this.highlight(0, 0, type);
|
||||
this.selectionTypes = {};
|
||||
}
|
||||
try {
|
||||
// Requires Gecko >= 1.9.1
|
||||
this.selectionTypes[type] = true;
|
||||
const selType = Ci.nsISelectionController["SELECTION_" + type];
|
||||
const editor = this.editor;
|
||||
let sel = editor.selectionController.getSelection(selType);
|
||||
let sel = this.editor.selectionController.getSelection(selType);
|
||||
if (length == 0)
|
||||
sel.removeAllRanges();
|
||||
else {
|
||||
let range = editor.selection.getRangeAt(0).cloneRange();
|
||||
let range = this.editor.selection.getRangeAt(0).cloneRange();
|
||||
range.setStart(range.startContainer, this.offset + start);
|
||||
range.setEnd(range.startContainer, this.offset + start + length);
|
||||
sel.addRange(range);
|
||||
}
|
||||
editor.selectionController.repaintSelection(selType);
|
||||
}
|
||||
catch (e) {}
|
||||
},
|
||||
@@ -557,23 +588,19 @@ const CompletionContext = Class("CompletionContext", {
|
||||
return this.matchString(this.filter, str);
|
||||
},
|
||||
|
||||
pushProcessor: function pushProcess(i, fn) {
|
||||
let next = this.process[i];
|
||||
this.process[i] = function (item, text) fn(item, text, next);
|
||||
},
|
||||
|
||||
reset: function reset() {
|
||||
let self = this;
|
||||
if (this.parent)
|
||||
throw Error();
|
||||
// Not ideal.
|
||||
for (let type in this.selectionTypes)
|
||||
this.highlight(0, 0, type);
|
||||
|
||||
/**
|
||||
* @property {[CompletionContext]} A list of active
|
||||
* completion contexts, in the order in which they were
|
||||
* instantiated.
|
||||
*/
|
||||
this.contextList = [];
|
||||
this.offset = 0;
|
||||
this.process = [];
|
||||
this.selectionTypes = {};
|
||||
this.process = [template.icon, function (item, k) k];
|
||||
this.filters = [CompletionContext.Filter.text];
|
||||
this.tabPressed = false;
|
||||
this.title = ["Completions"];
|
||||
this.updateAsync = false;
|
||||
@@ -595,6 +622,10 @@ const CompletionContext = Class("CompletionContext", {
|
||||
if (context != context.top)
|
||||
context.incomplete = false;
|
||||
}
|
||||
this.runCount++;
|
||||
for each (let context in this.contextList)
|
||||
context.lastActivated = this.runCount;
|
||||
this.contextList = [];
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -668,7 +699,7 @@ const Completion = Module("completion", {
|
||||
|
||||
commandline.commandOutput(
|
||||
<div highlight="Completions">
|
||||
{ template.map(context.contextList.filter(function (c) c.hasItems),
|
||||
{ template.map(context.contextList.filter(function (c) c.hasItems && c.items.length),
|
||||
function (context)
|
||||
template.completionRow(context.title, "CompTitle") +
|
||||
template.map(context.items, function (item) context.createRow(item), null, 100)) }
|
||||
@@ -747,7 +778,7 @@ const Completion = Module("completion", {
|
||||
|
||||
let re = RegExp(tokens.filter(util.identity).map(util.escapeRegex).join("|"), "g");
|
||||
function highlight(item, text, i) process[i].call(this, item, template.highlightRegexp(text, re));
|
||||
let process = [template.icon, function (item, k) k];
|
||||
let process = context.process;
|
||||
context.process = [
|
||||
function (item, text) highlight.call(this, item, item.text, 0),
|
||||
function (item, text) highlight.call(this, item, text, 1)
|
||||
|
||||
Reference in New Issue
Block a user