diff --git a/common/content/buffer.js b/common/content/buffer.js index 2a403ed8..78ada638 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -1841,7 +1841,15 @@ var Buffer = Module("buffer", { mappings.add([modes.NORMAL], ["G", "", ""], "Go to the end of the document", - function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 100); }, + function (args) { + if (args.count) + var elem = options.get("linenumbers").getLine(buffer.focusedFrame.document, + args.count); + if (elem) + elem.scrollIntoView(true); + else + buffer.scrollToPercent(null, args.count != null ? args.count : 100); + }, { count: true }); mappings.add([modes.NORMAL], ["%", ""], @@ -2132,6 +2140,52 @@ var Buffer = Module("buffer", { && Object.keys(value).every(function (v) v.length == 1) }); + options.add(["linenumbers", "ln"], + "Patterns used to determine line numbers used by G", + "sitemap", { + "code.google.com": '#nums [id^="nums_table"] a[href^="#"]', + "github.com": '.line_numbers>*', + "mxr.mozilla.org": 'a.l', + "pastebin.com": '#code_frame>div>ol>li' + }, + { + getLine: function getLine(doc, line) { + let uri = util.newURI(doc.documentURI); + for (let filter in values(this.value)) + if (filter(uri, doc)) { + if (/^func:/.test(filter.result)) + var res = dactyl.userEval("(" + Option.dequote(filter.result.substr(5)) + ")")(doc, line); + else + res = iter.nth(filter.matcher(doc), + function (elem) (elem.nodeValue || elem.textContent).trim() == line && DOM(elem).display != "none", + 0) + || iter.nth(filter.matcher(doc), util.identity, line - 1); + if (res) + break; + } + + return res; + }, + + keepQuotes: true, + + setter: function (vals) { + for (let value in values(vals)) + if (!/^func:/.test(value.result)) + value.matcher = DOM.compileMatcher(Option.splitList(value.result)); + return vals; + }, + + validate: function validate(values) { + return this.testValues(values, function (value) { + if (/^func:/.test(value)) + return callable(dactyl.userEval("(" + Option.dequote(value.substr(5)) + ")")); + else + return DOM.testMatcher(value); + }); + } + }); + options.add(["nextpattern"], "Patterns to use when guessing the next page in a document sequence", "regexplist", UTF8(/'\bnext\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\bmore\b'/.source), diff --git a/common/content/dactyl.js b/common/content/dactyl.js index c649dbd5..967c740d 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -111,20 +111,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { } }, - /** @property {string} The name of the current user profile. */ - profileName: Class.Memoize(function () { - // NOTE: services.profile.selectedProfile.name doesn't return - // what you might expect. It returns the last _actively_ selected - // profile (i.e. via the Profile Manager or -P option) rather than the - // current profile. These will differ if the current process was run - // without explicitly selecting a profile. - - let dir = services.directory.get("ProfD", Ci.nsIFile); - for (let prof in iter(services.profile.profiles)) - if (prof.QueryInterface(Ci.nsIToolkitProfile).rootDir.path === dir.path) - return prof.name; - return "unknown"; - }), + profileName: deprecated("config.profileName", { get: function profileName() config.profileName }), /** * @property {Modes.Mode} The current main mode. diff --git a/common/locale/en-US/buffer.xml b/common/locale/en-US/buffer.xml index 7e1b645b..21ac4bdc 100644 --- a/common/locale/en-US/buffer.xml +++ b/common/locale/en-US/buffer.xml @@ -134,7 +134,9 @@

Go to the end of the document. With count, - behaves exactly the same as gg. + go to the countth line as determined by linenumbers, + or to the countth percent of the document if the line number + can't be determined.

diff --git a/common/locale/en-US/options.xml b/common/locale/en-US/options.xml index 944d3693..5de5eb06 100644 --- a/common/locale/en-US/options.xml +++ b/common/locale/en-US/options.xml @@ -1034,6 +1034,25 @@ + + 'ln' 'linenumbers' + 'linenumbers' 'ln' + &option.linenumbers.type; + &option.linenumbers.default; + +

+ Patterns used to determine line numbers used by G. May be + either a selector expression as accepted by hinttags, in + which case the first matching element whose text content is equal to + the desired line number is used or the countth element + failing that, or the string func: followed by a + function which, given arguments for the document and desired line + number must return the target element. +

+
+
+ + 'lpl' 'loadplugins' 'loadplugins' 'lpl' diff --git a/common/modules/base.jsm b/common/modules/base.jsm index 88259be4..a9a064eb 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -122,7 +122,12 @@ if (!Object.keys) let getGlobalForObject = Cu.getGlobalForObject || function (obj) obj.__parent__; -let jsmodules = {}; +let jsmodules = { + lazyRequire: function lazyRequire(module, names, target) { + for each (let name in names) + memoize(target || this, name, function (name) require(module)[name]); + } +}; let use = {}; let loaded = {}; let currentModule; @@ -229,6 +234,8 @@ defineModule("base", { ] }, this); +this.lazyRequire("messages", ["_", "Messages"]); + /** * Returns a list of all of the top-level properties of an object, by * way of the debugger. @@ -325,7 +332,7 @@ deprecated.warn = function warn(func, name, alternative, frame) { let filename = util.fixURI(frame.filename || "unknown"); if (!Set.add(func.seenCaller, filename)) util.dactyl(func).warn([util.urlPath(filename), frame.lineNumber, " "].join(":") - + require("messages")._("warn.deprecated", name, alternative)); + + _("warn.deprecated", name, alternative)); } /** @@ -1242,7 +1249,7 @@ var StructBase = Class("StructBase", Array, { localize: function localize(key, defaultValue) { let i = this.prototype.members[key]; - Object.defineProperty(this.prototype, i, require("messages").Messages.Localized(defaultValue).init(key, this.prototype)); + Object.defineProperty(this.prototype, i, Messages.Localized(defaultValue).init(key, this.prototype)); return this; } }); diff --git a/common/modules/config.jsm b/common/modules/config.jsm index 4453edf4..33ac268b 100644 --- a/common/modules/config.jsm +++ b/common/modules/config.jsm @@ -13,6 +13,10 @@ defineModule("config", { require: ["dom", "protocol", "services", "storage", "util", "template"] }, this); +this.lazyRequire("addons", ["AddonManager"]); +this.lazyRequire("highlight", ["highlight"]); +this.lazyRequire("messages", ["_"]); + function AboutHandler() {} AboutHandler.prototype = { get classDescription() "About " + config.appName + " Page", @@ -102,9 +106,6 @@ var ConfigBase = Class("ConfigBase", { }, loadStyles: function loadStyles(force) { - const { highlight } = require("highlight"); - const { _ } = require("messages"); - highlight.styleableChrome = this.styleableChrome; highlight.loadCSS(this.CSS.replace(/__MSG_(.*?)__/g, function (m0, m1) _(m1))); @@ -145,7 +146,7 @@ var ConfigBase = Class("ConfigBase", { addon: Class.Memoize(function () { return (JSMLoader.bootstrap || {}).addon || - require("addons").AddonManager.getAddonByID(this.addonID); + AddonManager.getAddonByID(this.addonID); }), /** @@ -311,6 +312,21 @@ var ConfigBase = Class("ConfigBase", { return (/pre-hg\d+-(\S*)/.exec(this.version) || [])[1]; }), + /** @property {string} The name of the current user profile. */ + profileName: Class.Memoize(function () { + // NOTE: services.profile.selectedProfile.name doesn't return + // what you might expect. It returns the last _actively_ selected + // profile (i.e. via the Profile Manager or -P option) rather than the + // current profile. These will differ if the current process was run + // without explicitly selecting a profile. + + let dir = services.directory.get("ProfD", Ci.nsIFile); + for (let prof in iter(services.profile.profiles)) + if (prof.QueryInterface(Ci.nsIToolkitProfile).rootDir.path === dir.path) + return prof.name; + return "unknown"; + }), + /** @property {string} The Dactyl version string. */ version: Class.Memoize(function () { if (this.VCSPath) @@ -375,7 +391,6 @@ var ConfigBase = Class("ConfigBase", { helpStyles: /^(Help|StatusLine|REPL)|^(Boolean|Dense|Indicator|MoreMsg|Number|Object|Logo|Key(word)?|String)$/, styleHelp: function styleHelp() { if (!this.helpStyled) { - const { highlight } = require("highlight"); for (let k in keys(highlight.loaded)) if (this.helpStyles.test(k)) highlight.loaded[k] = true; @@ -1064,7 +1079,6 @@ config.INIT = update(Object.create(config.INIT), config.INIT, { let img = window.Image(); img.src = this.logo || "resource://dactyl-local-content/logo.png"; img.onload = util.wrapCallback(function () { - const { highlight } = require("highlight"); highlight.loadCSS(<>{"!Logo {"} display: inline-block; background: url({img.src}); diff --git a/common/modules/dom.jsm b/common/modules/dom.jsm index 5a7a9a29..df05d094 100644 --- a/common/modules/dom.jsm +++ b/common/modules/dom.jsm @@ -1324,7 +1324,7 @@ var DOM = Class("DOM", { yield elem; if (matcher.css) - for (let [, elem] in iter(node.querySelectorAll(matcher.css))) + for (let [, elem] in iter(util.withProperErrors("querySelectorAll", node, matcher.css))) yield elem; }, { css: css.join(", "), @@ -1343,13 +1343,15 @@ var DOM = Class("DOM", { validateMatcher: function validateMatcher(list) { let evaluator = services.XPathEvaluator(); let node = services.XMLDocument(); - return this.testValues(list, function (value) { - if (/^xpath:/.test(value)) - evaluator.createExpression(value.substr(6), DOM.XPath.resolver); - else - node.querySelector(value); - return true; - }); + return this.testValues(list, this.closure.testMatcher); + }, + + testMatcher: function testMatcher(value) { + if (/^xpath:/.test(value)) + evaluator.createExpression(value.substr(6), DOM.XPath.resolver); + else + node.querySelector(value); + return true; }, /** diff --git a/common/modules/finder.jsm b/common/modules/finder.jsm index 19317896..14e5ebe7 100644 --- a/common/modules/finder.jsm +++ b/common/modules/finder.jsm @@ -12,8 +12,6 @@ defineModule("finder", { function equals(a, b) XPCNativeWrapper(a) == XPCNativeWrapper(b); -try { - /** @instance rangefinder */ var RangeFinder = Module("rangefinder", { Local: function (dactyl, modules, window) ({ @@ -265,7 +263,6 @@ var RangeFinder = Module("rangefinder", { }, options: function (dactyl, modules, window) { const { options, rangefinder } = modules; - const { prefs } = require("prefs"); options.add(["hlfind", "hlf"], "Highlight all /find pattern matches on the current page after submission", @@ -844,9 +841,8 @@ var RangeFind = Class("RangeFind", { } }); -} catch(e){ if (typeof e === "string") e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); } +// catch(e){ if (typeof e === "string") e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); } endModule(); - // vim: set fdm=marker sw=4 ts=4 et ft=javascript: diff --git a/common/modules/storage.jsm b/common/modules/storage.jsm index b1d09ab4..6bfa0ccd 100644 --- a/common/modules/storage.jsm +++ b/common/modules/storage.jsm @@ -7,9 +7,11 @@ Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("storage", { exports: ["File", "Storage", "storage"], - require: ["services", "util"] + require: ["config", "services", "util"] }, this); +this.lazyRequire("io", ["IO"]); + var win32 = /^win(32|nt)$/i.test(services.runtime.OS); var myObject = JSON.parse("{}").constructor; @@ -169,6 +171,10 @@ var Storage = Module("Storage", { this.observers = {}; }, + infoPath: Class.Memoize(function () + File(IO.runtimePath.replace(/,.*/, "")) + .child("info").child(config.profileName)), + exists: function exists(name) this.infoPath.child(name).exists(), newObject: function newObject(key, constructor, params) { @@ -270,12 +276,6 @@ var Storage = Module("Storage", { skipXpcom: function skipXpcom(key, val) val instanceof Ci.nsISupports ? null : val } }, { - init: function init(dactyl, modules) { - init.superapply(this, arguments); - storage.infoPath = File(modules.IO.runtimePath.replace(/,.*/, "")) - .child("info").child(dactyl.profileName); - }, - cleanup: function (dactyl, modules, window) { overlay.setData(window, "storage-refs", null); this.removeDeadObservers(); diff --git a/pentadactyl/NEWS b/pentadactyl/NEWS index ad3a2521..3f6ecbe9 100644 --- a/pentadactyl/NEWS +++ b/pentadactyl/NEWS @@ -80,6 +80,7 @@ - It's now possible to map keys in many more modes, including Hint, Multi-line Output, and Menu. [b4] - and now behave more like Vim. [b8] + - n_G now uses 'linenumbers' to determine destination if possible. [b8] - Add n_s and n_S. [b8] - Added Operator mode for motion maps, per Vim. [b8] - Added site-specific mapping groups and related command @@ -211,6 +212,7 @@ - 'complete' now defaults to "slf" but file completion only triggers when the URL begins as above. [b1] - Added 'jumptags' option. [b7] + - Added 'linenumbers' option. [b8] - Added 's' flag to 'pageinfo' and changed default value. [b7] - Added 'passkeys' option. [b3] - Added 'passunknown' option. [b7]