diff --git a/NEWS b/NEWS index 4e5b0f59..1231c7ee 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,10 @@
 2007-XX-XX:
 	* version 0.5.2
+	* statusline is now white on black with bold font by default (like in (g)vim)
+	  (change with userChrome.css if you don't like it until we have :colorscheme)
+	* :let mapleader=","  and  in :map support
+	* added new :let and :unlet commands
 	* separated search and Ex command history
 	* added 'visualbellstyle' for styling/hiding the visual bell
 	* merge the existing status bar with the standard FF status bar so that
diff --git a/chrome/content/vimperator/commands.js b/chrome/content/vimperator/commands.js
index 0f868897..ae40247e 100644
--- a/chrome/content/vimperator/commands.js
+++ b/chrome/content/vimperator/commands.js
@@ -604,6 +604,77 @@ function Commands() //{{{
                   "The special version :javascript! will open the JavaScript console of Firefox."
         }
     ));
+    addDefaultCommand(new Command(["let"],
+        function(args)
+        {
+            if (!args)
+            {
+                return;
+                // List all defined variables
+            }
+
+            var match;
+            // 1 - type, 2 - name, 3 - +-., 4 - expr
+            if (match = args.match(/([$@&])?([\w:]+)\s*([+-.])?=\s*(.+)/))
+            {
+                if (!match[1])
+                {
+                    var reference = vimperator.variableReference(match[2]);
+                    if (!reference[0] && match[3])
+                        return vimperator.echoerr("E121: Undefined variable: " + match[2]);
+
+                    var expr = vimperator.eval(match[4]);
+                    if (typeof expr === undefined)
+                        return vimperator.echoerr("E15: Invalid expression: " + match[4]);
+                    else
+                    {
+                        if (!reference[0]) {
+                            if (reference[2] == 'g')
+                                reference[0] = vimperator.globalVariables;
+                            else
+                                return; // for now
+                        }
+
+                        if (match[3])
+                        {
+                            if (match[3] == '+')
+                                reference[0][reference[1]] += expr;
+                            else if (match[3] == '-')
+                                reference[0][reference[1]] -= expr;
+                            else if (match[3] == '.')
+                                reference[0][reference[1]] += expr.toString();
+                        }
+                        else
+                            reference[0][reference[1]] = expr;
+                    }
+                }
+            }
+            // 1 - name
+            else if (match = args.match(/^\s*([\w:]+)\s*$/))
+            {
+                var reference = vimperator.variableReference(match[1]);
+                if (!reference[0])
+                    return vimperator.echoerr("E121: Undefined variable: " + match[1]);
+
+                var value = reference[0][reference[1]];
+                if (typeof value == 'number')
+                    var prefix = '#';
+                else if (typeof value == 'function')
+                    var prefix = '*';
+                else
+                    var prefix = '';
+                vimperator.echo(reference[1] + '\t\t' + prefix + value);
+            }
+        },
+        {
+            usage: ["let {var-name} [+-.]= {expr1}", "let {var-name}", "let"],
+            short_help: "Sets or lists a variable",
+            help: "Sets the variable {var-name} " +
+                  "to the value of the expression {expr1}." +
+                  "If no expression is given, the value of the variable is displayed." +
+                  "Without arguments, displays a list of all variables."
+        }
+    ));
     addDefaultCommand(new Command(["map"],
         // 0 args -> list all maps
         // 1 arg  -> list the maps starting with args
@@ -618,6 +689,15 @@ function Commands() //{{{
 
             var matches = args.match(/^([^\s]+)(?:\s+(.+))?$/)
             var [lhs, rhs] = [matches[1], matches[2]];
+            var leader_reg = new RegExp('', 'i');
+
+            if (leader_reg.test(lhs))
+            {
+                var leader_ref = vimperator.variableReference('mapleader');
+                var leader = leader_ref[0] ? leader_ref[0][leader_ref[1]] : '\\';
+
+                lhs = lhs.replace(leader_reg, leader);
+            }
 
             if (rhs)
             {
@@ -1338,6 +1418,35 @@ function Commands() //{{{
             help: "If a count is given, don't close the last but the [count]th last tab."
         }
     ));
