From 38fd04be35bbd8b942d512d6f45d921d9b64a231 Mon Sep 17 00:00:00 2001 From: Doug Kearns Date: Sat, 21 Jul 2007 16:42:38 +0000 Subject: [PATCH] add initial (very rough) implementations of :map, :mapclear and :unmap --- chrome/content/vimperator/commands.js | 90 ++++++++ chrome/content/vimperator/mappings.js | 295 ++++++++++++++++++++------ 2 files changed, 319 insertions(+), 66 deletions(-) diff --git a/chrome/content/vimperator/commands.js b/chrome/content/vimperator/commands.js index 4d60a2f8..e48973f7 100644 --- a/chrome/content/vimperator/commands.js +++ b/chrome/content/vimperator/commands.js @@ -187,6 +187,7 @@ function Commands() //{{{ if (ex_commands[i].hasName(name)) return ex_commands[i]; } + return null; } @@ -505,6 +506,73 @@ function Commands() //{{{ "The special version :javascript! will open the javascript console of Firefox." } )); + addDefaultCommand(new Command(["map"], + // 0 args -> list all maps + // 1 arg -> list the maps starting with args + // 2 args -> map arg1 to arg* + function(args) + { + if (args.length == 0) + { + vimperator.mappings.list(vimperator.modes.NORMAL); + return; + } + + var matches = args.match(/^([^ ]+)(?:\s+(.+))?$/); + var [lhs, rhs] = [matches[1], matches[2]]; + + if (rhs) + { + if (/^:/.test(rhs)) + { + vimperator.mappings.add( + new Map(vimperator.modes.NORMAL, [lhs], function() { execute(rhs); }, { rhs: rhs }) + ); + } + else + { + var map = vimperator.mappings.get(vimperator.modes.NORMAL, rhs); + + // create a new Map for {lhs} with the same action as + // {rhs}...until we have feedkeys(). + // NOTE: Currently only really useful for static use ie. from + // the RC file + if (map) + vimperator.mappings.add( + new Map(vimperator.modes.NORMAL, [lhs], map.action, { rhs: rhs }) + ); + else + vimperator.echoerr("E475: Invalid argument: " + "{rhs} must be a existing singular mapping"); + } + } + else + { + // FIXME: no filtering for now + vimperator.mappings.list(vimperator.modes.NORMAL, lhs); + } + }, + { + usage: ["map {lhs} {rhs}", "map {lhs}", "map"], + short_help: "Map the key sequence {lhs} to {rhs}", + help: "" + } + )); + addDefaultCommand(new Command(["mapc[lear]"], + function(args) + { + if (args.length > 0) + { + vimperator.echoerr("E474: Invalid argument"); + return; + } + + vimperator.mappings.removeAll(vimperator.modes.NORMAL); + }, + { + short_help: "Remove all mappings", + help: "" + } + )); addDefaultCommand(new Command(["ma[rk]"], function(args) { @@ -891,6 +959,28 @@ function Commands() //{{{ help: "TODO." } )); + addDefaultCommand(new Command(["unm[ap]"], + function(args) + { + if (args.length == 0) + { + vimperator.echoerr("E474: Invalid argument"); + return; + } + + var lhs = args; + + if (vimperator.mappings.hasMap(vimperator.modes.NORMAL, lhs)) + vimperator.mappings.remove(vimperator.modes.NORMAL, lhs); + else + vimperator.echoerr("E31: No such mapping"); + }, + { + usage: ["unm[ap] {lhs}"], + short_help: "Remove the mapping of {lhs}", + help: "" + } + )); addDefaultCommand(new Command(["ve[rsion]"], function(args, special) { diff --git a/chrome/content/vimperator/mappings.js b/chrome/content/vimperator/mappings.js index db8992ae..9547c773 100644 --- a/chrome/content/vimperator/mappings.js +++ b/chrome/content/vimperator/mappings.js @@ -26,14 +26,14 @@ 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 *****/ -function Map(mode, cmds, act, extra_info) //{{{ +function Map(mode, cmds, action, extra_info) //{{{ { - if (!mode || (!cmds || !cmds.length) || !act) + if (!mode || (!cmds || !cmds.length) || !action) return null; this.mode = mode; this.names = cmds; - this.action = act; + this.action = action; this.usage = [this.names[0]]; @@ -56,6 +56,8 @@ function Map(mode, cmds, act, extra_info) //{{{ this.help = extra_info.help || null; this.short_help = extra_info.short_help || null; + this.rhs = extra_info.rhs || null; + // TODO: are these limited to HINTS mode? // Only set for hints maps this.cancel_mode = extra_info.cancel_mode || false; @@ -64,6 +66,17 @@ function Map(mode, cmds, act, extra_info) //{{{ } +Map.prototype.hasName = function(name) +{ + for (var i = 0; i < this.names.length; i++) + { + if (this.names[i] == name) + return true; + } + + return false; +} + // Since we will add many Map-objects, we add some functions as prototypes // this will ensure we only have one copy of each function, not one for each object Map.prototype.execute = function(motion, count, argument) @@ -96,6 +109,7 @@ function Mappings() //{{{ ////////////////////// PRIVATE SECTION ///////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ + // TODO: initialize empty map tables? var main = []; // array of default Map() objects var user = []; // array of objects created by :map @@ -103,33 +117,56 @@ function Mappings() //{{{ { if (!main[map.mode]) main[map.mode] = []; + main[map.mode].push(map); } - function getFrom(mode, cmd, stack) + function getMap(mode, cmd, stack) { - if (!stack || !stack[mode] || !stack[mode].length) - return; - var substack = stack[mode]; - var stack_length = substack.length; - for (var i = 0; i < stack_length; i++) + + for (var i = 0; i < substack.length; i++) { for (var j = 0; j < substack[i].names.length; j++) if (substack[i].names[j] == cmd) return substack[i]; } + + return null; + } + + function removeMap(mode, cmd) + { + var maps = user[mode]; + var names; + + for (var i = 0; i < maps.length; i++) + { + names = maps[i].names; + for (var j = 0; j < names.length; j++) + { + if (names[j] == cmd) + { + names.splice(j, 1) + + if (names.length == 0) + maps.splice(i, 1); + + return; + } + } + } } function mappingsIterator(mode) { - var mappings; + var mappings = main[mode]; - // FIXME: initialize empty map tables - if (user[mode]) - mappings = user[mode].concat(main[mode]); - else - mappings = main[mode] + //// FIXME: do we want to document user commands by default? + //if (user[mode]) + // mappings = user[mode].concat(main[mode]); + //else + // mappings = main[mode] for (var i = 0; i < mappings.length; i++) yield mappings[i]; @@ -158,39 +195,46 @@ function Mappings() //{{{ return mappingsIterator(mode); } + this.hasMap = function(mode, cmd) + { + var user_maps = user[mode]; + + for (var i = 0; i < user_maps.length; i++) + { + if (user_maps[i].names.indexOf(cmd) != -1) + return true; + } + + return false; + } + this.add = function(map) { - if (!map) - return false; - if (!user[map.mode]) user[map.mode] = []; - user[map.mode].push(map); + for (var i = 0; i < map.names.length; i++) + removeMap(map.mode, map.names[i]); - return true; + user[map.mode].push(map); } - // FIXME: this doesn't work - this.remove = function(map) + this.remove = function(mode, cmd) { - var index; + removeMap(mode, cmd); + } - if (!map || !(index = user[map.mode].indexOf(map))) - return false; - - user[map.mode].splice(index, 1); - return true; + this.removeAll = function(mode) + { + user[mode] = []; } this.get = function(mode, cmd) { - if (!mode || !cmd) - return null; + var map = getMap(mode, cmd, user); - var map = getFrom(mode, cmd, user); if (!map) - map = getFrom(mode, cmd, main); + map = getMap(mode, cmd, main); return map; } @@ -201,9 +245,6 @@ function Mappings() //{{{ var mappings = []; var matches = []; - if (!mode || !cmd) - return matches; - if (user[mode]) mappings = user[mode].concat(main[mode]); else @@ -222,6 +263,35 @@ function Mappings() //{{{ return matches; } + // TODO: implement filtering + this.list = function(mode, filter) + { + var maps = user[mode]; + + if (!maps || maps.length == 0) + { + vimperator.echo("No mappings found"); + return; + } + + var list = ""; + + for (var i = 0; i < maps.length; i++) + { + for (var j = 0; j < maps[i].names.length; j++) + { + list += ""; + list += "" + if (maps[i].rhs) + list += "" + list += ""; + } + } + list += "
 " + maps[i].names[j].replace(//g, ">") + " " + maps[i].rhs.replace(//g, ">") + "
