From a53b70220031ab3a1aad96b8d1b209e7ab4ee846 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Thu, 30 Oct 2008 03:31:19 +0000 Subject: [PATCH] Generally niftier :mkv --- content/buffer.js | 11 +++++- content/commands.js | 82 +++++++++++++++++++++++++++++---------------- content/editor.js | 73 ++++++++++++++++++++-------------------- content/io.js | 37 +++++--------------- content/mappings.js | 43 ++++++++++-------------- content/options.js | 11 +++++- content/template.js | 2 +- content/util.js | 19 ++++++++++- 8 files changed, 155 insertions(+), 123 deletions(-) diff --git a/content/buffer.js b/content/buffer.js index c623b6a5..94edd772 100644 --- a/content/buffer.js +++ b/content/buffer.js @@ -857,7 +857,16 @@ function Buffer() //{{{ bang: true, hereDoc: true, literal: true, - options: [[["-name", "-n"], commands.OPTION_STRING]] + options: [[["-name", "-n"], commands.OPTION_STRING]], + serial: function () [ + { + command: this.name, + bang: true, + options: sty.name ? {"-name": sty.name} : {}, + arguments: [sty.sites.join(",")], + literalArg: sty.css + } for ([k, sty] in styles.userSheets) + ] }); commands.add(["dels[tyle]"], diff --git a/content/commands.js b/content/commands.js index b9930152..9224023d 100644 --- a/content/commands.js +++ b/content/commands.js @@ -81,6 +81,7 @@ function Command(specs, description, action, extraInfo) //{{{ this.bang = extraInfo.bang || false; this.count = extraInfo.count || false; this.literal = extraInfo.literal || false; + this.serial = extraInfo.serial; this.isUserCommand = extraInfo.isUserCommand || false; this.replacementText = extraInfo.replacementText || null; @@ -166,25 +167,15 @@ function Commands() //{{{ function getMatchingUserCommands(name, filter) { var matches = []; - outer: for (let [,cmd] in Iterator(exCommands)) { if (cmd.isUserCommand) { - for (let [k, v] in Iterator(filter)) + if (!name || cmd.name.match("^" + name)) { - if (v != null && cmd[k] != v) - continue outer; - } - if (name) - { - if (cmd.name.match("^" + name)) + if (util.map(filter, function (f) f).every(function ([k, v]) v == null || cmd[k] == v)) matches.push(cmd); } - else - { - matches.push(cmd); - } } } return matches; @@ -199,6 +190,17 @@ function Commands() //{{{ return NaN; } + function quote(q, list) list.reduce(function (acc, [k,v]) + { + v = "\\" + (v || k); + return function (str) acc(String.replace(str, k, v, "g")) + }, function (val) q + val + q); + const quoteArg = { + '"': quote('"', [["\n", "n"], ["\t", "t"], ['"'], ["\\"]]), + "'": quote("'", [["\\"], ["'"]]), + "": quote("", [["\\"], [" "]]) + } + const ArgType = new Struct("description", "parse"); const argTypes = [ null, @@ -291,6 +293,26 @@ function Commands() //{{{ return true; }, + commandToString: function (args) + { + let res = [args.command + (args.bang ? "!" : "")]; + function quote(str) quoteArg[/\s/.test(str) ? '"' : ""](str); + + for (let [opt, val] in Iterator(args.options || {})) + { + res.push(opt); + if (val != null) + res.push(quote(val)); + } + for (let [,arg] in Iterator(args.arguments || [])) + res.push(quote(arg)); + + let str = args.literalArg; + if (str) + res.push(/\n/.test(str) ? "<"; if (quote && typeof res != "number") - return '"' + String.replace(res, '"', '\\"', "g") + '"'; + return quoteArg['"'](res); return res; }); } @@ -782,6 +792,20 @@ function Commands() //{{{ [["-nargs"], commandManager.OPTION_STRING, function (arg) /^[01*?+]$/.test(arg), ["0", "1", "*", "?", "+"]], [["-bang"], commandManager.OPTION_NOARG], [["-count"], commandManager.OPTION_NOARG], + ], + serial: function () [ + { + command: this.name, + // Yeah, this is a bit scary. Perhaps I'll fix it when I'm + // awake. + options: util.Array.assocToObj(util.map({argCount: "-nargs", bang: "-bang", count: "-count"}, + function ([k, v]) k in cmd && cmd[k] != "0" && [v, typeof cmd[k] == "boolean" ? null : cmd[k]]) + .filter(function(k) k)), + arguments: [cmd.name], + literalArg: cmd.replacementText + } + for ([k,cmd] in Iterator(exCommands)) + if (cmd.isUserCommand && cmd.replacementText) ] }); diff --git a/content/editor.js b/content/editor.js index 5974a96b..9b441eeb 100644 --- a/content/editor.js +++ b/content/editor.js @@ -147,6 +147,15 @@ function Editor() //{{{ { flags: Mappings.flags.MOTION | Mappings.flags.COUNT }); } + // For the record, some of this code I've just finished throwing + // away makes me want to pull someone else's hair out. --Kris + function abbrevs() + { + for (let [lhs, abbr] in Iterator(abbrev)) + for (let [,rhs] in Iterator(abbr)) + yield [lhs, rhs]; + } + // mode = "i" -> add :iabbrev, :iabclear and :iunabbrev commands function addAbbreviationCommands(ch, modeDescription) { @@ -157,20 +166,25 @@ function Editor() //{{{ "Abbreviate a key sequence" + modeDescription, function (args) { - args = args.string; - - if (!args) - { - editor.listAbbreviations(mode, ""); - return; - } - - var matches = args.match(/^(\S+)(?:\s+(.+))?$/); - var [lhs, rhs] = [matches[1], matches[2]]; + let lhs = args.arguments[0]; + let rhs = args.literalArg; if (rhs) editor.addAbbreviation(mode, lhs, rhs); else - editor.listAbbreviations(mode, lhs); + editor.listAbbreviations(mode, lhs || ""); + }, + { + argCount: 1, + literal: true, + serial: function () [ + { + command: this.name, + arguments: [lhs], + literalArg: abbr[1] + } + for ([lhs, abbr] in abbrevs()) + if (abbr[0] == mode) + ] }); commands.add([ch ? ch + "una[bbrev]" : "una[bbreviate]"], @@ -892,21 +906,6 @@ function Editor() //{{{ // Abbreviations {{{ - abbreviations: { - __iterator__: function () - { - var tmpCmd; - for (let lhs in abbrev) - { - for (let i = 0; i < abbrev[lhs].length; i++) - { - tmpCmd = (abbrev[lhs][i][0] == "!") ? "abbreviate" : abbrev[lhs][i][0] + "abbrev"; - yield (tmpCmd + " " + lhs + " " + abbrev[lhs][i][1] + "\n"); - } - } - } - }, - // filter is i, c or "!" (insert or command abbreviations or both) listAbbreviations: function (filter, lhs) { @@ -914,10 +913,11 @@ function Editor() //{{{ { if (abbrev[lhs]) { - for (let i = 0; i < abbrev[lhs].length; i++) + for (let [,abbr] in Iterator(abbrev[lhs])) { - if (abbrev[lhs][i][0] == filter) - liberator.echo(abbrev[lhs][i][0] + " " + lhs + " " + abbrev[lhs][i][1]); + if (abbr[0] == filter) + liberator.echo(abbr[0] + " " + lhs + " " + abbr[1]); + // Is it me, or is this clearly very wrong? --Kris return true; } } @@ -932,14 +932,13 @@ function Editor() //{{{ let list = { - template.map(abbrev, function ([lhs, rhs]) - template.map(rhs, function (abbr) - searchFilter.indexOf(abbr[0]) < 0 ? undefined : - - - - - )) + template.map(abbrevs(), function ([lhs, rhs]) + searchFilter.indexOf(rhs[0]) < 0 ? undefined : + + + + + ) }
{abbr[0]}{lhs}{abbr[1]}
{rhs[0]}{lhs}{rhs[1]}
; if (list.*.length()) diff --git a/content/io.js b/content/io.js index 45f1f4d7..de875d1f 100644 --- a/content/io.js +++ b/content/io.js @@ -228,50 +228,31 @@ function IO() //{{{ return; } - let line = "\" " + liberator.version + "\n"; - line += "\" Mappings\n"; + let lines = [['"' + liberator.version]]; - [[[modes.NORMAL], ""], - [[modes.COMMAND_LINE], "c"], - [[modes.INSERT, modes.TEXTAREA], "i"]].forEach(function ([modes, modechar]) { - // NOTE: names.length is always 1 on user maps. If that changes, also fix getUserIterator and v.m.list - for (let map in mappings.getUserIterator(modes)) - line += modechar + (map.noremap ? "noremap" : "map") + " " + map.names[0] + " " + map.rhs + "\n"; - }); - - line += "\n\" Options\n"; - for (let option in options) + // FIXME: Use a set/specifiable list here: + for (let cmd in commands) { - // TODO: options should be queried for this info - // TODO: string/list options might need escaping in future - if (!/fullscreen|usermode/.test(option.name) && option.value != option.defaultValue) - { - if (option.type == "boolean") - line += "set " + (option.value ? option.name : "no" + option.name) + "\n"; - else - line += "set " + option.name + "=" + option.value + "\n"; - } + if (cmd.serial) + lines.push(cmd.serial().map(commands.commandToString)); } + lines = util.Array.flatten(lines); // :mkvimrc doesn't save autocommands, so we don't either - remove this code at some point // line += "\n\" Auto-Commands\n"; // for (let item in autocommands) // line += "autocmd " + item.event + " " + item.pattern.source + " " + item.command + "\n"; - line += "\n\" Abbreviations\n"; - for (let abbrCmd in editor.abbreviations) - line += abbrCmd; - // if (mappings.getMapLeader() != "\\") // line += "\nlet mapleader = \"" + mappings.getMapLeader() + "\"\n"; // source a user .vimperatorrc file - line += "\nsource! " + filename + ".local\n"; - line += "\n\" vim: set ft=vimperator:"; + lines.push("\nsource! " + filename + ".local"); + lines.push("\n\" vim: set ft=vimperator:"); try { - io.writeFile(file, line); + io.writeFile(file, lines.join("\n")); } catch (e) { diff --git a/content/mappings.js b/content/mappings.js index 3faa2159..5011665a 100644 --- a/content/mappings.js +++ b/content/mappings.js @@ -132,30 +132,10 @@ function Mappings() //{{{ function mappingsIterator(modes, stack) { - var output; - var maps = stack[modes[0]]; - - for (let i = 0; i < maps.length; i++) - { - output = true; - for (let index = 1; index < modes.length; index++) // check other modes - { - output = false; // toggle false, only true whan also found in this mode - for (let j = 0; j < user[modes[index]].length; j++) // maps - { - // NOTE: when other than user maps, there might be more than only one names[x]. - // since only user mappings gets queried here, only names[0] gets checked for equality. - if (maps[i].rhs == user[modes[index]][j].rhs && maps[i].names[0] == user[modes[index]][j].names[0]) - { - output = true; - break; // found on this mode - check next mode, if there is one, where it could still fail... - } - } - break; // not found in this mode -> map wont' match all modes... - } - if (output) - yield maps[i]; - } + modes = modes.slice(); + return (map for ([i, map] in Iterator(stack[modes.shift()])) + if (modes.every(function (mode) stack[mode].some( + function (m) m.rhs == map.rhs && m.names[0] == map.names[0])))) } function addMapCommands(ch, modes, modeDescription) @@ -203,7 +183,20 @@ function Mappings() //{{{ [["", ""], commands.OPTION_NOARG] ], argCount: 1, - literal: true + literal: true, + serial: function () { + let noremap = this.name.indexOf("noremap") > -1; + return [ + { + command: this.name, + options: map.silent ? {"": null} : {}, + arguments: [map.names[0]], + literalArg: map.rhs + } + for (map in mappingsIterator(modes, user)) + if (map.rhs && map.noremap == noremap) + ] + } }; commands.add([ch ? ch + "m[ap]" : "map"], diff --git a/content/options.js b/content/options.js index 072dae14..a32779e0 100644 --- a/content/options.js +++ b/content/options.js @@ -799,7 +799,16 @@ function Options() //{{{ } } return [len, completion.filter(completions, filter, true)]; - } + }, + serial: function () [ + { + command: this.name, + literalArg: opt.type == "boolean" ? (opt.value ? "" : "no") + opt.name + : opt.name + "=" + opt.value + } + for (opt in options) + if (!opt.getter && opt.value != opt.defaultValue && (opt.scope & options.OPTION_SCOPE_GLOBAL)) + ] }); commands.add(["unl[et]"], diff --git a/content/template.js b/content/template.js index 02de9aa3..4bb585e1 100644 --- a/content/template.js +++ b/content/template.js @@ -53,7 +53,7 @@ const template = { // Vim generally doesn't like /foo*/, because */ looks like a comment terminator. // Using /foo*(:?)/ instead. if (processStrings) - return {String(arg).replace(/\{(.|\n)*(?:)/, "{ ... }")}; + return {String(arg).replace(/\{(.|\n)*(?:)/g, "{ ... }")}; return <>{arg}; case "undefined": return {arg}; diff --git a/content/util.js b/content/util.js index 77faafe6..8d5746f5 100644 --- a/content/util.js +++ b/content/util.js @@ -29,6 +29,15 @@ the terms of any one of the MPL, the GPL or the LGPL. const util = { //{{{ Array: { + // [["a", "b"], ["c", "d"]] -> {a: "b", c: "d"} + // From Common Lisp, more or less + assocToObj: function (assoc) + { + let obj = {}; + assoc.forEach(function ([k, v]) { obj[k] = v }); + return obj; + }, + // flatten an array: [["foo", "bar"], ["baz"]] -> ["foo", "bar", "baz"] flatten: function (ary) { @@ -165,7 +174,7 @@ const util = { //{{{ { if (delimiter == undefined) delimiter = '"'; - return delimiter + str.replace(/([\\'"])/g, "\\$1").replace("\n", "\\n").replace("\t", "\\t") + delimiter; + return delimiter + str.replace(/([\\'"])/g, "\\$1").replace("\n", "\\n", "g").replace("\t", "\\t", "g") + delimiter; }, formatBytes: function (num, decimalPlaces, humanReadable) @@ -254,6 +263,14 @@ const util = { //{{{ return ret; }, + map: function (obj, fn) + { + let ary = []; + for (let i in Iterator(obj)) + ary.push(fn(i)); + return ary; + }, + // if color = true it uses HTML markup to color certain items objectToString: function (object, color) {