mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-20 18:07:58 +01:00
Added -javascript option to :abbrev, thanks mostly to anekos.
--HG-- extra : rebase_source : 06036f7e9e2bb21fc77b1cb783c8f7e4a7e3f50d
This commit is contained in:
300
common/content/abbreviations.js
Normal file
300
common/content/abbreviations.js
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||||
|
// Copyright (c) 2010 by anekos <anekos@snca.net>
|
||||||
|
// Copyright (c) 2010 by Kris Maglion <maglione.k at Gmail>
|
||||||
|
//
|
||||||
|
// This work is licensed for reuse under an MIT license. Details are
|
||||||
|
// given in the License.txt file included with this file.
|
||||||
|
|
||||||
|
|
||||||
|
/** @scope modules */
|
||||||
|
|
||||||
|
const Abbreviation = Class("Abbreviation", {
|
||||||
|
init: function (modes, lhs, rhs) {
|
||||||
|
this.modes = modes.sort();
|
||||||
|
this.lhs = lhs;
|
||||||
|
this.rhs = rhs;
|
||||||
|
},
|
||||||
|
|
||||||
|
equals: function (other) {
|
||||||
|
return this.lhs == other.lhs && this.rhs == other.rhs;
|
||||||
|
},
|
||||||
|
|
||||||
|
expand: function (editor) String(callable(this.rhs) ? this.rhs(editor) : this.rhs),
|
||||||
|
|
||||||
|
modesEqual: function (modes) array.equals(this.modes, modes),
|
||||||
|
|
||||||
|
inMode: function (mode) this.modes.some(function (_mode) _mode == mode),
|
||||||
|
|
||||||
|
inModes: function (modes) modes.some(function (mode) this.inMode(mode), this),
|
||||||
|
|
||||||
|
removeMode: function (mode) {
|
||||||
|
this.modes = this.modes.filter(function (m) m != mode).sort();
|
||||||
|
},
|
||||||
|
|
||||||
|
get modeChar() Abbreviation.modeChar(this.modes)
|
||||||
|
}, {
|
||||||
|
modeChar: function (_modes) {
|
||||||
|
let result = "";
|
||||||
|
for ([, mode] in Iterator(_modes))
|
||||||
|
result += modes.getMode(mode).char;
|
||||||
|
if (/^(ic|ci)$/(result))
|
||||||
|
result = "!";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const Abbreviations = Module("abbreviations", {
|
||||||
|
init: function () {
|
||||||
|
this.abbrevs = {};
|
||||||
|
|
||||||
|
// (summarized from Vim's ":help abbreviations")
|
||||||
|
//
|
||||||
|
// There are three types of abbreviations.
|
||||||
|
//
|
||||||
|
// full-id: Consists entirely of keyword characters.
|
||||||
|
// ("foo", "g3", "-1")
|
||||||
|
//
|
||||||
|
// end-id: Ends in a keyword character, but all other
|
||||||
|
// are not keyword characters.
|
||||||
|
// ("#i", "..f", "$/7")
|
||||||
|
//
|
||||||
|
// non-id: Ends in a non-keyword character, but the
|
||||||
|
// others can be of any type other than space
|
||||||
|
// and tab.
|
||||||
|
// ("def#", "4/7$")
|
||||||
|
//
|
||||||
|
// Example strings that cannot be abbreviations:
|
||||||
|
// "a.b", "#def", "a b", "_$r"
|
||||||
|
//
|
||||||
|
// For now, a keyword character is anything except for \s, ", or '
|
||||||
|
// (i.e., whitespace and quotes). In Vim, a keyword character is
|
||||||
|
// specified by the 'iskeyword' setting and is a much less inclusive
|
||||||
|
// list.
|
||||||
|
//
|
||||||
|
// TODO: Make keyword definition closer to Vim's default keyword
|
||||||
|
// definition (which differs across platforms).
|
||||||
|
|
||||||
|
let nonkw = "\\s\"'";
|
||||||
|
let keyword = "[^" + nonkw + "]";
|
||||||
|
let nonkeyword = "[" + nonkw + "]";
|
||||||
|
|
||||||
|
let fullId = keyword + "+";
|
||||||
|
let endId = nonkeyword + "+" + keyword;
|
||||||
|
let nonId = "\\S*" + nonkeyword;
|
||||||
|
|
||||||
|
// Used in add and expand
|
||||||
|
this._match = fullId + "|" + endId + "|" + nonId;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new abbreviation.
|
||||||
|
*
|
||||||
|
* @param {Abbreviation}
|
||||||
|
*/
|
||||||
|
add: function (abbr) {
|
||||||
|
if (!(abbr instanceof Abbreviation))
|
||||||
|
abbr = Abbreviation.apply(null, arguments);
|
||||||
|
|
||||||
|
for (let [, mode] in Iterator(abbr.modes)) {
|
||||||
|
if (!this.abbrevs[mode])
|
||||||
|
this.abbrevs[mode] = {};
|
||||||
|
this.abbrevs[mode][abbr.lhs] = abbr;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns matched abbreviation.
|
||||||
|
*
|
||||||
|
* @param {mode}
|
||||||
|
* @param {string}
|
||||||
|
*/
|
||||||
|
get: function (mode, lhs) {
|
||||||
|
let abbrevs = this.abbrevs[mode];
|
||||||
|
return abbrevs && abbrevs[lhs];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the abbreviation that matches the given text.
|
||||||
|
*
|
||||||
|
* @returns {Abbreviation}
|
||||||
|
*/
|
||||||
|
match: function (mode, text) {
|
||||||
|
let match = text.match(RegExp('(' + abbreviations._match + ')$'));
|
||||||
|
if (match)
|
||||||
|
return abbreviations.get(mode, match[0]);
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of the abbreviations merged from each modes.
|
||||||
|
*/
|
||||||
|
get merged() {
|
||||||
|
let result = [];
|
||||||
|
let lhses = [];
|
||||||
|
let modes = [mode for (mode in this.abbrevs)];
|
||||||
|
|
||||||
|
for (let [, mabbrevs] in Iterator(this.abbrevs))
|
||||||
|
lhses = lhses.concat([key for (key in mabbrevs)]);
|
||||||
|
lhses.sort();
|
||||||
|
lhses = util.Array.uniq(lhses);
|
||||||
|
|
||||||
|
for (let [, lhs] in Iterator(lhses)) {
|
||||||
|
let exists = {};
|
||||||
|
for (let [, mabbrevs] in Iterator(this.abbrevs)) {
|
||||||
|
let abbr = mabbrevs[lhs];
|
||||||
|
if (abbr && !exists[abbr.rhs]) {
|
||||||
|
exists[abbr.rhs] = 1;
|
||||||
|
result.push(abbr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all abbreviations matching <b>modes</b> and <b>lhs</b>.
|
||||||
|
*
|
||||||
|
* @param {Array} list of mode.
|
||||||
|
* @param {string} lhs The LHS of the abbreviation.
|
||||||
|
*/
|
||||||
|
list: function (modes, lhs) {
|
||||||
|
let list = this.merged.filter(function (abbr) (abbr.inModes(modes) && abbr.lhs.indexOf(lhs) == 0));
|
||||||
|
|
||||||
|
if (!list.length)
|
||||||
|
dactyl.echomsg("No abbreviations found");
|
||||||
|
else if (list.length == 1) {
|
||||||
|
let head = list[0];
|
||||||
|
dactyl.echo(head.modeChar + " " + head.lhs + " " + head.rhs, commandline.FORCE_SINGLELINE); // 2 spaces, 3 spaces
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
list = list.map(function (abbr) [abbr.modeChar, abbr.lhs, abbr.rhs]);
|
||||||
|
list = template.tabular(["", "LHS", "RHS"], [], list);
|
||||||
|
commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified abbreviations.
|
||||||
|
*
|
||||||
|
* @param {Array} list of mode.
|
||||||
|
* @param {string} lhs of abbreviation.
|
||||||
|
* @returns {boolean} did the deleted abbreviation exist?
|
||||||
|
*/
|
||||||
|
remove: function (modes, lhs) {
|
||||||
|
let result = false;
|
||||||
|
for (let [, mode] in Iterator(modes)) {
|
||||||
|
if ((mode in this.abbrevs) && (lhs in this.abbrevs[mode])) {
|
||||||
|
result = true;
|
||||||
|
this.abbrevs[mode][lhs].removeMode(mode);
|
||||||
|
delete this.abbrevs[mode][lhs];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all abbreviations specified <b>modes<b>.
|
||||||
|
*
|
||||||
|
* @param {Array} list of mode.
|
||||||
|
*/
|
||||||
|
removeAll: function (modes) {
|
||||||
|
for (let [, mode] in modes) {
|
||||||
|
if (!(mode in this.abbrevs))
|
||||||
|
return;
|
||||||
|
for (let [, abbr] in this.abbrevs[mode])
|
||||||
|
abbr.removeMode(mode);
|
||||||
|
delete this.abbrevs[mode];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
}, {
|
||||||
|
completion: function () {
|
||||||
|
// TODO: shouldn't all of these have a standard signature (context, args, ...)? --djk
|
||||||
|
completion.abbreviation = function abbreviation(context, args, modes) {
|
||||||
|
if (args.completeArg == 0) {
|
||||||
|
let abbrevs = abbreviations.merged.filter(function (abbr) abbr.inModes(modes));
|
||||||
|
context.completions = [[abbr.lhs, abbr.rhs] for ([, abbr] in Iterator(abbrevs))];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
commands: function () {
|
||||||
|
function addAbbreviationCommands(modes, ch, modeDescription) {
|
||||||
|
modes.sort();
|
||||||
|
modeDescription = modeDescription ? " in " + modeDescription + " mode" : "";
|
||||||
|
|
||||||
|
// Why? --Kris
|
||||||
|
function splitAbbrev(abbrev) abbrev.match(RegExp("^(\\s*)($|" + abbreviations._match + ")(?:\\s*$|(\\s+)(.*))")) || [];
|
||||||
|
|
||||||
|
commands.add([ch ? ch + "a[bbrev]" : "ab[breviate]"],
|
||||||
|
"Abbreviate a key sequence" + modeDescription,
|
||||||
|
function (args) {
|
||||||
|
let [,, lhs,, rhs] = splitAbbrev(args[0]);
|
||||||
|
dactyl.assert(lhs, "E474: Invalid argument");
|
||||||
|
|
||||||
|
if (rhs) {
|
||||||
|
if (args["-javascript"]) {
|
||||||
|
let expr = rhs;
|
||||||
|
rhs = dactyl.userfunc("editor", expr);
|
||||||
|
rhs.toString = function () expr;
|
||||||
|
}
|
||||||
|
abbreviations.add(modes, lhs, rhs);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
abbreviations.list(modes, lhs || "");
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
names: ["-javascript", "-js", "-j"],
|
||||||
|
description: "Expand this abbreviation by evaluating its right-hand-side as JavaScript"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
completer: function (context, args) {
|
||||||
|
let [, sp1, lhs, sp2, rhs] = splitAbbrev(args[0]);
|
||||||
|
if (rhs == null)
|
||||||
|
return completion.abbreviation(context, args, modes)
|
||||||
|
context.advance((sp1 + lhs + sp2).length);
|
||||||
|
if (args["-javascript"])
|
||||||
|
return completion.javascript(context);
|
||||||
|
},
|
||||||
|
literal: 0,
|
||||||
|
serial: function () [ {
|
||||||
|
command: this.name,
|
||||||
|
arguments: [abbr.lhs],
|
||||||
|
literalArg: abbr.rhs,
|
||||||
|
options: callable(abbr.rhs) ? {"-javascript": null} : {}
|
||||||
|
}
|
||||||
|
for ([, abbr] in Iterator(abbreviations.merged))
|
||||||
|
if (abbr.modesEqual(modes))
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add([ch ? ch + "una[bbrev]" : "una[bbreviate]"],
|
||||||
|
"Remove an abbreviation" + modeDescription,
|
||||||
|
function (args) {
|
||||||
|
let lhs = args.literalArg;
|
||||||
|
if (!lhs)
|
||||||
|
return dactyl.echoerr("E474: Invalid argument");
|
||||||
|
if (!abbreviations.remove(modes, lhs))
|
||||||
|
return dactyl.echoerr("E24: No such abbreviation");
|
||||||
|
}, {
|
||||||
|
argCount: "1",
|
||||||
|
completer: function (context, args) completion.abbreviation(context, args, modes),
|
||||||
|
literal: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.add([ch + "abc[lear]"],
|
||||||
|
"Remove all abbreviations" + modeDescription,
|
||||||
|
function () { abbreviations.removeAll(modes); },
|
||||||
|
{ argCount: "0" });
|
||||||
|
}
|
||||||
|
|
||||||
|
addAbbreviationCommands([modes.INSERT, modes.COMMAND_LINE], "", "");
|
||||||
|
addAbbreviationCommands([modes.INSERT], "i", "insert");
|
||||||
|
addAbbreviationCommands([modes.COMMAND_LINE], "c", "command line");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
@@ -1487,13 +1487,13 @@ const CommandLine = Module("commandline", {
|
|||||||
["<Space>", '"', "'"], "Expand command line abbreviation",
|
["<Space>", '"', "'"], "Expand command line abbreviation",
|
||||||
function () {
|
function () {
|
||||||
commandline.resetCompletions();
|
commandline.resetCompletions();
|
||||||
return editor.expandAbbreviation("c");
|
return editor.expandAbbreviation(modes.COMMAND_LINE);
|
||||||
},
|
},
|
||||||
{ route: true });
|
{ route: true });
|
||||||
|
|
||||||
mappings.add(myModes,
|
mappings.add(myModes,
|
||||||
["<C-]>", "<C-5>"], "Expand command line abbreviation",
|
["<C-]>", "<C-5>"], "Expand command line abbreviation",
|
||||||
function () { editor.expandAbbreviation("c"); });
|
function () { editor.expandAbbreviation(modes.COMMAND_LINE); });
|
||||||
|
|
||||||
mappings.add([modes.NORMAL],
|
mappings.add([modes.NORMAL],
|
||||||
["g<"], "Redisplay the last command output",
|
["g<"], "Redisplay the last command output",
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
"modules",
|
"modules",
|
||||||
"storage",
|
"storage",
|
||||||
"util",
|
"util",
|
||||||
|
"abbreviations",
|
||||||
"autocommands",
|
"autocommands",
|
||||||
"buffer",
|
"buffer",
|
||||||
"commandline",
|
"commandline",
|
||||||
|
|||||||
@@ -16,56 +16,6 @@ const Editor = Module("editor", {
|
|||||||
//
|
//
|
||||||
this._lastFindChar = null;
|
this._lastFindChar = null;
|
||||||
this._lastFindCharFunc = null;
|
this._lastFindCharFunc = null;
|
||||||
// XXX: this strikes me as a rather odd ds; everyone's a critic --djk
|
|
||||||
this._abbreviations = {}; // this._abbreviations["lhr"][0]["{i,c,!}","rhs"]
|
|
||||||
|
|
||||||
// (summarized from Vim's ":help this._abbreviations")
|
|
||||||
//
|
|
||||||
// There are three types of this._abbreviations:
|
|
||||||
//
|
|
||||||
// full-id: Consists entirely of keyword characters.
|
|
||||||
// ("foo", "g3", "-1")
|
|
||||||
//
|
|
||||||
// end-id: Ends in a keyword character, but all other
|
|
||||||
// are not keyword characters.
|
|
||||||
// ("#i", "..f", "$/7")
|
|
||||||
//
|
|
||||||
// non-id: Ends in a non-keyword character, but the
|
|
||||||
// others can be of any type other than space
|
|
||||||
// and tab.
|
|
||||||
// ("def#", "4/7$")
|
|
||||||
//
|
|
||||||
// Example strings that cannot be this._abbreviations:
|
|
||||||
// "a.b", "#def", "a b", "_$r"
|
|
||||||
//
|
|
||||||
// For now, a keyword character is anything except for \s, ", or '
|
|
||||||
// (i.e., whitespace and quotes). In Vim, a keyword character is
|
|
||||||
// specified by the 'iskeyword' setting and is a much less inclusive
|
|
||||||
// list.
|
|
||||||
//
|
|
||||||
// TODO: Make keyword definition closer to Vim's default keyword
|
|
||||||
// definition (which differs across platforms).
|
|
||||||
//
|
|
||||||
|
|
||||||
let nonkw = "\\s\"'";
|
|
||||||
let keyword = "[^" + nonkw + "]";
|
|
||||||
let nonkeyword = "[" + nonkw + "]";
|
|
||||||
|
|
||||||
let fullId = keyword + "+";
|
|
||||||
let endId = nonkeyword + "+" + keyword;
|
|
||||||
let nonId = "\\S*" + nonkeyword;
|
|
||||||
|
|
||||||
// Used in addAbbrevation and expandAbbreviation
|
|
||||||
this._abbrevmatch = fullId + "|" + endId + "|" + nonId;
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
// For the record, some of this code I've just finished throwing
|
|
||||||
// away makes me want to pull someone else's hair out. --Kris
|
|
||||||
abbrevs: function () {
|
|
||||||
for (let [lhs, abbr] in Iterator(this._abbreviations))
|
|
||||||
for (let [, rhs] in Iterator(abbr))
|
|
||||||
yield [lhs, rhs];
|
|
||||||
},
|
},
|
||||||
|
|
||||||
line: function () {
|
line: function () {
|
||||||
@@ -416,225 +366,31 @@ const Editor = Module("editor", {
|
|||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Abbreviations {{{
|
|
||||||
|
|
||||||
// NOTE: I think this comment block is trying to say something but no
|
|
||||||
// one is listening. In space, no one can hear you scream. --djk
|
|
||||||
//
|
|
||||||
// System for adding this._abbreviations:
|
|
||||||
//
|
|
||||||
// filter == ! delete all, and set first (END)
|
|
||||||
//
|
|
||||||
// if filter == ! remove all and add it as only END
|
|
||||||
//
|
|
||||||
// variant 1: rhs matches anywhere in loop
|
|
||||||
//
|
|
||||||
// 1 mod matches anywhere in loop
|
|
||||||
// a) simple replace and
|
|
||||||
// I) (maybe there's another rhs that matches? not possible)
|
|
||||||
// (when there's another item, it's opposite mod with different rhs)
|
|
||||||
// (so do nothing further but END)
|
|
||||||
//
|
|
||||||
// 2 mod does not match
|
|
||||||
// a) the opposite is there -> make a ! and put it as only and END
|
|
||||||
// (b) a ! is there. do nothing END)
|
|
||||||
//
|
|
||||||
// variant 2: rhs matches *no*were in loop and filter is c or i
|
|
||||||
// every kind of current combo is possible to 1 {c,i,!} or two {c and i}
|
|
||||||
//
|
|
||||||
// 1 mod is ! split into two i + c END
|
|
||||||
// 1 not !: opposite mode (first), add/change 'second' and END
|
|
||||||
// 1 not !: same mode (first), overwrite first this END
|
|
||||||
//
|
|
||||||
// TODO: I don't like these funky filters, I am a funky filter hater. --djk
|
|
||||||
// : make this a separate object
|
|
||||||
// : use Struct for individual this._abbreviations
|
|
||||||
// : rename "filter" arg "mode"
|
|
||||||
/**
|
|
||||||
* Adds a new abbreviation. Abbreviations consist of a LHS (the text
|
|
||||||
* that is replaced when the abbreviation is expanded) and a RHS (the
|
|
||||||
* replacement text).
|
|
||||||
*
|
|
||||||
* @param {string} filter The mode filter. This specifies the modes in
|
|
||||||
* which this abbreviation is available. Either:
|
|
||||||
* "c" - applies in command-line mode
|
|
||||||
* "i" - applies in insert mode
|
|
||||||
* "!" - applies in both command-line and insert modes
|
|
||||||
* @param {string} lhs The LHS of the abbreviation.
|
|
||||||
* @param {string} rhs The RHS of the abbreviation.
|
|
||||||
*/
|
|
||||||
addAbbreviation: function (filter, lhs, rhs) {
|
|
||||||
if (!this._abbreviations[lhs]) {
|
|
||||||
this._abbreviations[lhs] = [];
|
|
||||||
this._abbreviations[lhs][0] = [filter, rhs];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter == "!") {
|
|
||||||
if (this._abbreviations[lhs][1])
|
|
||||||
this._abbreviations[lhs][1] = "";
|
|
||||||
this._abbreviations[lhs][0] = [filter, rhs];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < this._abbreviations[lhs].length; i++) {
|
|
||||||
if (this._abbreviations[lhs][i][1] == rhs) {
|
|
||||||
if (this._abbreviations[lhs][i][0] == filter) {
|
|
||||||
this._abbreviations[lhs][i] = [filter, rhs];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (this._abbreviations[lhs][i][0] != "!") {
|
|
||||||
if (this._abbreviations[lhs][1])
|
|
||||||
this._abbreviations[lhs][1] = "";
|
|
||||||
this._abbreviations[lhs][0] = ["!", rhs];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._abbreviations[lhs][0][0] == "!") {
|
|
||||||
let tmpOpp = ("i" == filter) ? "c" : "i";
|
|
||||||
this._abbreviations[lhs][1] = [tmpOpp, this._abbreviations[lhs][0][1]];
|
|
||||||
this._abbreviations[lhs][0] = [filter, rhs];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._abbreviations[lhs][0][0] != filter)
|
|
||||||
this._abbreviations[lhs][1] = [filter, rhs];
|
|
||||||
else
|
|
||||||
this._abbreviations[lhs][0] = [filter, rhs];
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expands an abbreviation in the currently active textbox.
|
* Expands an abbreviation in the currently active textbox.
|
||||||
*
|
*
|
||||||
* @param {string} filter The mode filter.
|
* @param {string} mode The mode filter.
|
||||||
* @see #addAbbreviation
|
* @see #addAbbreviation
|
||||||
*/
|
*/
|
||||||
expandAbbreviation: function (filter) {
|
expandAbbreviation: function (mode) {
|
||||||
let textbox = Editor.getEditor();
|
let textbox = Editor.getEditor();
|
||||||
if (!textbox)
|
if (!textbox)
|
||||||
return false;
|
return false;
|
||||||
let text = textbox.value;
|
let text = textbox.value;
|
||||||
let currStart = textbox.selectionStart;
|
let currStart = textbox.selectionStart;
|
||||||
let currEnd = textbox.selectionEnd;
|
let currEnd = textbox.selectionEnd;
|
||||||
let foundWord = text.substring(0, currStart).replace(RegExp("^(.|\\n)*?\\s*(" + this._abbrevmatch + ")$", "m"), "$2"); // get last word \b word boundary
|
let abbrev = abbreviations.match(mode, text.substring(0, currStart).replace(/.*\s/g, ""));
|
||||||
if (!foundWord)
|
if (abbrev) {
|
||||||
return true;
|
let len = abbrev.lhs.length;
|
||||||
|
let abbrText = abbrev.expand(textbox);
|
||||||
for (let lhs in this._abbreviations) {
|
text = text.substring(0, currStart - len) + abbrText + text.substring(currStart);
|
||||||
for (let i = 0; i < this._abbreviations[lhs].length; i++) {
|
textbox.value = text;
|
||||||
if (lhs == foundWord && (this._abbreviations[lhs][i][0] == filter || this._abbreviations[lhs][i][0] == "!")) {
|
textbox.selectionStart = currStart - len + abbrText.length;
|
||||||
// if found, replace accordingly
|
textbox.selectionEnd = currEnd - len + abbrText.length;
|
||||||
let len = foundWord.length;
|
|
||||||
let abbrText = this._abbreviations[lhs][i][1];
|
|
||||||
text = text.substring(0, currStart - len) + abbrText + text.substring(currStart);
|
|
||||||
textbox.value = text;
|
|
||||||
textbox.selectionStart = currStart - len + abbrText.length;
|
|
||||||
textbox.selectionEnd = currEnd - len + abbrText.length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all abbreviations matching <b>filter</b> and <b>lhs</b>.
|
|
||||||
*
|
|
||||||
* @param {string} filter The mode filter.
|
|
||||||
* @param {string} lhs The LHS of the abbreviation.
|
|
||||||
* @returns {Array} The matching abbreviations [mode, lhs, rhs]
|
|
||||||
* @see #addAbbreviation
|
|
||||||
*/
|
|
||||||
getAbbreviations: function (filter, lhs) {
|
|
||||||
// ! -> list all, on c or i ! matches too
|
|
||||||
let searchFilter = (filter == "!") ? "!ci" : filter + "!";
|
|
||||||
return [[mode, left, right] for ([left, [mode, right]] in this.abbrevs())
|
|
||||||
if (searchFilter.indexOf(mode) >= 0 && left.indexOf(lhs || "") == 0)];
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lists all abbreviations matching <b>filter</b> and <b>lhs</b>.
|
|
||||||
*
|
|
||||||
* @param {string} filter The mode filter.
|
|
||||||
* @param {string} lhs The LHS of the abbreviation.
|
|
||||||
* @see #addAbbreviation
|
|
||||||
*/
|
|
||||||
listAbbreviations: function (filter, lhs) {
|
|
||||||
let list = this.getAbbreviations(filter, lhs);
|
|
||||||
|
|
||||||
if (!list.length)
|
|
||||||
dactyl.echomsg("No abbreviations found");
|
|
||||||
else if (list.length == 1) {
|
|
||||||
let [mode, lhs, rhs] = list[0];
|
|
||||||
|
|
||||||
dactyl.echo(mode + " " + lhs + " " + rhs, commandline.FORCE_SINGLELINE); // 2 spaces, 3 spaces
|
|
||||||
}
|
|
||||||
else
|
|
||||||
commandline.commandOutput(template.tabular(["", "LHS", "RHS"], [], list));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes all abbreviations matching <b>filter</b> and <b>lhs</b>.
|
|
||||||
*
|
|
||||||
* @param {string} filter The mode filter.
|
|
||||||
* @param {string} lhs The LHS of the abbreviation.
|
|
||||||
* @see #addAbbreviation
|
|
||||||
*/
|
|
||||||
removeAbbreviation: function (filter, lhs) {
|
|
||||||
if (!lhs) {
|
|
||||||
dactyl.echoerr("E474: Invalid argument");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._abbreviations[lhs]) { // this._abbreviations exists
|
|
||||||
if (filter == "!") {
|
|
||||||
this._abbreviations[lhs] = "";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!this._abbreviations[lhs][1]) { // only one exists
|
|
||||||
if (this._abbreviations[lhs][0][0] == "!") { // exists as ! -> no 'full' delete
|
|
||||||
this._abbreviations[lhs][0][0] = (filter == "i") ? "c" : "i"; // ! - i = c; ! - c = i
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (this._abbreviations[lhs][0][0] == filter) {
|
|
||||||
this._abbreviations[lhs] = "";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // two this._abbreviations exist ( 'i' or 'c' (filter as well))
|
|
||||||
if (this._abbreviations[lhs][0][0] == "c" && filter == "c")
|
|
||||||
this._abbreviations[lhs][0] = this._abbreviations[lhs][1];
|
|
||||||
|
|
||||||
this._abbreviations[lhs][1] = "";
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dactyl.echoerr("E24: No such abbreviation");
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all abbreviations matching <b>filter</b>.
|
|
||||||
*
|
|
||||||
* @param {string} filter The mode filter.
|
|
||||||
* @see #addAbbreviation
|
|
||||||
*/
|
|
||||||
removeAllAbbreviations: function (filter) {
|
|
||||||
let searchFilter = (filter == "!") ? "!ci" : filter + "!";
|
|
||||||
for (let [lhs, [mode, rhs]] in this.abbrevs())
|
|
||||||
if (searchFilter.indexOf(mode) >= 0)
|
|
||||||
this.removeAbbreviation(filter, lhs);
|
|
||||||
}
|
|
||||||
}, {
|
}, {
|
||||||
getEditor: function () dactyl.focus,
|
getEditor: function () dactyl.focus,
|
||||||
|
|
||||||
@@ -646,67 +402,6 @@ const Editor = Module("editor", {
|
|||||||
return ed.controllers.getControllerForCommand("cmd_beginLine");
|
return ed.controllers.getControllerForCommand("cmd_beginLine");
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
commands: function () {
|
|
||||||
// mode = "i" -> add :iabbrev, :iabclear and :iunabbrev commands
|
|
||||||
function addAbbreviationCommands(ch, modeDescription) {
|
|
||||||
let mode = ch || "!";
|
|
||||||
modeDescription = modeDescription ? " in " + modeDescription + " mode" : "";
|
|
||||||
|
|
||||||
commands.add([ch ? ch + "a[bbrev]" : "ab[breviate]"],
|
|
||||||
"Abbreviate a key sequence" + modeDescription,
|
|
||||||
function (args) {
|
|
||||||
let matches = args.string.match(RegExp("^\\s*($|" + editor._abbrevmatch + ")(?:\\s*$|\\s+(.*))"));
|
|
||||||
dactyl.assert(matches, "E474: Invalid argument");
|
|
||||||
|
|
||||||
let [, lhs, rhs] = matches;
|
|
||||||
if (rhs)
|
|
||||||
editor.addAbbreviation(mode, lhs, rhs);
|
|
||||||
else
|
|
||||||
editor.listAbbreviations(mode, lhs || "");
|
|
||||||
}, {
|
|
||||||
completer: function (context, args) completion.abbreviation(context, args, mode),
|
|
||||||
literal: 0,
|
|
||||||
serialize: function () [ {
|
|
||||||
command: this.name,
|
|
||||||
arguments: [lhs],
|
|
||||||
literalArg: abbr[1]
|
|
||||||
}
|
|
||||||
for ([lhs, abbr] in editor.abbrevs())
|
|
||||||
if (abbr[0] == mode)
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.add([ch ? ch + "una[bbrev]" : "una[bbreviate]"],
|
|
||||||
"Remove an abbreviation" + modeDescription,
|
|
||||||
function (args) { editor.removeAbbreviation(mode, args.literalArg); }, {
|
|
||||||
argCount: "1",
|
|
||||||
completer: function (context, args) completion.abbreviation(context, args, mode),
|
|
||||||
literal: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.add([ch + "abc[lear]"],
|
|
||||||
"Remove all this._abbreviations" + modeDescription,
|
|
||||||
function () { editor.removeAllAbbreviations(mode); },
|
|
||||||
{ argCount: "0" });
|
|
||||||
}
|
|
||||||
|
|
||||||
addAbbreviationCommands("", "");
|
|
||||||
addAbbreviationCommands("i", "insert");
|
|
||||||
addAbbreviationCommands("c", "command line");
|
|
||||||
},
|
|
||||||
|
|
||||||
completion: function () {
|
|
||||||
// TODO: shouldn't all of these have a standard signature (context, args, ...)? --djk
|
|
||||||
completion.abbreviation = function abbreviation(context, args, mode) {
|
|
||||||
mode = mode || "!";
|
|
||||||
|
|
||||||
if (args.completeArg == 0) {
|
|
||||||
let abbreviations = editor.getAbbreviations(mode);
|
|
||||||
context.completions = [[lhs, ""] for ([, [, lhs,]] in Iterator(abbreviations))];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
mappings: function () {
|
mappings: function () {
|
||||||
var myModes = [modes.INSERT, modes.COMMAND_LINE];
|
var myModes = [modes.INSERT, modes.COMMAND_LINE];
|
||||||
|
|
||||||
@@ -866,16 +561,16 @@ const Editor = Module("editor", {
|
|||||||
|
|
||||||
mappings.add([modes.INSERT],
|
mappings.add([modes.INSERT],
|
||||||
["<Space>", "<Return>"], "Expand insert mode abbreviation",
|
["<Space>", "<Return>"], "Expand insert mode abbreviation",
|
||||||
function () { editor.expandAbbreviation("i"); },
|
function () { editor.expandAbbreviation(modes.INSERT); },
|
||||||
{ route: true });
|
{ route: true });
|
||||||
|
|
||||||
mappings.add([modes.INSERT],
|
mappings.add([modes.INSERT],
|
||||||
["<Tab>"], "Expand insert mode abbreviation",
|
["<Tab>"], "Expand insert mode abbreviation",
|
||||||
function () { editor.expandAbbreviation("i"); document.commandDispatcher.advanceFocus(); });
|
function () { editor.expandAbbreviation(modes.INSERT); document.commandDispatcher.advanceFocus(); });
|
||||||
|
|
||||||
mappings.add([modes.INSERT],
|
mappings.add([modes.INSERT],
|
||||||
["<C-]>", "<C-5>"], "Expand insert mode abbreviation",
|
["<C-]>", "<C-5>"], "Expand insert mode abbreviation",
|
||||||
function () { editor.expandAbbreviation("i"); });
|
function () { editor.expandAbbreviation(modes.INSERT); });
|
||||||
|
|
||||||
// textarea mode
|
// textarea mode
|
||||||
mappings.add([modes.TEXTAREA],
|
mappings.add([modes.TEXTAREA],
|
||||||
|
|||||||
@@ -416,6 +416,17 @@
|
|||||||
<a>lhs</a>. If no arguments are given, list all
|
<a>lhs</a>. If no arguments are given, list all
|
||||||
abbreviations.
|
abbreviations.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If the <em>-javascript</em> (short names <em>-js</em>,
|
||||||
|
<em>-j</em>) option is given, <a>lhs</a> is expanded to
|
||||||
|
the value <em>return</em>ed by the JavaScript code
|
||||||
|
<a>rhs</a>. The code is evaluated with the variable
|
||||||
|
<em>editor</em> set to the editable element that the
|
||||||
|
abbreviation is currently being expanded in. The code
|
||||||
|
should <em>not</em> make any changes to the contents of
|
||||||
|
the editor.
|
||||||
|
</p>
|
||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
* Added ‘transliterated’ option to 'hintmatching'
|
* Added ‘transliterated’ option to 'hintmatching'
|
||||||
* Added 'autocomplete' option for specifying which completion
|
* Added 'autocomplete' option for specifying which completion
|
||||||
contexts should be autocompleted
|
contexts should be autocompleted
|
||||||
|
* Added -javascript option to :abbrev and :map
|
||||||
* Added several new options to :map
|
* Added several new options to :map
|
||||||
* Removed the :source line at the end of files generated by
|
* Removed the :source line at the end of files generated by
|
||||||
:mkpentadactylrc
|
:mkpentadactylrc
|
||||||
|
|||||||
Reference in New Issue
Block a user