From baebe6978c4e54890ba838600488cab408915eea Mon Sep 17 00:00:00 2001 From: Martin Stubenschrott Date: Thu, 1 Nov 2007 20:30:03 +0000 Subject: [PATCH] abbreviations for textboxes (thanks calmar) --- AUTHORS | 2 +- NEWS | 1 + content/commands.js | 106 +++++++++++++++++++++++++++++- content/editor.js | 154 ++++++++++++++++++++++++++++++++++++++++---- content/events.js | 5 +- content/mappings.js | 30 +++++++-- content/ui.js | 1 - 7 files changed, 275 insertions(+), 24 deletions(-) diff --git a/AUTHORS b/AUTHORS index 659ca289..92e76916 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,6 +5,7 @@ Main developer: Developers: * Viktor Kojouharov (Виктор Кожухаров) * Doug Kearns (dougkearns@gmail.com) + * Marco Candrian Patches: * Muthu Kannan (ctrl-v support) @@ -12,7 +13,6 @@ Patches: * Lee Hinman (:open ./.. support) * Bart Trojanowski (Makefile) * Hannes Rist (:set titlestring support) - * Marco Candrian (shift-insert patch) * Nikolai Weibull ($VIMPERATOR_HOME) * Joseph Xu (supporting multiple top level windows better) diff --git a/NEWS b/NEWS index 92f6950d..1bb1a4be 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ read up the new help for the f, F and ; commands for details removed the following hint options: 'hintchars' added the following hint options: 'hinttimeout' + * abbreviations for text fields (:abbr etc.) (thanks calmar) * you can edit textfields with Ctrl-i now using an external editor (thanks to Joseph Xu) * :open, :bmarks, etc. filter on space separated tokens now, so you can search with :open linux windows <tab> all your bookmarks/history diff --git a/content/commands.js b/content/commands.js index 16cc7bff..25ca5672 100644 --- a/content/commands.js +++ b/content/commands.js @@ -92,7 +92,7 @@ vimperator.Command = function(specs, action, extra_info) //{{{ vimperator.Command.prototype.execute = function(args, special, count, modifiers) { - this.action.call(this, args, special, count, modifiers); + return this.action.call(this, args, special, count, modifiers); } // return true if the candidate name matches one of the command's aliases @@ -1098,6 +1098,110 @@ vimperator.Commands = function() //{{{ "Without arguments, displays a list of all variables." } )); + // code for abbreviations + addDefaultCommand(new vimperator.Command(["ab[breviate]"], + function(args) + { + if (!args) + { + vimperator.editor.listAbbreviations("!", ""); + return; + } + + var matches = args.match(/^([^\s]+)(?:\s+(.+))?$/) + var [lhs, rhs] = [matches[1], matches[2]]; + if (rhs) + vimperator.editor.addAbbreviation("!", lhs, rhs); + else + vimperator.editor.listAbbreviations("!", lhs); + }, + { + usage: ["ab[breviate] {lhs} {rhs}", "ab[breviate] {lhs}", "ab[breviate]"], + short_help: "Abbreviate a key sequence", + help: "Abbreviate {lhs} to {rhs}.
" + + "If only {lhs} given, list that particual abbreviation.
" + + "List all abbreviations, if no arguments to are given.
" + } + )); + addDefaultCommand(new vimperator.Command(["ca[bbrev]"], + function(args) + { + if (!args) + { + vimperator.editor.listAbbreviations("c", ""); + return; + } + + var matches = args.match(/^([^\s]+)(?:\s+(.+))?$/) + var [lhs, rhs] = [matches[1], matches[2]]; + if (rhs) + vimperator.editor.addAbbreviation("c", lhs, rhs); + else + vimperator.editor.listAbbreviations("c", lhs); + }, + { + usage: ["ca[bbrev] {lhs} {rhs}", "ca[bbrev] {lhs}", "ca[bbrev]"], + short_help: "Abbreviate a key sequence for Command-line mode", + help: "Same as :ab[reviate], but for Command-line mode only." + } + )); + addDefaultCommand(new vimperator.Command(["ia[bbrev]"], + function(args) + { + if (!args) + { + vimperator.editor.listAbbreviations("i", ""); + return; + } + + var matches = args.match(/^([^\s]+)(?:\s+(.+))?$/) + var [lhs, rhs] = [matches[1], matches[2]]; + if (rhs) + vimperator.editor.addAbbreviation("i", lhs, rhs); + else + vimperator.editor.listAbbreviations("i", lhs); + }, + { + usage: ["ia[bbrev] {lhs} {rhs}", "ia[bbrev] {lhs}", "ia[bbrev]"], + short_help: "Abbreviate a key sequence for Insert mode", + help: "Same as :ab[breviate], but for Insert mode only." + } + )); + addDefaultCommand(new vimperator.Command(["una[bbreviate]"], + function(args) { vimperator.editor.removeAbbreviation("!", args); }, + { + usage: ["una[bbreviate] {lhs}"], + short_help: "Remove an abbreviation" + } + )); + addDefaultCommand(new vimperator.Command(["cuna[bbrev]"], + function(args) { vimperator.editor.removeAbbreviation("c", args); }, + { + usage: ["cuna[bbrev] {lhs}"], + short_help: "Remove an abbreviation for Command-line mode", + help: "Same as :una[bbreviate], but for Command-line mode only." + } + )); + addDefaultCommand(new vimperator.Command(["iuna[bbrev]"], + function(args) { vimperator.editor.removeAbbreviation("i", args); }, + { + usage: ["iuna[bbrev] {lhs}"], + short_help: "Remove an abbreviation for Insert mode", + help: "Same as :una[bbreviate], but for Insert mode only." + } + )); + addDefaultCommand(new vimperator.Command(["ab[clear]"], + function(args) { vimperator.editor.removeAllAbbreviations("!"); }, + { short_help: "Remove all abbreviations" } + )); + addDefaultCommand(new vimperator.Command(["cab[clear]"], + function(args) { vimperator.editor.removeAllAbbreviations("c"); }, + { short_help: "Remove all abbreviations for Command-line mode" } + )); + addDefaultCommand(new vimperator.Command(["iab[clear]"], + function(args) { vimperator.editor.removeAllAbbreviations("i"); }, + { short_help: "Remove all abbreviations for Insert mode" } + )); addDefaultCommand(new vimperator.Command(["map"], // 0 args -> list all maps // 1 arg -> list the maps starting with args diff --git a/content/editor.js b/content/editor.js index 555c56c7..175973e7 100644 --- a/content/editor.js +++ b/content/editor.js @@ -1,17 +1,29 @@ /***** BEGIN LICENSE BLOCK ***** {{{ - * - * Mozilla Public License Notice - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License - * at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and - * limitations under the License. - * +Version: MPL 1.1/GPL 2.0/LGPL 2.1 + +The contents of this file are subject to the Mozilla Public License Version +1.1 (the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +for the specific language governing rights and limitations under the +License. + +(c) 2006-2007: Martin Stubenschrott + +Alternatively, the contents of this file may be used under the terms of +either the GNU General Public License Version 2 or later (the "GPL"), or +the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +in which case the provisions of the GPL or the LGPL are applicable instead +of those above. If you wish to allow use of your version of this file only +under the terms of either the GPL or the LGPL, and not to allow others to +use your version of this file under the terms of the MPL, indicate your +decision by deleting the provisions above and replace them with the notice +and other provisions required by the GPL or the LGPL. If you do not delete +the provisions above, a recipient may use your version of this file under +the terms of any one of the MPL, the GPL or the LGPL. }}} ***** END LICENSE BLOCK *****/ // command names taken from: @@ -19,9 +31,10 @@ vimperator.Editor = function() //{{{ { - // store our last search with f,F,t or T + // store our last search with f, F, t or T var last_findChar = null; var last_findChar_func = null; + var abbrev = {}; // abbrev["lhr"][0] is the {rhs} and abbrev["lhr"][1] the mode {i,c,!} function editor() { @@ -379,6 +392,119 @@ vimperator.Editor = function() //{{{ tmpfile.remove(false); } + + // Abbreviations {{{ + // TODO: won't save into vimperatorrc? with mkvimperatorrc + + // filter is i, c or "!" (insert or command abbreviations or both) + this.listAbbreviations = function(filter, lhs) + { + if (lhs) // list only that one + { + if (abbrev[lhs]) + { + vimperator.echo(abbrev[lhs][1] + " " + lhs + " " + abbrev[lhs][0]); + return true; + } + vimperator.echoerr("No abbreviations found"); + return false; + } + else // list all (for that filter {i,c,!}) + { + var flagFound = false; + var searchFilter = (filter == "!") ? "!ci" : filter + "!"; // ! -> list all, on c or i ! matches too) + var list = ""; + for (var item in abbrev) + { + if (searchFilter.indexOf(abbrev[item][1]) > -1) + { + if (!flagFound) + flagFound = true; + + list += ""; + list += ""; + list += ""; + list += ""; + list += ""; + } + } + if (!flagFound) + { + vimperator.echoerr("No abbreviations found"); + return; + } + list += "
" + abbrev[item][1] + " " + vimperator.util.escapeHTML(item) + " " + vimperator.util.escapeHTML(abbrev[item][0]) + "
"; + vimperator.commandline.echo(list, vimperator.commandline.HL_NORMAL, vimperator.commandline.FORCE_MULTILINE); + } + } + + this.addAbbreviation = function(filter, lhs, rhs) + { + var modeSign = filter; + + // get proper Mode value i + c = ! on identical abbreviations + if (abbrev[lhs] && abbrev[lhs][0] == rhs && abbrev[lhs][1] != filter) + modeSign = "!"; + + abbrev[lhs] = [rhs, modeSign]; + } + + this.removeAbbreviation = function(filter, lhs) + { + if (abbrev[lhs]) // abbrev exists + { + if (filter == "!" || (abbrev[lhs][1] == filter && filter != "!" )) + { + delete (abbrev[lhs]); + return true; + } + else if (abbrev[lhs][1] == "!") // exists as ! -> no 'full' delete, since filter is not either + { + abbrev[lhs][1] = filter == "i" ? "c" : "i"; // it's a !; e.g. ! - i = c; ! - c = i + return true; + } + } + vimperator.echoerr("E24: No such abbreviation"); + return false; + } + + this.removeAllAbbreviations = function(filter) + { + for (var item in abbrev) + { + if (filter == "!" || abbrev[item][1] == "!" || abbrev[item][1] == filter) + this.removeAbbreviation(filter, item); + } + } + + this.expandAbbreviation = function(filter) // try to find an candidate and replace accordingly + { + var textbox = editor(); + var text = textbox.value; + var currStart = textbox.selectionStart; + var currEnd = textbox.selectionEnd; + var foundWord = text.substring(0, currStart).replace(/^(.|\n)*?(\S+)$/m, "$2"); // get last word \b word boundary + if (!foundWord) + return true; + + for (var item in abbrev) + { + if (item == foundWord && (abbrev[item][1] == filter || abbrev[item][1] == "!")) + { + // if found, replace accordingly + var len = foundWord.length; + var abbr_text = abbrev[item][0]; + text = text.substring(0, currStart - len) + abbr_text + text.substring(currStart); + textbox.value = text; + textbox.selectionStart = currStart - len + abbr_text.length; + textbox.selectionEnd = currEnd - len + abbr_text.length; + break; + } + } + return true; + } + + //}}} } //}}} // vim: set fdm=marker sw=4 ts=4 et: diff --git a/content/events.js b/content/events.js index 4799ff98..b9e47942 100644 --- a/content/events.js +++ b/content/events.js @@ -622,7 +622,6 @@ vimperator.Events = function() //{{{ // if Hit-a-hint mode is on, special handling of keys is required - // FIXME: should be handled properly! if (key != "" && key != "") { if (vimperator.mode == vimperator.modes.HINTS) @@ -684,7 +683,9 @@ vimperator.Events = function() //{{{ else { vimperator.input.buffer = ""; - map.execute(null, vimperator.input.count); + var ret = map.execute(null, vimperator.input.count); + if (map.flags & vimperator.Mappings.flags.ALLOW_EVENT_ROUTING && ret) + stop = false; } } else if (vimperator.mappings.getCandidates(vimperator.mode, candidate_command).length > 0) diff --git a/content/mappings.js b/content/mappings.js index fda80c1c..0adf2b74 100644 --- a/content/mappings.js +++ b/content/mappings.js @@ -87,7 +87,8 @@ vimperator.Map.prototype.execute = function(motion, count, argument) args.push(count); if (this.flags & vimperator.Mappings.flags.ARGUMENT) args.push(argument); - this.action.apply(this, args); + + return this.action.apply(this, args); } //}}} @@ -176,9 +177,10 @@ vimperator.Mappings = function() //{{{ /////////////////////////////////////////////////////////////////////////////{{{ vimperator.Mappings.flags = { - MOTION: 1 << 0, - COUNT: 1 << 1, - ARGUMENT: 1 << 2 + ALLOW_EVENT_ROUTING: 1 << 0, // if set, return true inside the map command to pass the event further to firefox + MOTION: 1 << 1, + COUNT: 1 << 2, + ARGUMENT: 1 << 3 }; // NOTE: just normal mode for now @@ -1948,7 +1950,7 @@ vimperator.Mappings = function() //{{{ addDefaultMap(new vimperator.Map([vimperator.modes.INSERT, vimperator.modes.COMMAND_LINE], [""], function() { - // broken in FF3, deletes the wohle line: + // broken in FF3, deletes the whole line: // vimperator.editor.executeCommand("cmd_deleteToBeginningOfLine", 1); vimperator.editor.executeCommand("cmd_selectBeginLine", 1); vimperator.editor.executeCommand("cmd_delete", 1); @@ -1979,6 +1981,24 @@ vimperator.Mappings = function() //{{{ function() { vimperator.editor.editWithExternalEditor(); }, { } )); + addDefaultMap(new vimperator.Map([vimperator.modes.INSERT, vimperator.modes.TEXTAREA], ["", "", ""], + function() { return vimperator.editor.expandAbbreviation("i"); }, + { flags: vimperator.Mappings.flags.ALLOW_EVENT_ROUTING } + )); + addDefaultMap(new vimperator.Map([vimperator.modes.INSERT, vimperator.modes.TEXTAREA], + ["", ""], function() { vimperator.editor.expandAbbreviation("i"); }, { } + )); + + //}}} + // COMMAND_LINE mode + //{{{ + addDefaultMap(new vimperator.Map([vimperator.modes.COMMAND_LINE], [""], + function() { return vimperator.editor.expandAbbreviation("c"); }, + { flags: vimperator.Mappings.flags.ALLOW_EVENT_ROUTING } + )); + addDefaultMap(new vimperator.Map([vimperator.modes.COMMAND_LINE], + ["", ""], function() { vimperator.editor.expandAbbreviation("c"); }, { } + )); //}}} diff --git a/content/ui.js b/content/ui.js index 8c73496f..ee1b7a3e 100644 --- a/content/ui.js +++ b/content/ui.js @@ -37,7 +37,6 @@ vimperator.CommandLine = function() //{{{ //////////////////////////////////////////////////////////////////////////////// ////////////////////// PRIVATE SECTION ///////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ - const UNINITIALIZED = -2; // notifies us, if we need to start history/tab-completion from the beginning var completionlist = new vimperator.InformationList("vimperator-completion", { min_items: 2, max_items: 10 });