mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-20 06:07:59 +01:00
Add :list* commands, linkify help tags in certain output, and augment :yank to accept JavaScript directly.
This commit is contained in:
@@ -111,6 +111,7 @@ Dactyl.prototype = {
|
||||
for (let [k, v] in Iterator(obj[prop] || {}))
|
||||
this[prop][k] = v;
|
||||
}
|
||||
this.initialized = true;
|
||||
},
|
||||
|
||||
scheme: "dactyl",
|
||||
|
||||
@@ -940,14 +940,18 @@ const CommandLine = Module("commandline", {
|
||||
dactyl.open(event.target.href, where);
|
||||
}
|
||||
|
||||
let command = event.originalTarget.getAttributeNS(NS.uri, "command");
|
||||
if (command && dactyl.commands[command]) {
|
||||
return dactyl.withSavedValues(["forceNewTab"], function () {
|
||||
dactyl.forceNewTab = event.ctrlKey || event.shiftKey || event.button == 1;
|
||||
dactyl.commands[command](event);
|
||||
});
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case "<LeftMouse>":
|
||||
event.preventDefault();
|
||||
let command = event.originalTarget.getAttributeNS(NS.uri, "command");
|
||||
if (command && dactyl.commands[command])
|
||||
return dactyl.commands[command](event);
|
||||
else
|
||||
openLink(dactyl.CURRENT_TAB);
|
||||
openLink(dactyl.CURRENT_TAB);
|
||||
return false;
|
||||
case "<MiddleMouse>":
|
||||
case "<C-LeftMouse>":
|
||||
@@ -1708,7 +1712,7 @@ const CommandLine = Module("commandline", {
|
||||
{ validator: function (value) value >= 1 });
|
||||
|
||||
options.add(["messages", "msgs"],
|
||||
"Number of messages to store in the :message history",
|
||||
"Number of messages to store in the :messages history",
|
||||
"number", 100,
|
||||
{ validator: function (value) value >= 0 });
|
||||
|
||||
|
||||
@@ -1395,6 +1395,15 @@ const Commands = Module("commands", {
|
||||
completer: function (context) completion.userCommand(context)
|
||||
});
|
||||
|
||||
dactyl.addUsageCommand({
|
||||
name: ["listc[ommands]", "lc"],
|
||||
description: "List all Ex commands along with their short descriptions",
|
||||
iterate: function (args) commands,
|
||||
format: {
|
||||
description: function (cmd) template.linkifyHelp(cmd.description + (cmd.replacementText ? ": " + cmd.action : ""))
|
||||
}
|
||||
});
|
||||
|
||||
function checkStack(cmd) {
|
||||
util.assert(io.sourcing && io.sourcing.stack &&
|
||||
io.sourcing.stack[cmd] && io.sourcing.stack[cmd].length,
|
||||
@@ -1454,13 +1463,14 @@ const Commands = Module("commands", {
|
||||
commands.add(["y[ank]"],
|
||||
"Yanks the output of the given command to the clipboard",
|
||||
function (args) {
|
||||
let res = commandline.withOutputToString(commands.execute, commands, args[0]);
|
||||
let cmd = /^:/.test(args[0]) ? args[0] : ":echo " + args[0];
|
||||
let res = commandline.withOutputToString(commands.execute, commands, cmd);
|
||||
dactyl.clipboardWrite(res);
|
||||
let lines = res.split("\n").length;
|
||||
dactyl.echomsg("Yanked " + lines + " line" + (lines == 1 ? "" : "s"));
|
||||
},
|
||||
{
|
||||
completer: function (context) completion.ex(context),
|
||||
completer: function (context) completion[/^:/.test(context.filter) ? "ex" : "javascript"](context),
|
||||
literal: 0
|
||||
});
|
||||
},
|
||||
|
||||
@@ -48,6 +48,10 @@ const Dactyl = Module("dactyl", {
|
||||
this.commands = {};
|
||||
this.modules = modules;
|
||||
this.observers = {};
|
||||
|
||||
this.commands["dactyl.help"] = function (event) {
|
||||
dactyl.help(event.originalTarget.textContent);
|
||||
};
|
||||
},
|
||||
|
||||
/** @property {string} The name of the current user profile. */
|
||||
@@ -139,6 +143,35 @@ const Dactyl = Module("dactyl", {
|
||||
});
|
||||
},
|
||||
|
||||
addUsageCommand: function (params) {
|
||||
commands.add(params.name, params.description,
|
||||
function (args) {
|
||||
let results = array(params.iterate(args))
|
||||
.sort(function (a, b) String.localeCompare(a.name, b.name));
|
||||
if (args.length)
|
||||
results = results.filter(function (item) args.map(String.toLowerCase)
|
||||
.every(function (arg) (item.name + item.description).toLowerCase().indexOf(arg) >= 0));
|
||||
commandline.commandOutput(
|
||||
template.usage(results, params.format));
|
||||
},
|
||||
{
|
||||
argCount: "*",
|
||||
completer: function (context, args) {
|
||||
context.keys.text = util.identity;
|
||||
context.keys.description = function () seen[this.text] + " matching items";
|
||||
let seen = {};
|
||||
context.completions = array(item.description.toLowerCase().split(/[()\s]+/)
|
||||
for (item in params.iterate(args)))
|
||||
.flatten().filter(function (w) /^\w[\w-_']+$/.test(w))
|
||||
.map(function (k) {
|
||||
seen[k] = (seen[k] || 0) + 1;
|
||||
return k;
|
||||
}).uniq()
|
||||
},
|
||||
options: params.options || []
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggers the application bell to notify the user of an error. The
|
||||
* bell may be either audible or visual depending on the value of the
|
||||
@@ -257,8 +290,7 @@ const Dactyl = Module("dactyl", {
|
||||
if (typeof str == "object" && "echoerr" in str)
|
||||
str = str.echoerr;
|
||||
else if (isinstance(str, ["Error"]))
|
||||
str = str.fileName.replace(/^.*? -> /, "")
|
||||
+ ":" + str.lineNumber + ": " + str;
|
||||
str = <>{str.fileName.replace(/^.*? -> /, "")}: {str.lineNumber}: {str}</>;
|
||||
|
||||
if (options["errorbells"])
|
||||
dactyl.beep();
|
||||
@@ -1044,7 +1076,7 @@ const Dactyl = Module("dactyl", {
|
||||
reportError: function reportError(error, echo) {
|
||||
if (error instanceof FailedAssertion || error.message === "Interrupted") {
|
||||
if (error.message)
|
||||
dactyl.echoerr(error.message);
|
||||
dactyl.echoerr(template.linkifyHelp(error.message));
|
||||
else
|
||||
dactyl.beep();
|
||||
return;
|
||||
@@ -1935,7 +1967,6 @@ const Dactyl = Module("dactyl", {
|
||||
argCount: "0",
|
||||
bang: true
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
completion: function () {
|
||||
|
||||
@@ -185,16 +185,17 @@ const Mappings = Module("mappings", {
|
||||
some(function (m) m.rhs && m.rhs === map.rhs && m.name === map.name))))
|
||||
},
|
||||
|
||||
// NOTE: just normal mode for now
|
||||
/** @property {Iterator(Map)} @private */
|
||||
__iterator__: function () {
|
||||
let mode = modes.NORMAL;
|
||||
iterate: function (mode) {
|
||||
let seen = {};
|
||||
for (let map in iterAll(values(this._user[mode]), values(this._main[mode])))
|
||||
if (!set.add(seen, map.name))
|
||||
yield map;
|
||||
},
|
||||
|
||||
// NOTE: just normal mode for now
|
||||
/** @property {Iterator(Map)} */
|
||||
__iterator__: function () this.iterate(modes.NORMAL),
|
||||
|
||||
// used by :mkpentadactylrc to save mappings
|
||||
/**
|
||||
* Returns a user-defined mappings iterator for the specified *mode*.
|
||||
@@ -381,19 +382,6 @@ const Mappings = Module("mappings", {
|
||||
}
|
||||
}
|
||||
|
||||
function findMode(name) {
|
||||
for (let mode in modes.mainModes)
|
||||
if (name == mode || name == mode.char || String.toLowerCase(name).replace(/-/g, "_") == mode.name.toLowerCase())
|
||||
return mode.mask;
|
||||
return null;
|
||||
}
|
||||
function uniqueModes(modes) {
|
||||
modes = modes.map(modules.modes.closure.getMode);
|
||||
let chars = [k for ([k, v] in Iterator(modules.modes.modeChars))
|
||||
if (v.every(function (mode) modes.indexOf(mode) >= 0))];
|
||||
return array.uniq(modes.filter(function (m) chars.indexOf(m.char) < 0).concat(chars));
|
||||
}
|
||||
|
||||
const opts = {
|
||||
completer: function (context, args) {
|
||||
if (args.length == 1)
|
||||
@@ -511,8 +499,66 @@ const Mappings = Module("mappings", {
|
||||
});
|
||||
}
|
||||
|
||||
function findMode(name) {
|
||||
for (let mode in modes.mainModes)
|
||||
if (name == mode || name == mode.char || String.toLowerCase(name).replace(/-/g, "_") == mode.name.toLowerCase())
|
||||
return mode.mask;
|
||||
return null;
|
||||
}
|
||||
function uniqueModes(modes) {
|
||||
modes = modes.map(modules.modes.closure.getMode);
|
||||
let chars = [k for ([k, v] in Iterator(modules.modes.modeChars))
|
||||
if (v.every(function (mode) modes.indexOf(mode) >= 0))];
|
||||
return array.uniq(modes.filter(function (m) chars.indexOf(m.char) < 0).concat(chars));
|
||||
}
|
||||
|
||||
addMapCommands("", [modes.NORMAL, modes.VISUAL], "");
|
||||
|
||||
let args = {
|
||||
getMode: function (args) findMode(args["-mode"]),
|
||||
iterate: function (args) {
|
||||
for (let map in mappings.iterate(this.getMode(args)))
|
||||
for (let name in values(map.names))
|
||||
yield { name: name, __proto__: map };
|
||||
},
|
||||
format: {
|
||||
description: function (map) (XML.ignoreWhitespace = false, XML.prettyPrinting = false, <>
|
||||
{options.get("passkeys").has(map.name)
|
||||
? <span highlight="URLExtra">(passed by {template.helpLink("'passkeys'")})</span>
|
||||
: <></>}
|
||||
{template.linkifyHelp(map.description + (map.rhs ? ": " + map.rhs : ""))}
|
||||
</>)
|
||||
}
|
||||
}
|
||||
|
||||
dactyl.addUsageCommand({
|
||||
__proto__: args,
|
||||
name: ["listk[eys]", "lk"],
|
||||
description: "List all mappings along with their short descriptions",
|
||||
options: [
|
||||
{
|
||||
names: ["-mode", "-m"],
|
||||
type: CommandOption.STRING,
|
||||
description: "The mode for which to list mappings",
|
||||
default: "n",
|
||||
completer: function () [[array.compact([mode.name.toLowerCase().replace(/_/g, "-"), mode.char]), mode.disp]
|
||||
for (mode in modes.mainModes)],
|
||||
validator: function (m) findMode(m)
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
forEach(modes.mainModes, function (mode) {
|
||||
if (mode.char && !commands.get(mode.char + "listkeys", true))
|
||||
dactyl.addUsageCommand({
|
||||
__proto__: args,
|
||||
name: [mode.char + "listk[eys]", mode.char + "lk"],
|
||||
description: "List all " + mode.name + " mode mappings along with their short descriptions",
|
||||
getMode: function (args) mode,
|
||||
options: []
|
||||
});
|
||||
});
|
||||
|
||||
for (let mode in modes.mainModes)
|
||||
if (mode.char && !commands.get(mode.char + "map", true))
|
||||
addMapCommands(mode.char,
|
||||
|
||||
@@ -817,6 +817,36 @@ const Options = Module("options", {
|
||||
}, {
|
||||
}, {
|
||||
commands: function () {
|
||||
let args = {
|
||||
getMode: function (args) findMode(args["-mode"]),
|
||||
iterate: function (args) {
|
||||
for (let map in mappings.iterate(this.getMode(args)))
|
||||
for (let name in values(map.names))
|
||||
yield { name: name, __proto__: map };
|
||||
},
|
||||
format: {
|
||||
description: function (map) (XML.ignoreWhitespace = false, XML.prettyPrinting = false, <>
|
||||
{options.get("passkeys").has(map.name)
|
||||
? <span highlight="URLExtra">(passed by {template.helpLink("'passkeys'")})</span>
|
||||
: <></>}
|
||||
{template.linkifyHelp(map.description)}
|
||||
</>)
|
||||
}
|
||||
}
|
||||
|
||||
dactyl.addUsageCommand({
|
||||
name: ["listo[ptions]", "lo"],
|
||||
description: "List all options along with their short descriptions",
|
||||
iterate: function (args) options,
|
||||
format: {
|
||||
description: function (opt) (XML.ignoreWhitespace = false, XML.prettyPrinting = false, <>
|
||||
{opt.scope == Option.SCOPE_LOCAL
|
||||
? <span highlight="URLExtra">(buffer local)</span> : ""}
|
||||
{template.linkifyHelp(opt.description)}
|
||||
</>)
|
||||
}
|
||||
});
|
||||
|
||||
function setAction(args, modifiers) {
|
||||
let bang = args.bang;
|
||||
if (!args.length)
|
||||
|
||||
@@ -113,9 +113,12 @@
|
||||
|
||||
<item>
|
||||
<tags>:yank :y</tags>
|
||||
<spec>:y[ank] <a>cmd</a></spec>
|
||||
<spec>:y[ank] :<a>cmd</a></spec>
|
||||
<spec>:y[ank] <a>js</a></spec>
|
||||
<description>
|
||||
<p>Yanks the output of the given command to the clipboard.</p>
|
||||
<p>
|
||||
Yanks the output of the given Ex command <a>cmd</a> or JavaScript <a>js</a> to the clipboard.
|
||||
</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ const Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference
|
||||
if (saved == null && curval != defval || curval != saved) {
|
||||
let msg = "Warning: setting preference " + name + ", but it's changed from its default value.";
|
||||
if (message)
|
||||
msg += " " + message;
|
||||
msg = template.linkifyHelp(msg + " " + message);
|
||||
util.dactyl.echomsg(msg);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
Components.utils.import("resource://dactyl/base.jsm");
|
||||
defineModule("template", {
|
||||
exports: ["Template", "template"],
|
||||
require: ["util"]
|
||||
require: ["util"],
|
||||
use: ["services"]
|
||||
});
|
||||
|
||||
default xml namespace = XHTML;
|
||||
@@ -78,6 +79,16 @@ const Template = Module("Template", {
|
||||
// </e4x>
|
||||
},
|
||||
|
||||
helpLink: function (topic, type) {
|
||||
if (services["dactyl:"].initialized && !set.has(services["dactyl:"].HELP_TAGS, topic))
|
||||
return <>{topic}</>;
|
||||
|
||||
XML.ignoreWhitespace = false; XML.prettyPrinting = false;
|
||||
type = type || /^'.*'$/.test(topic) ? "HelpOpt" :
|
||||
/^:\w/.test(topic) ? "HelpEx" : "HelpKey";
|
||||
return <a highlight={type} href={"dactyl://help-tag/" + topic} dactyl:command="dactyl.help" xmlns:dactyl={NS}>{topic}</a>
|
||||
},
|
||||
|
||||
// if "processStrings" is true, any passed strings will be surrounded by " and
|
||||
// any line breaks are displayed as \n
|
||||
highlight: function highlight(arg, processStrings, clip) {
|
||||
@@ -199,6 +210,21 @@ const Template = Module("Template", {
|
||||
// </e4x>
|
||||
},
|
||||
|
||||
linkifyHelp: function linkifyHelp(str) {
|
||||
util.dactyl.initHelp();
|
||||
|
||||
let re = util.regexp(<![CDATA[
|
||||
([/\s]|^)
|
||||
( '[\w-]+' | :(?:[\w-]+|!) | (?:._)?<[\w-]+> )
|
||||
(?=[[!,;./\s]|$)
|
||||
]]>, "g");
|
||||
return this.highlightSubstrings(str, (function () {
|
||||
let res;
|
||||
while ((res = re.exec(str)) && res[2].length)
|
||||
yield [res.index + res[1].length, res[2].length];
|
||||
})(), template.helpLink);
|
||||
},
|
||||
|
||||
options: function options(title, opts) {
|
||||
XML.ignoreWhitespace = false; XML.prettyPrinting = false;
|
||||
// <e4x>
|
||||
@@ -285,8 +311,9 @@ const Template = Module("Template", {
|
||||
// </e4x>
|
||||
},
|
||||
|
||||
usage: function usage(iter) {
|
||||
usage: function usage(iter, format) {
|
||||
XML.ignoreWhitespace = false; XML.prettyPrinting = false;
|
||||
let desc = format && format.description || function (item) template.linkifyHelp(item.description);
|
||||
// <e4x>
|
||||
return <table>
|
||||
{
|
||||
@@ -298,7 +325,7 @@ const Template = Module("Template", {
|
||||
<span highlight="Title">{name}</span> + <> </> +
|
||||
<span highlight="LineInfo">Defined at {template.sourceLink(frame)}</span>
|
||||
}</td>
|
||||
<td>{item.description}</td>
|
||||
<td>{desc(item)}</td>
|
||||
</tr>)
|
||||
}
|
||||
</table>;
|
||||
|
||||
@@ -30,7 +30,7 @@ memoize(this, "Commands", function () {
|
||||
|
||||
const FailedAssertion = Class("FailedAssertion", Error, {
|
||||
init: function (message) {
|
||||
this.message = message;
|
||||
update(this, Error(message))
|
||||
}
|
||||
});
|
||||
|
||||
@@ -102,7 +102,7 @@ const Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference])
|
||||
*/
|
||||
assert: function (condition, message) {
|
||||
if (!condition)
|
||||
throw new FailedAssertion(message);
|
||||
throw FailedAssertion(message);
|
||||
},
|
||||
|
||||
get chromePackages() {
|
||||
|
||||
@@ -76,6 +76,14 @@
|
||||
|
||||
}
|
||||
|
||||
@-moz-document
|
||||
url-prefix(chrome://dactyl/) {
|
||||
|
||||
*:-moz-any-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
/* Applied to completion buffer, MOW, browser window */
|
||||
@-moz-document
|
||||
url-prefix(chrome://) {
|
||||
|
||||
Reference in New Issue
Block a user