+    addDefaultCommand(new Command(["unl[et]"],
+        function(args, special)
+        {
+            if (!args)
+                return vimperator.echoerr("E471: Argument required");
+
+            var names = args.split(/ /);
+            if (typeof names == 'string') names = [names];
+            var length = names.length;
+            for (var i = 0, name = names[i]; i < length; name = names[++i])
+            {
+                var reference = vimperator.variableReference(name);
+                if (!reference[0])
+                {
+                    if (!special)
+                        vimperator.echoerr("E108: No such variable: " + name);
+                    return;
+                }
+
+                delete reference[0][reference[1]];
+            }
+        },
+        {
+            usage: ["unl[et][!] {name} ..."],
+            short_help: "Deletes a variable.",
+            help: "Deletes the variable {name}." +
+                  "Several variable names can be given."
+        }
+    ));
     addDefaultCommand(new Command(["unm[ap]"],
         function(args)
         {
diff --git a/chrome/content/vimperator/vimperator.js b/chrome/content/vimperator/vimperator.js
index 6aa84e52..ed1d9a0a 100644
--- a/chrome/content/vimperator/vimperator.js
+++ b/chrome/content/vimperator/vimperator.js
@@ -359,6 +359,78 @@ const vimperator = (function() //{{{
             return new LocalFile(path, mode, perms, tmp);
         },
 
+        // partial sixth level expression evaluation
+        eval: function(string)
+        {
+            string = string.toString().replace(/^\s*/, "").replace(/\s*$/, "");
+            var match = string.match(/^&(\w+)/);
+            if (match)
+            {
+                var opt = this.options.get(match[1]);
+                if (!opt)
+                {
+                    this.echoerr("E113: Unknown option: " + match[1]);
+                    return;
+                }
+                var type = opt.type;
+                var value = opt.getter();
+                if (type != "boolean" && type != "number")
+                    value = value.toString();
+                return value;
+            }
+
+            // String
+            else if (match = string.match(/^(['"])([^\1]*?[^\\]?)\1/))
+            {
+                if (match)
+                    return match[2].toString();
+                else
+                {
+                    this.echoerr("E115: Missing quote: " + string);
+                    return;
+                }
+            }
+
+            // Number
+            else if (match = string.match(/^(\d+)$/))
+            {
+                return parseInt(match[1]);
+            }
+
+            var reference = this.variableReference(string);
+            if (!reference[0])
+                this.echoerr("E121: Undefined variable: " + string);
+            else
+                return reference[0][reference[1]];
+
+            return;
+        },
+
+        variableReference: function(string)
+        {
+            if (!string)
+                return [null, null, null];
+
+            if (match = string.match(/^([bwtglsv]):(\w+)/)) // Variable
+            {
+                // Other variables should be implemented
+                if (match[1] == "g")
+                {
+                    if (match[2] in this.globalVariables)
+                        return [this.globalVariables, match[2], match[1]];
+                    else
+                        return [null, match[2], match[1]];
+                }
+            }
+            else // Global variable
+            {
+                if (string in this.globalVariables)
+                    return [this.globalVariables, string, "g"];
+                else
+                    return [null, string, "g"];
+            }
+        },
+
         // logs a message to the javascript error console
         log: function(msg, level)
         {
@@ -383,10 +455,10 @@ const vimperator = (function() //{{{
                 }
                 catch (e)
                 {
-                    value = '';
+                    value = "";
                 }
 
-                string += i + ': ' + value + '\n';
+                string += i + ": " + value + "\n";
             }
             vimperator.log(string, level);
         },
@@ -623,6 +695,8 @@ const vimperator = (function() //{{{
             vimperator.echo    = vimperator.commandline.echo;
             vimperator.echoerr = vimperator.commandline.echoErr;
 
+            vimperator.globalVariables = {};
+
             // TODO: move elsewhere
             vimperator.registerCallback("submit", vimperator.modes.EX, function(command) { vimperator.execute(command); } );
             vimperator.registerCallback("complete", vimperator.modes.EX, function(str) { return vimperator.completion.exTabCompletion(str); } );