From e1cd0e07bb06ba63fa692c3d8bcc76e16f535b5d Mon Sep 17 00:00:00 2001 From: Doug Kearns Date: Tue, 6 Nov 2007 08:59:02 +0000 Subject: [PATCH] allow ^= to be used for prepending values to string options and for multiplying number options --- content/commands.js | 201 +++++++++++++++++++++++++++----------------- content/options.js | 12 ++- 2 files changed, 136 insertions(+), 77 deletions(-) diff --git a/content/commands.js b/content/commands.js index 20b7accb..e611cb37 100644 --- a/content/commands.js +++ b/content/commands.js @@ -182,7 +182,7 @@ vimperator.Commands = function() //{{{ outer: for (var i = 0; i < str.length; i++) { - switch(str[i]) + switch (str[i]) { case "\"": if (in_escape_key) @@ -1111,7 +1111,7 @@ vimperator.Commands = function() //{{{ var matches = args.match(/^([^\s]+)(?:\s+(.+))?$/) var [lhs, rhs] = [matches[1], matches[2]]; if (rhs) - vimperator.editor.addAbbreviation("!", lhs, rhs); + vimperator.editor.addAbbreviation("!", lhs, rhs); else vimperator.editor.listAbbreviations("!", lhs); }, @@ -1135,7 +1135,7 @@ vimperator.Commands = function() //{{{ var matches = args.match(/^([^\s]+)(?:\s+(.+))?$/) var [lhs, rhs] = [matches[1], matches[2]]; if (rhs) - vimperator.editor.addAbbreviation("c", lhs, rhs); + vimperator.editor.addAbbreviation("c", lhs, rhs); else vimperator.editor.listAbbreviations("c", lhs); }, @@ -1157,7 +1157,7 @@ vimperator.Commands = function() //{{{ var matches = args.match(/^([^\s]+)(?:\s+(.+))?$/) var [lhs, rhs] = [matches[1], matches[2]]; if (rhs) - vimperator.editor.addAbbreviation("i", lhs, rhs); + vimperator.editor.addAbbreviation("i", lhs, rhs); else vimperator.editor.listAbbreviations("i", lhs); }, @@ -1641,6 +1641,7 @@ vimperator.Commands = function() //{{{ } )); addDefaultCommand(new vimperator.Command(["se[t]"], + // TODO: support setting multiple options at once function(args, special, count, modifiers) { if (special) @@ -1649,63 +1650,64 @@ vimperator.Commands = function() //{{{ return; } - var only_non_default = false; //used for :set to print non-default options + var only_non_default = false; // used for :set to print non-default options if (!args) { - args = "all?"; + args = "all"; only_non_default = true; } - // 1 2 3 4 5 6 - var matches = args.match(/^\s*(no|inv)?([a-z]+)([?&!])?(([+-])?=(.*))?/); + // 1 2 3 4 5 6 + var matches = args.match(/^\s*(no|inv)?([a-z]+)([?&!])?\s*(([+-^]?)=(.*))?\s*$/); if (!matches) { vimperator.echoerr("E518: Unknown option: " + args); return; } - var no = false; + var unset_boolean = false; if (matches[1] == "no") - no = true; - - var opt = matches[2]; + unset_boolean = true; + var name = matches[2]; var all = false; - if (opt == "all") + if (name == "all") all = true; - var option = vimperator.options.get(opt); + var option = vimperator.options.get(name); if (!option && !all) { - vimperator.echoerr("E518: Unknown option: " + opt); + vimperator.echoerr("E518: Unknown option: " + args); return; } + var value_given = !!matches[4]; + var get = false; - if (all || matches[3] == "?" || (option.type != "boolean" && matches[4] === undefined)) + if (all || matches[3] == "?" || (option.type != "boolean" && !value_given)) get = true; var reset = false; if (matches[3] == "&") reset = true; - var invert = false; + var invert_boolean = false; if (matches[1] == "inv" || matches[3] == "!") - invert = true; + invert_boolean = true; - var oper = matches[5]; + var operator = matches[5]; - var val = matches[6]; - if (val === undefined) - val = ""; + var value = matches[6]; + if (value === undefined) + value = ""; // reset a variable to its default value if (reset) { if (all) { - for (let opt in vimperator.options) - opt.reset(); + for (let option in vimperator.options) + option.reset(); } else { @@ -1728,68 +1730,116 @@ vimperator.Commands = function() //{{{ } } // write access + // NOTE: the behaviour is generally Vim compatible but could be + // improved. i.e. Vim's behaviour is pretty sloppy to no real + // benefit else { - var type = option.type; - if (type == "boolean") + var current_value = option.value; + var new_value; + + switch (option.type) { - if (matches[4]) - vimperator.echoerr("E474: Invalid argument: " + option.name + "=" + val); - else if (invert) - option.value = !option.value; - else - option.value = !no; - } - else if (type == "number") - { - var num = parseInt(val, 10); - if (isNaN(num)) - vimperator.echoerr("E521: Number required after =: " + option.name + "=" + val); - else - { - var cur_val = option.value; - if (oper == '+') num = cur_val + num; - if (oper == '-') num = cur_val - num; - // FIXME - if (option.validator != null && option.validator.call(this, num) == false) - vimperator.echoerr("E474: Invalid argument: " + option.name + "=" + val); // FIXME: need to be able to specify unique parse error messages - else // all checks passed, execute option handler - option.value = num; - } - } - else if (type == "charlist" || type == "stringlist" || type == "string") - { - var cur_val = option.value; - if (type == "charlist" || type == "string") - { - if (oper == '+' && !cur_val.match(val)) - val = cur_val + val; - if (oper == '-') val = cur_val.replace(val, ''); - } - else - { - if (oper == '+' && !cur_val.match(val) && cur_val.length > 0) - val = cur_val + ',' + val; - if (oper == '-') + case "boolean": + if (value_given) { - val = cur_val.replace(new RegExp(',?' + val), ''); - val = val.replace(/^,?/, ''); + vimperator.echoerr("E474: Invalid argument: " + args); + return; } - } - // FIXME - if (option.validator != null && option.validator.call(this, val) == false) - vimperator.echoerr("E474: Invalid argument: " + option.name + "=" + val); - else // all checks passed, execute option handler - option.value = val; + + if (invert_boolean) + new_value = !option.value; + else + new_value = !unset_boolean; + + break; + + case "number": + value = parseInt(value, 10); + + if (isNaN(value)) + { + vimperator.echoerr("E521: Number required after =: " + args); + return; + } + + if (operator == "+") + new_value = current_value + value; + else if (operator == "-") + new_value = current_value - value; + else if (operator == "^") + new_value = current_value * value; + else + new_value = value; + + break; + + case "charlist": + if (operator == "+") + new_value = current_value.replace(new RegExp("[" + value + "]", "g"), "") + value; + else if (operator == "-") + new_value = current_value.replace(value, ""); + else if (operator == "^") + // NOTE: Vim doesn't prepend if there's a match in the current value + new_value = value + current_value.replace(new RegExp("[" + value + "]", "g"), ""); + else + new_value = value; + + break; + + case "stringlist": + if (operator == "+") + { + if (!current_value.match(value)) + new_value = (current_value ? current_value + "," : "") + value; + else + new_value = current_value; + } + else if (operator == "-") + { + new_value = current_value.replace(new RegExp("^" + value + ",?|," + value), ""); + } + else if (operator == "^") + { + if (!current_value.match(value)) + new_value = value + (current_value ? "," : "") + current_value; + else + new_value = current_value; + } + else + { + new_value = value; + } + + break; + + case "string": + if (operator == "+") + new_value = current_value + value; + else if (operator == "-") + new_value = current_value.replace(value, ""); + else if (operator == "^") + new_value = value + current_value; + else + new_value = value; + + break; + + default: + vimperator.echoerr("E685: Internal error: option type `" + option.type + "' not supported"); } + + if (option.isValidValue(new_value)) + option.value = new_value; else - vimperator.echoerr("E685: Internal error: option format `" + type + "' not supported"); + // FIXME: need to be able to specify more specific errors + vimperator.echoerr("E474: Invalid argument: " + args); } }, { usage: ["se[t][!]", "se[t] {option}?", "se[t] [no]{option}", "se[t] {option}[+-]={value}", "se[t] {option}! | inv{option}", "se[t] {option}&"], short_help: "Set an option", - help: "Permanently change an option. In contrast to Vim options are currently stored throughout sessions.
" + + help: "Permanently change an option.
" + ":set without an argument shows all Vimperator options which differ from their default values.
" + ":set! without an argument shows all about:config preferences which differ from their default values.
" + "There are three types of options: boolean, number and string. " + @@ -1798,7 +1848,8 @@ vimperator.Commands = function() //{{{ ":set option! and :set invoption invert the value of a boolean option.
" + ":set option? or :set option(for string and list options) shows the current value of an option.
" + ":set option& resets an option to its default value.
" + - ":set option+={value} and :set option-={value} adds/subtracts {value} to a number option and append/remove {value} to a string option.
" + + ":set option+={value}, :set option^={value} and :set option-={value} " + + "adds/multiplies/subtracts {value} from a number option and appends/prepends/removes {value} from a string option.
" + ":set all shows the current value of all options and :set all& resets all options to their default values.
", completer: function(filter) { return vimperator.completion.get_options_completions(filter); } } diff --git a/content/options.js b/content/options.js index 3a813841..1e8feb8a 100644 --- a/content/options.js +++ b/content/options.js @@ -106,6 +106,14 @@ vimperator.Option = function(names, type, extra_info) //{{{ return false; } + this.isValidValue = function(value) + { + if (this.validator) + return this.validator(value); + else + return true; + } + this.reset = function() { this.value = this.default_value; @@ -461,8 +469,8 @@ vimperator.Options = function() //{{{ short_help: "Set the external text editor", help: "Sets the editor to run when <C-i> " + "is pressed in INSERT and TEXTAREA modes. Note that Vimperator will " + - "not behave correctly if the editor forks its own process, such as with "+ - "gvim without the -f argument.", + "not behave correctly if the editor forks its own process, such as with "+ + "gvim without the -f argument.", default_value: "gvim -f" } ));