1
0
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:
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] || {}))
this[prop][k] = v;
}
this.initialized = true;
},
scheme: "dactyl",

View File

@@ -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 });

View File

@@ -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
});
},

View File

@@ -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 () {

View File

@@ -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,

View File

@@ -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)

View File

@@ -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>

View File

@@ -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);
}
},

View File

@@ -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>;

View File

@@ -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() {

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 */
@-moz-document
url-prefix(chrome://) {