1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-20 08:27:59 +01:00

Add :list* commands, linkify help tags in certain output, and augment :yank to accept JavaScript directly.

This commit is contained in:
Kris Maglione
2010-12-18 11:54:31 -05:00
parent 3f343d0d98
commit f1e4ef93df
11 changed files with 197 additions and 37 deletions

View File

@@ -111,6 +111,7 @@ Dactyl.prototype = {
for (let [k, v] in Iterator(obj[prop] || {})) for (let [k, v] in Iterator(obj[prop] || {}))
this[prop][k] = v; this[prop][k] = v;
} }
this.initialized = true;
}, },
scheme: "dactyl", scheme: "dactyl",

View File

@@ -940,14 +940,18 @@ const CommandLine = Module("commandline", {
dactyl.open(event.target.href, where); 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) { switch (key) {
case "<LeftMouse>": case "<LeftMouse>":
event.preventDefault(); event.preventDefault();
let command = event.originalTarget.getAttributeNS(NS.uri, "command"); openLink(dactyl.CURRENT_TAB);
if (command && dactyl.commands[command])
return dactyl.commands[command](event);
else
openLink(dactyl.CURRENT_TAB);
return false; return false;
case "<MiddleMouse>": case "<MiddleMouse>":
case "<C-LeftMouse>": case "<C-LeftMouse>":
@@ -1708,7 +1712,7 @@ const CommandLine = Module("commandline", {
{ validator: function (value) value >= 1 }); { validator: function (value) value >= 1 });
options.add(["messages", "msgs"], options.add(["messages", "msgs"],
"Number of messages to store in the :message history", "Number of messages to store in the :messages history",
"number", 100, "number", 100,
{ validator: function (value) value >= 0 }); { validator: function (value) value >= 0 });

View File

@@ -1395,6 +1395,15 @@ const Commands = Module("commands", {
completer: function (context) completion.userCommand(context) 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) { function checkStack(cmd) {
util.assert(io.sourcing && io.sourcing.stack && util.assert(io.sourcing && io.sourcing.stack &&
io.sourcing.stack[cmd] && io.sourcing.stack[cmd].length, io.sourcing.stack[cmd] && io.sourcing.stack[cmd].length,
@@ -1454,13 +1463,14 @@ const Commands = Module("commands", {
commands.add(["y[ank]"], commands.add(["y[ank]"],
"Yanks the output of the given command to the clipboard", "Yanks the output of the given command to the clipboard",
function (args) { 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); dactyl.clipboardWrite(res);
let lines = res.split("\n").length; let lines = res.split("\n").length;
dactyl.echomsg("Yanked " + lines + " line" + (lines == 1 ? "" : "s")); 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 literal: 0
}); });
}, },

View File

@@ -48,6 +48,10 @@ const Dactyl = Module("dactyl", {
this.commands = {}; this.commands = {};
this.modules = modules; this.modules = modules;
this.observers = {}; this.observers = {};
this.commands["dactyl.help"] = function (event) {
dactyl.help(event.originalTarget.textContent);
};
}, },
/** @property {string} The name of the current user profile. */ /** @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 * 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 * 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) if (typeof str == "object" && "echoerr" in str)
str = str.echoerr; str = str.echoerr;
else if (isinstance(str, ["Error"])) else if (isinstance(str, ["Error"]))
str = str.fileName.replace(/^.*? -> /, "") str = <>{str.fileName.replace(/^.*? -> /, "")}: {str.lineNumber}: {str}</>;
+ ":" + str.lineNumber + ": " + str;
if (options["errorbells"]) if (options["errorbells"])
dactyl.beep(); dactyl.beep();
@@ -1044,7 +1076,7 @@ const Dactyl = Module("dactyl", {
reportError: function reportError(error, echo) { reportError: function reportError(error, echo) {
if (error instanceof FailedAssertion || error.message === "Interrupted") { if (error instanceof FailedAssertion || error.message === "Interrupted") {
if (error.message) if (error.message)
dactyl.echoerr(error.message); dactyl.echoerr(template.linkifyHelp(error.message));
else else
dactyl.beep(); dactyl.beep();
return; return;
@@ -1935,7 +1967,6 @@ const Dactyl = Module("dactyl", {
argCount: "0", argCount: "0",
bang: true bang: true
}); });
}, },
completion: function () { completion: function () {

View File

@@ -185,16 +185,17 @@ const Mappings = Module("mappings", {
some(function (m) m.rhs && m.rhs === map.rhs && m.name === map.name)))) some(function (m) m.rhs && m.rhs === map.rhs && m.name === map.name))))
}, },
// NOTE: just normal mode for now iterate: function (mode) {
/** @property {Iterator(Map)} @private */
__iterator__: function () {
let mode = modes.NORMAL;
let seen = {}; let seen = {};
for (let map in iterAll(values(this._user[mode]), values(this._main[mode]))) for (let map in iterAll(values(this._user[mode]), values(this._main[mode])))
if (!set.add(seen, map.name)) if (!set.add(seen, map.name))
yield map; yield map;
}, },
// NOTE: just normal mode for now
/** @property {Iterator(Map)} */
__iterator__: function () this.iterate(modes.NORMAL),
// used by :mkpentadactylrc to save mappings // used by :mkpentadactylrc to save mappings
/** /**
* Returns a user-defined mappings iterator for the specified *mode*. * 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 = { const opts = {
completer: function (context, args) { completer: function (context, args) {
if (args.length == 1) 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], ""); 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) for (let mode in modes.mainModes)
if (mode.char && !commands.get(mode.char + "map", true)) if (mode.char && !commands.get(mode.char + "map", true))
addMapCommands(mode.char, addMapCommands(mode.char,

View File

@@ -817,6 +817,36 @@ const Options = Module("options", {
}, { }, {
}, { }, {
commands: function () { 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) { function setAction(args, modifiers) {
let bang = args.bang; let bang = args.bang;
if (!args.length) if (!args.length)

View File

@@ -113,9 +113,12 @@
<item> <item>
<tags>:yank :y</tags> <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> <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> </description>
</item> </item>

View File

@@ -131,7 +131,7 @@ const Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference
if (saved == null && curval != defval || curval != saved) { if (saved == null && curval != defval || curval != saved) {
let msg = "Warning: setting preference " + name + ", but it's changed from its default value."; let msg = "Warning: setting preference " + name + ", but it's changed from its default value.";
if (message) if (message)
msg += " " + message; msg = template.linkifyHelp(msg + " " + message);
util.dactyl.echomsg(msg); util.dactyl.echomsg(msg);
} }
}, },

View File

@@ -7,7 +7,8 @@
Components.utils.import("resource://dactyl/base.jsm"); Components.utils.import("resource://dactyl/base.jsm");
defineModule("template", { defineModule("template", {
exports: ["Template", "template"], exports: ["Template", "template"],
require: ["util"] require: ["util"],
use: ["services"]
}); });
default xml namespace = XHTML; default xml namespace = XHTML;
@@ -78,6 +79,16 @@ const Template = Module("Template", {
// </e4x> // </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 // if "processStrings" is true, any passed strings will be surrounded by " and
// any line breaks are displayed as \n // any line breaks are displayed as \n
highlight: function highlight(arg, processStrings, clip) { highlight: function highlight(arg, processStrings, clip) {
@@ -199,6 +210,21 @@ const Template = Module("Template", {
// </e4x> // </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) { options: function options(title, opts) {
XML.ignoreWhitespace = false; XML.prettyPrinting = false; XML.ignoreWhitespace = false; XML.prettyPrinting = false;
// <e4x> // <e4x>
@@ -285,8 +311,9 @@ const Template = Module("Template", {
// </e4x> // </e4x>
}, },
usage: function usage(iter) { usage: function usage(iter, format) {
XML.ignoreWhitespace = false; XML.prettyPrinting = false; XML.ignoreWhitespace = false; XML.prettyPrinting = false;
let desc = format && format.description || function (item) template.linkifyHelp(item.description);
// <e4x> // <e4x>
return <table> return <table>
{ {
@@ -298,7 +325,7 @@ const Template = Module("Template", {
<span highlight="Title">{name}</span> + <> </> + <span highlight="Title">{name}</span> + <> </> +
<span highlight="LineInfo">Defined at {template.sourceLink(frame)}</span> <span highlight="LineInfo">Defined at {template.sourceLink(frame)}</span>
}</td> }</td>
<td>{item.description}</td> <td>{desc(item)}</td>
</tr>) </tr>)
} }
</table>; </table>;

View File

@@ -30,7 +30,7 @@ memoize(this, "Commands", function () {
const FailedAssertion = Class("FailedAssertion", Error, { const FailedAssertion = Class("FailedAssertion", Error, {
init: function (message) { 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) { assert: function (condition, message) {
if (!condition) if (!condition)
throw new FailedAssertion(message); throw FailedAssertion(message);
}, },
get chromePackages() { get chromePackages() {

View File

@@ -76,6 +76,14 @@
} }
@-moz-document
url-prefix(chrome://dactyl/) {
*:-moz-any-link:hover {
text-decoration: underline;
}
}
/* Applied to completion buffer, MOW, browser window */ /* Applied to completion buffer, MOW, browser window */
@-moz-document @-moz-document
url-prefix(chrome://) { url-prefix(chrome://) {