"; + + vimperator.commandline.echo(list, true); // TODO: force of multiline widget a better way + } + /////////////////////////////////////////////////////////////////////////////}}} ////////////////////// DEFAULT MAPPINGS //////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ @@ -800,135 +870,228 @@ function Mappings() //{{{ // action keys addDefaultMap(new Map(vimperator.modes.HINTS, ["o"], function() { vimperator.hints.openHints(false, false); }, - { cancel_mode: true, always_active: false } + { + cancel_mode: true, + always_active: false + } )); addDefaultMap(new Map(vimperator.modes.HINTS, ["t"], function() { vimperator.hints.openHints(true, false); }, - { cancel_mode: true, always_active: false } + { + cancel_mode: true, + always_active: false + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { vimperator.hints.openHints(false, true ); }, - { cancel_mode: true, always_active: false } + { + cancel_mode: true, + always_active: false + } )); addDefaultMap(new Map(vimperator.modes.HINTS, ["s"], function() { vimperator.echoerr('Saving of links not yet implemented'); }, - { cancel_mode: true, always_active: false } + { + cancel_mode: true, + always_active: false + } )); addDefaultMap(new Map(vimperator.modes.HINTS, ["y"], function() { vimperator.hints.yankUrlHints(); }, - { cancel_mode: true, always_active: false } + { + cancel_mode: true, + always_active: false + } )); addDefaultMap(new Map(vimperator.modes.HINTS, ["Y"], function() { vimperator.hints.yankTextHints(); }, - { cancel_mode: true, always_active: false } + { + cancel_mode: true, + always_active: false + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [","], function() { vimperator.input.buffer += ','; vimperator.hints.setCurrentState(0); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [":"], function() { vimperator.commandline.open(':', '', vimperator.modes.EX); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); // movement keys addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { scrollBufferRelative(0, 1); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { scrollBufferRelative(0, -1); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { scrollBufferAbsolute(-1, 0); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { scrollBufferAbsolute(-1, 100); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { goDoCommand('cmd_scrollPageUp'); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { goDoCommand('cmd_scrollPageUp'); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { goDoCommand('cmd_scrollPageDown'); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { goDoCommand('cmd_scrollPageDown'); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { scrollBufferRelative(-1, 0); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { scrollBufferRelative(0, 1); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { scrollBufferRelative(0, -1); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { scrollBufferRelative(1, 0); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); // tab management addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { vimperator.tabs.select('+1', true); }, - { cancel_mode: true, always_active: true } + { + cancel_mode: true, + always_active: true + } )); // same as gt, but no count supported addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { vimperator.tabs.select('-1', true); }, - { cancel_mode: true, always_active: true } + { + cancel_mode: true, + always_active: true + } )); // navigation addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { vimperator.history.stepTo(vimperator.input.count > 0 ? -1 * vimperator.input.count : -1); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { vimperator.history.stepTo(vimperator.input.count > 0 ? vimperator.input.count : 1); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { vimperator.history.stepTo(vimperator.input.count > 0 ? -1 * vimperator.input.count : -1); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { vimperator.history.stepTo(vimperator.input.count > 0 ? vimperator.input.count : 1); }, - { cancel_mode: false, always_active: true } + { + cancel_mode: false, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { vimperator.tabs.remove(getBrowser().mCurrentTab, vimperator.input.count, false, 0); }, - { cancel_mode: true, always_active: true } + { + cancel_mode: true, + always_active: true + } )); // cancel_mode hint mode keys addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { ; }, - { cancel_mode: true, always_active: true } + { + cancel_mode: true, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { ; }, - { cancel_mode: true, always_active: true } + { + cancel_mode: true, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { ; }, - { cancel_mode: true, always_active: true } + { + cancel_mode: true, + always_active: true + } )); addDefaultMap(new Map(vimperator.modes.HINTS, [""], function() { ; }, - { cancel_mode: true, always_active: true } + { + cancel_mode: true, + always_active: true + } )); //}}} //}}}