penguin/AstroJS

Javascript framework for my blog

commit f4dc0e7d9c584d8d1598faf8bdbba410758af7d8

author斟酌 鵬兄 <tgckpg@gmail.com>
date2016-04-02T21:23:26Z
subjectSome placeholder commands, working substitution
commit f4dc0e7d9c584d8d1598faf8bdbba410758af7d8
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2016-04-02T21:23:26Z

    Some placeholder commands, working substitution
---
 botanjs/src/Components/Vim/Actions/DELETE.js       |   2 +-
 .../src/Components/Vim/Actions/EDITOR_COMMAND.js   | 165 +++++++++++++
 botanjs/src/Components/Vim/Actions/FIND.js         |  63 +++--
 botanjs/src/Components/Vim/Actions/INSERT.js       |   2 +-
 botanjs/src/Components/Vim/Actions/PRINT_HEX.js    |   2 +-
 botanjs/src/Components/Vim/Actions/PUT.js          |   2 +-
 botanjs/src/Components/Vim/Actions/REDO.js         |   2 +-
 botanjs/src/Components/Vim/Actions/REPLACE.js      | 260 +++++++++++++++++++++
 botanjs/src/Components/Vim/Actions/UNDO.js         |   2 +-
 botanjs/src/Components/Vim/Actions/VISUAL.js       |   6 +-
 botanjs/src/Components/Vim/Actions/YANK.js         |   2 +-
 botanjs/src/Components/Vim/Controls.js             |  21 ++
 botanjs/src/Components/Vim/Cursor.js               |  12 +-
 botanjs/src/Components/Vim/Ex/Command.js           |   6 +-
 botanjs/src/Components/Vim/LineFeeder.js           |  62 ++---
 botanjs/src/Components/Vim/StatusBar.js            |   2 +
 botanjs/src/Components/Vim/VimArea.js              |   2 +-
 botanjs/src/Components/Vim/_this.js                |  14 +-
 botanjs/src/externs/Components.Vim.Actions.js      |   8 +
 19 files changed, 572 insertions(+), 63 deletions(-)

diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js
index 1683c32..30cb180 100644
--- a/botanjs/src/Components/Vim/Actions/DELETE.js
+++ b/botanjs/src/Components/Vim/Actions/DELETE.js
@@ -14,7 +14,7 @@
 
 	var occurence = __import( "System.utils.Perf.CountSubstr" );
 
-	/** @type {Components.Vim.Cursor.IAction} */
+	/** @type {Components.Vim.IAction} */
 	var DELETE = function( Cursor )
 	{
 		/** @type {Components.Vim.Cursor} */
diff --git a/botanjs/src/Components/Vim/Actions/EDITOR_COMMAND.js b/botanjs/src/Components/Vim/Actions/EDITOR_COMMAND.js
new file mode 100644
index 0000000..1b4450d
--- /dev/null
+++ b/botanjs/src/Components/Vim/Actions/EDITOR_COMMAND.js
@@ -0,0 +1,165 @@
+(function(){
+	var ns = __namespace( "Components.Vim.Actions" );
+
+	/** @type {System.Debug} */
+	var debug                                 = __import( "System.Debug" );
+
+	var VimError = __import( "Components.Vim.Error" );
+
+	var CMD_RANGE = 0;
+	var CMD_TYPE = 1;
+	var CMD_ARGS = 2;
+	var CMD_ERR = 3;
+
+	var ParseCommand = function( pattern )
+	{
+		var i = 1;
+
+		var range = "";
+		var out = [];
+
+		if( ".$%".indexOf( pattern[ i ] ) != -1 )
+		{
+			range = pattern[ i ++ ];
+		}
+		else
+		{
+			for( ; "0123456789".indexOf( pattern[ i ] ) != -1; i ++ )
+			{
+				range += pattern[ i ];
+			}
+		}
+
+		var command = "";
+
+		if( "/?".indexOf( pattern[ i ] ) != -1 )
+		{
+			command = pattern[ i ];
+		}
+		else
+		{
+			var cmdReg = /\w/g;
+			for( var j = pattern[ i ]; j = pattern[ i ]; i ++ )
+			{
+				if( j.match( cmdReg ) )
+				{
+					command += j;
+				}
+				else break;
+			}
+		}
+
+		var allowRange = false;
+		switch( command )
+		{
+			case "s":
+			case "su":
+			case "substitute":
+				allowRange = true;
+				out[ CMD_TYPE ] = "REPLACE";
+				break;
+			case "/":
+				allowRange = true;
+				out[ CMD_TYPE ] = "FIND";
+				break;
+
+			case "buffers":
+			case "ls":
+				out[ CMD_TYPE ] = "BUFFERS";
+
+				break;
+			case "w":
+			case "write":
+				out[ CMD_TYPE ] = "WRITE";
+				break;
+			case "q":
+			case "quit":
+				out[ CMD_TYPE ] = "QUIT";
+				break;
+			case "register":
+			case "registers":
+				out[ CMD_TYPE ] = "REGISTERS";
+				break;
+			case "ver":
+			case "version":
+				out[ CMD_TYPE ] = "VERSION";
+				break;
+			case "h":
+			case "help":
+				out[ CMD_TYPE ] = "HELP";
+				break;
+		}
+
+		if( range !== "" )
+		{
+			if( allowRange ) out[ CMD_RANGE ] = range;
+			else out[ CMD_ERR ] = VimError( "E481" );
+		}
+
+		out[ CMD_ARGS ] = pattern.slice( i );
+		return out;
+	};
+
+	/** @type {Components.Vim.IAction} */
+	var EDITOR_COMMAND = function( Cursor )
+	{
+		/** @type {Components.Vim.Cursor} */
+		this.__cursor = Cursor;
+		this.__msg = "";
+		Cursor.suppressEvent();
+	};
+
+	EDITOR_COMMAND.prototype.dispose = function()
+	{
+		this.__cursor.unsuppressEvent();
+	};
+
+	EDITOR_COMMAND.prototype.handler = function( e, p )
+	{
+		e.preventDefault();
+
+		var cmd = ParseCommand( p );
+
+		if( cmd[ CMD_ERR ] )
+		{
+			this.__msg = cmd[ CMD_ERR ];
+			return true;
+		}
+		else if( !cmd[ CMD_TYPE ] )
+		{
+			this.__msg = VimError( "E492", p.slice( 1 ).join( "" ) );
+			return true;
+		}
+
+		try
+		{
+			ns[ NS_INVOKE ]( cmd[ CMD_TYPE ] );
+		}
+		catch( ex )
+		{
+			this.__msg = VimError( "TODO", cmd[ CMD_TYPE ] );
+			return true;
+		}
+
+		try
+		{
+			this.__cursor.openRunAction(
+				cmd[ CMD_TYPE ], e, cmd[ CMD_ARGS ], cmd[ CMD_RANGE ]
+			);
+			this.__msg = this.__cursor.message;
+		}
+		catch( ex )
+		{
+			debug.Error( ex );
+		}
+
+		return true;
+	};
+
+	EDITOR_COMMAND.prototype.getMessage = function()
+	{
+		return this.__msg;
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "EDITOR_COMMAND", EDITOR_COMMAND );
+})();
diff --git a/botanjs/src/Components/Vim/Actions/FIND.js b/botanjs/src/Components/Vim/Actions/FIND.js
index c94f4f0..86db51d 100644
--- a/botanjs/src/Components/Vim/Actions/FIND.js
+++ b/botanjs/src/Components/Vim/Actions/FIND.js
@@ -4,10 +4,11 @@
 	/** @type {System.Debug} */
 	var debug                                 = __import( "System.Debug" );
 
+	var VimError = __import( "Components.Vim.Error" );
 	var Mesg = __import( "Components.Vim.Message" );
 
 	// Private static
-	var PATTERN = "";
+	var PATTERN = [];
 
 	var ParsePattern = function( pattern )
 	{
@@ -23,28 +24,27 @@
 					break;
 				case "\\":
 					var tok = pattern[ ++ i ];
-					debug.Error( "Unknown escaped token: " + tok );
-					++ i;
+					if( "nrts.[]()^".indexOf( tok ) != -1 )
+					{
+						parsed += "\\" + tok;
+					}
+					else
+					{
+						throw new Error( "Missing token impl: \"" + tok + "\"" );
+					}
+					break;
 				default:
 					parsed += pattern[ i ];
 			}
 		}
 
-		var RegEx = null;
-
-		try
-		{
-			var RegEx = new RegExp( parsed, "gm" );
-		}
-		catch( ex )
-		{
-			debug.Error( ex );
-		}
+		// The root bracket as back ref 0
+		var RegEx = new RegExp( "(" +  parsed + ")", "g" );
 
 		return RegEx;
 	};
 
-	/** @type {Components.Vim.Cursor.IAction} */
+	/** @type {Components.Vim.IAction} */
 	var FIND = function( Cursor )
 	{
 		/** @type {Components.Vim.Cursor} */
@@ -62,9 +62,37 @@
 	{
 		e.preventDefault();
 
-		if( p ) PATTERN = p;
+		if( p )
+		{
+			if( p.length < 2 )
+			{
+				if( PATTERN.length < 1 )
+				{
+					this.__msg = VimError( "E35" );
+					return true;
+				}
+				else p = PATTERN;
+			}
 
-		var search = ParsePattern( PATTERN );
+			PATTERN = p;
+		}
+
+		if( PATTERN.length < 1 )
+		{
+			this.__msg = VimError( "E35" );
+			return true;
+		}
+
+		var search;
+		try
+		{
+			search = ParsePattern( PATTERN );
+		}
+		catch( ex )
+		{
+			this.__msg = VimError( "EX1", ex.message );
+			return true;
+		}
 
 		var content = this.__cursor.feeder.content;
 
@@ -111,6 +139,7 @@
 
 		if( Hit == undefined )
 		{
+			this.__msg = VimError( "E486", PATTERN.slice( 1 ).join( "" ) );
 		}
 		else
 		{
@@ -123,5 +152,7 @@
 		return this.__msg;
 	};
 
+	__static_method( FIND, "Pattern", ParsePattern );
+
 	ns[ NS_EXPORT ]( EX_CLASS, "FIND", FIND );
 })();
diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js
index f119a30..759aec6 100644
--- a/botanjs/src/Components/Vim/Actions/INSERT.js
+++ b/botanjs/src/Components/Vim/Actions/INSERT.js
@@ -23,7 +23,7 @@
 		}
 	};
 
-	/** @type {Components.Vim.Cursor.IAction} */
+	/** @type {Components.Vim.IAction} */
 	var INSERT = function( Cursor )
 	{
 		/** @type {Components.Vim.Cursor} */
diff --git a/botanjs/src/Components/Vim/Actions/PRINT_HEX.js b/botanjs/src/Components/Vim/Actions/PRINT_HEX.js
index 185bb28..e55c77a 100644
--- a/botanjs/src/Components/Vim/Actions/PRINT_HEX.js
+++ b/botanjs/src/Components/Vim/Actions/PRINT_HEX.js
@@ -4,7 +4,7 @@
 	/** @type {System.Debug} */
 	var debug                                 = __import( "System.Debug" );
 
-	/** @type {Components.Vim.Cursor.IAction} */
+	/** @type {Components.Vim.IAction} */
 	var PRINT_HEX = function( Cursor )
 	{
 		/** @type {Components.Vim.Cursor} */
diff --git a/botanjs/src/Components/Vim/Actions/PUT.js b/botanjs/src/Components/Vim/Actions/PUT.js
index e58c9f9..3152444 100644
--- a/botanjs/src/Components/Vim/Actions/PUT.js
+++ b/botanjs/src/Components/Vim/Actions/PUT.js
@@ -9,7 +9,7 @@
 	var Mesg = __import( "Components.Vim.Message" );
 	var occurence = __import( "System.utils.Perf.CountSubstr" );
 
-	/** @type {Components.Vim.Cursor.IAction} */
+	/** @type {Components.Vim.IAction} */
 	var PUT = function( Cursor )
 	{
 		/** @type {Components.Vim.Cursor} */
diff --git a/botanjs/src/Components/Vim/Actions/REDO.js b/botanjs/src/Components/Vim/Actions/REDO.js
index da241d9..43ca88b 100644
--- a/botanjs/src/Components/Vim/Actions/REDO.js
+++ b/botanjs/src/Components/Vim/Actions/REDO.js
@@ -3,7 +3,7 @@
 
 	var Mesg = __import( "Components.Vim.Message" );
 
-	/** @type {Components.Vim.Cursor.IAction} */
+	/** @type {Components.Vim.IAction} */
 	var REDO = function( Cursor )
 	{
 		/** @type {Components.Vim.Cursor} */
diff --git a/botanjs/src/Components/Vim/Actions/REPLACE.js b/botanjs/src/Components/Vim/Actions/REPLACE.js
new file mode 100644
index 0000000..1acf895
--- /dev/null
+++ b/botanjs/src/Components/Vim/Actions/REPLACE.js
@@ -0,0 +1,260 @@
+(function(){
+	var ns = __namespace( "Components.Vim.Actions" );
+
+	/** @type {System.Debug} */
+	var debug                                 = __import( "System.Debug" );
+
+	/** @type {Components.Vim.State.Stack} */
+	var Stack                                  = __import( "Components.Vim.State.Stack" );
+
+	var VimError = __import( "Components.Vim.Error" );
+	var Mesg = __import( "Components.Vim.Message" );
+
+	var occurence = __import( "System.utils.Perf.CountSubstr" );
+
+	/** @type {Components.Vim.Actions.FIND} */
+	var FIND = ns[ NS_INVOKE ]( "FIND" );
+
+	var REPL_BEFORE = 0;
+	var REPL_OFFSET = 1;
+	var REPL_LENGTH = 2;
+
+	var ParseReplace = function( repl )
+	{
+		var parsed = "";
+		var l = repl.length;
+		var rStack = [ "" ]
+
+		for( var i = 1; i < l; i ++ )
+		{
+			switch( repl[ i ] )
+			{
+				case "^I":
+					parsed += "\t";
+					break;
+
+				case "\\":
+					i ++;
+
+					var j = repl[ i ];
+					if( "$nrt\\".indexOf( j ) != -1 )
+					{
+						parsed += JSON.parse( "\"\\" + j + "\"" );
+						break;
+					}
+
+					// 9 is shifted by 0
+					// which I think is more important
+					if( "012345678".indexOf( j ) != -1 )
+					{
+						rStack.push( parsed.length );
+						parsed += j;
+					}
+					else if( j == "9" )
+					{
+						throw new Error( "Back ref 9 is reserved for back ref 0" );
+					}
+					else
+					{
+						throw new Error( "Missing token impl: \"" + tok + "\"" );
+					}
+					break;
+
+				default:
+					parsed += repl[ i ];
+			}
+		}
+
+		rStack[0] = parsed;
+		return rStack;
+	};
+
+	/** @type {Components.Vim.IAction} */
+	var REPLACE = function( Cursor )
+	{
+		/** @type {Components.Vim.Cursor} */
+		this.__cursor = Cursor;
+		this.__msg = "";
+
+		this.__repl = "";
+		this.__replLen = 0;
+		this.__replCallback = this.__replCallback.bind( this );
+
+		this.__pOffset = 0;
+
+		this.__replacedGroups = [];
+
+		Cursor.suppressEvent();
+	};
+
+	REPLACE.prototype.dispose = function()
+	{
+		this.__cursor.unsuppressEvent();
+	};
+
+	REPLACE.prototype.handler = function( e, p, range )
+	{
+		e.preventDefault();
+
+		var search;
+		var spattern;
+
+		try
+		{
+			var slash = p.indexOf( "/", 0 );
+			var secSlash = p.indexOf( "/", slash + 1 );
+			if( slash == -1 )
+			{
+				this.__msg = VimError( "MISSING_FEATURE", "REPLACE %s" );
+				return true;
+			}
+			else if( secSlash == -1 )
+			{
+				search = FIND.Pattern( p );
+				spattern = p;
+			}
+			else
+			{
+				spattern = p.slice( slash, secSlash );
+				search = FIND.Pattern( spattern );
+
+				var thdSlash = p.indexOf( "/", secSlash + 1 );
+				if( thdSlash == -1 )
+				{
+					this.__repl = ParseReplace( p.slice( secSlash ) );
+				}
+				else
+				{
+					this.__repl = ParseReplace( p.slice( secSlash, thdSlash ) );
+				}
+
+				this.__replLen = this.__repl[0].length;
+			}
+		}
+		catch( ex )
+		{
+			this.__msg = VimError( "EX1", ex.message );
+			return true;
+		}
+
+		debug.Info( "Replace: " + search + ", [ " + this.__repl + " ]" );
+
+		var feeder = this.__cursor.feeder;
+		var content = feeder.content.slice( 0, -1 )
+			.replace( search, this.__replCallback ) + "\n";
+
+		if( !this.__replacedGroups.length )
+		{
+			this.__msg = VimError( "E486", spattern.join( "" ) );
+		}
+
+		feeder.content = content;
+
+		// Record this step for UNDO / REDO
+		this.__rec();
+
+		/* Move the cursor to last replaced line
+		var cur = this.__cursor;
+		var p = cur.aPos;
+		*/
+
+		feeder.pan();
+		feeder.softReset();
+
+		return true;
+	};
+
+	REPLACE.prototype.__replCallback = function()
+	{
+		var match = arguments[0];
+		var backRefs = Array.prototype.slice.call( arguments, 1, -2 );
+
+		var offset = this.__pOffset + arguments[ arguments.length - 2 ];
+
+		var replacedStr = "";
+		var replCand = this.__repl[0];
+
+		for( var i = 0; i < this.__replLen; i ++ )
+		{
+			var j = this.__repl.indexOf( i, 1 );
+			if( j == -1 )
+			{
+				replacedStr += replCand[ i ];
+			}
+			else
+			{
+				replacedStr += backRefs[ replCand[ this.__repl[ j ] ] ];
+			}
+		}
+
+		var rLen = replacedStr.length;
+		this.__pOffset += rLen - match.length;
+
+		var ReplObj = [];
+		ReplObj[ REPL_BEFORE ] = match;
+		ReplObj[ REPL_OFFSET ] = offset;
+		ReplObj[ REPL_LENGTH ] = rLen;
+
+		this.__replacedGroups.push( ReplObj );
+
+		return replacedStr;
+	};
+
+	REPLACE.prototype.__rec = function()
+	{
+		var stack = new Stack();
+
+		var reGroups = this.__replacedGroups;
+		var l = reGroups.length;
+		var cur = this.__cursor;
+		var feeder = cur.feeder;
+
+		stack.store( function()
+		{
+			var cont = feeder.content;
+			var newCont = "";
+			var st = 0;
+
+			var curStart = -1;
+			for( var i = 0; i < l; i ++ )
+			{
+				var grp = reGroups[ i ];
+
+				var RO = grp[ REPL_OFFSET ];
+				var RL = grp[ REPL_LENGTH ];
+				var RB = grp[ REPL_BEFORE ];
+
+				var NRL = RB.length;
+				newCont += cont.substring( st, RO ) + RB;
+
+				st = grp[ REPL_OFFSET ] + RL;
+
+				grp[ REPL_BEFORE ] = cont.substr( RO, RL );
+
+				grp[ REPL_OFFSET ] = newCont.length - NRL;
+				grp[ REPL_LENGTH ] = NRL;
+
+				if( curStart == -1 )
+				{
+					curStart = RO;
+				}
+			}
+
+			newCont += cont.substring( st );
+
+			feeder.content = newCont;
+			cur.moveTo( curStart );
+			feeder.pan();
+
+		} );
+
+		cur.rec.record( stack );
+	};
+
+	REPLACE.prototype.getMessage = function()
+	{
+		return this.__msg;
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "REPLACE", REPLACE );
+})();
diff --git a/botanjs/src/Components/Vim/Actions/UNDO.js b/botanjs/src/Components/Vim/Actions/UNDO.js
index 0e47a00..6f81f52 100644
--- a/botanjs/src/Components/Vim/Actions/UNDO.js
+++ b/botanjs/src/Components/Vim/Actions/UNDO.js
@@ -3,7 +3,7 @@
 
 	var Mesg = __import( "Components.Vim.Message" );
 
-	/** @type {Components.Vim.Cursor.IAction} */
+	/** @type {Components.Vim.IAction} */
 	var UNDO = function( Cursor )
 	{
 		/** @type {Components.Vim.Cursor} */
diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js
index 6d32caa..bd9085d 100644
--- a/botanjs/src/Components/Vim/Actions/VISUAL.js
+++ b/botanjs/src/Components/Vim/Actions/VISUAL.js
@@ -6,12 +6,12 @@
 
 	var Mesg = __import( "Components.Vim.Message" );
 
-	/** @type {Components.Vim.Cursor.IAction} */
+	/** @type {Components.Vim.IAction} */
 	var YANK = ns[ NS_INVOKE ]( "YANK" );
-	/** @type {Components.Vim.Cursor.IAction} */
+	/** @type {Components.Vim.IAction} */
 	var DELETE = ns[ NS_INVOKE ]( "DELETE" );
 
-	/** @type {Components.Vim.Cursor.IAction} */
+	/** @type {Components.Vim.IAction} */
 	var VISUAL = function( Cursor )
 	{
 		this.__reset( Cursor );
diff --git a/botanjs/src/Components/Vim/Actions/YANK.js b/botanjs/src/Components/Vim/Actions/YANK.js
index 12f7a5c..86ba222 100644
--- a/botanjs/src/Components/Vim/Actions/YANK.js
+++ b/botanjs/src/Components/Vim/Actions/YANK.js
@@ -9,7 +9,7 @@
 
 	var occurence = __import( "System.utils.Perf.CountSubstr" );
 
-	/** @type {Components.Vim.Cursor.IAction} */
+	/** @type {Components.Vim.IAction} */
 	var YANK = function( Cursor )
 	{
 		/** @type {Components.Vim.Cursor} */
diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js
index 19c76bd..b52b7c2 100644
--- a/botanjs/src/Components/Vim/Controls.js
+++ b/botanjs/src/Components/Vim/Controls.js
@@ -27,6 +27,8 @@
 	var _0 = 48; var _1 = 49; var _2 = 50; var _3 = 51; var _4 = 52;
 	var _5 = 53; var _6 = 54; var _7 = 55; var _8 = 56; var _9 = 57;
 
+	var SEMI_COLON = 59;
+
 	var A = 65; var B = 66; var C = 67; var D = 68; var E = 69;
 	var F = 70; var G = 71; var H = 72; var I = 73; var J = 74;
 	var K = 75; var L = 76; var M = 77; var N = 78; var O = 79;
@@ -203,6 +205,14 @@
 				ccur.openAction( "INSERT" );
 				break;
 
+			case S: // Delete Char and start insert
+				if( ccur.getLine().content != "" )
+				{
+					ccur.openRunAction( "DELETE", e, ccur.aPos );
+				}
+				ccur.openAction( "INSERT" );
+				break;
+
 			case SHIFT + O: // new line before insert
 				ccur.lineStart();
 				ccur.openAction( "INSERT" );
@@ -263,6 +273,11 @@
 				ccur.openAction( "VISUAL_LINE" );
 				break;
 
+			case SHIFT + SEMI_COLON: // ":" Command line
+				this.__divedCCmd = new ExCommand( ccur, ":" );
+				this.__divedCCmd.handler( e );
+				break;
+
 			case F1: // F1, help
 				break;
 			default:
@@ -564,6 +579,12 @@
 		{
 			this.__e = e;
 
+			// KeyCode HotFix
+			if( e.key == ";" || e.key == ":" )
+			{
+				SEMI_COLON = e.keyCode;
+			}
+
 			var c = this.__e.keyCode;
 
 			this.__escape = c == ESC || ( e.ctrlKey && c == C );
diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js
index bd1967d..2fd9833 100644
--- a/botanjs/src/Components/Vim/Cursor.js
+++ b/botanjs/src/Components/Vim/Cursor.js
@@ -42,6 +42,13 @@
 		var l = c.Y + d;
 		var i = c.Y;
 
+		if( !line )
+		{
+			line = c.feeder.firstBuffer;
+			i = 0;
+			l = d;
+		}
+
 		// First line ( visual ) does not count
 		if( line != c.feeder.firstBuffer ) i --;
 
@@ -396,11 +403,12 @@
 	};
 
 	// Open, Run, then close an action
-	Cursor.prototype.openRunAction = function( name, e, arg1 )
+	Cursor.prototype.openRunAction = function( name, e, arg1, arg2, arg3, arg4, arg5 )
 	{
+		debug.Info( "OpenRunAction: " + name );
 		/** @type {Components.Vim.IAction} */
 		var action = new (Actions[ name ])( this );
-		action.handler( e, arg1 );
+		action.handler( e, arg1, arg2, arg3, arg4, arg5 );
 		this.__pulseMsg = action.getMessage();
 		action.dispose();
 
diff --git a/botanjs/src/Components/Vim/Ex/Command.js b/botanjs/src/Components/Vim/Ex/Command.js
index d3bfe05..45fdb74 100644
--- a/botanjs/src/Components/Vim/Ex/Command.js
+++ b/botanjs/src/Components/Vim/Ex/Command.js
@@ -19,7 +19,7 @@
 		, ":" : Perf.uuid
 	};
 
-	/** @type {Components.Vim.Cursor.IAction} */
+	/** @type {Components.Vim.IAction} */
 	var Command = function( Cursor, Mode )
 	{
 		var _self = this;
@@ -216,9 +216,13 @@
 			case "/":
 				action = "FIND";
 				break;
+			case ":":
+				action = "EDITOR_COMMAND";
+				break;
 		}
 
 		var cur = this.__cursor;
+
 		cur.suppressEvent();
 		this.__cursor.openRunAction( action, e, this.__command.slice() );
 		cur.unsuppressEvent();
diff --git a/botanjs/src/Components/Vim/LineFeeder.js b/botanjs/src/Components/Vim/LineFeeder.js
index 2ead9a0..ee9f881 100644
--- a/botanjs/src/Components/Vim/LineFeeder.js
+++ b/botanjs/src/Components/Vim/LineFeeder.js
@@ -67,46 +67,46 @@
 			:  function( line ) { return !line.placeholder; }
 			;
 
-		this.__render = function( line, steps )
-		{
-			var display = ( line == undefined ? "" : line ) + "";
+			this.__render = function( line, steps )
+			{
+				var display = ( line == undefined ? "" : line ) + "";
 
-			var atSpace = false;
+				var atSpace = false;
 
-			for( var i = 0;
-				line && i < steps && ( line = line.next ) && placeholdCond( line );
-				i ++ )
-			{
-				if( atSpace || ( line.br && steps < ( i + line.visualLines.length ) ) )
+				for( var i = 0;
+						line && i < steps && ( line = line.next ) && placeholdCond( line );
+						i ++ )
 				{
-					if( !atSpace ) _self.__clseLine = line;
-					atSpace = true;
-					display += "\n@";
-					continue;
+					if( atSpace || ( line.br && steps < ( i + line.visualLines.length ) ) )
+					{
+						if( !atSpace ) _self.__clseLine = line;
+						atSpace = true;
+						display += "\n@";
+						continue;
+					}
+
+					display += "\n" + line;
 				}
 
-				display += "\n" + line;
-			}
-
-			return display;
-		};
+				return display;
+			};
 
-		this.__softRender = function()
-		{
-			var line = _self.lineBuffers[ _self.__rStart ];
-			var steps = _self.__rLength + 1;
-
-			for( var i = 0;
-				line && i < steps && ( line = line.next ) && placeholdCond( line );
-				i ++ )
+			this.__softRender = function()
 			{
-				if( line.br && steps < ( i + line.visualLines.length ) )
+				var line = _self.lineBuffers[ _self.__rStart ];
+				var steps = _self.__rLength + 1;
+
+				for( var i = 0;
+						line && i < steps && ( line = line.next ) && placeholdCond( line );
+						i ++ )
 				{
-					_self.__clseLine = line;
-					break;
+					if( line.br && steps < ( i + line.visualLines.length ) )
+					{
+						_self.__clseLine = line;
+						break;
+					}
 				}
-			}
-		};
+			};
 	}
 
 	Feeder.prototype.render = function( start, length )
diff --git a/botanjs/src/Components/Vim/StatusBar.js b/botanjs/src/Components/Vim/StatusBar.js
index 81af357..c524b12 100644
--- a/botanjs/src/Components/Vim/StatusBar.js
+++ b/botanjs/src/Components/Vim/StatusBar.js
@@ -36,6 +36,8 @@
 				text = text();
 				if( text == undefined || text === "" ) continue;
 
+				if( i == 0 && l <= text.length ) return text;
+
 				display += text.substr( 0, avail );
 				i = display.length;
 			}
diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js
index ee76302..f00d1d2 100644
--- a/botanjs/src/Components/Vim/VimArea.js
+++ b/botanjs/src/Components/Vim/VimArea.js
@@ -125,7 +125,7 @@
 
 			var sLine = sfeeder.linesOccupied;
 			element.value =
-				cfeeder.render( 0, r - sLine )
+				cfeeder.render( sLine - 1, r - sLine )
 				+ "\n" + sfeeder.render( 0, sLine < r ? sLine : r );
 
 			_self.__blink = false;
diff --git a/botanjs/src/Components/Vim/_this.js b/botanjs/src/Components/Vim/_this.js
index 345a882..f97d14a 100644
--- a/botanjs/src/Components/Vim/_this.js
+++ b/botanjs/src/Components/Vim/_this.js
@@ -24,10 +24,20 @@
 
 		, "SEARCH_HIT_BOTTOM": "search hit BOTTOM, continuing at TOP"
 		, "SEARCH_HIT_TOP": "search hit TOP, continuing at BOTTOM"
+		, "REPLACE": "%1 substitution(s) on %2 line(s)"
 	};
 
 	var errors = {
-		"E486": "E486: Pattern not found: %1"
+		"E35": "No previous regular expression"
+		, "E481": "No range allowed"
+		, "E492": "Not an editor command: %1"
+		, "E486": "Pattern not found: %1"
+
+		// EXtended Errors
+		, "EX1": "Pattern Error( %1 )"
+
+		, "TODO": "%1 is not implemented yet"
+		, "MISSING_FEATURE": "Sorry, I thought this command wasn't useful enough to implement. Please file a feature request titled \"Implement %1\" in github if you think this is important."
 	};
 
 	var GetString = function( arr, key, restArgs )
@@ -50,7 +60,7 @@
 	var Error = function( key )
 	{
 		var restArgs = Array.prototype.slice.call( arguments, 1 );
-		return GetString( errors, key, restArgs );
+		return key + ": " + GetString( errors, key, restArgs );
 	};
 
 	var bAudio = new Audio(
diff --git a/botanjs/src/externs/Components.Vim.Actions.js b/botanjs/src/externs/Components.Vim.Actions.js
new file mode 100644
index 0000000..af2f963
--- /dev/null
+++ b/botanjs/src/externs/Components.Vim.Actions.js
@@ -0,0 +1,8 @@
+/** @type Object */
+Components.Vim.Actions = {};
+
+/** @type constructor */
+Components.Vim.Actions.FIND = function(){};
+
+/** @type Function */
+Components.Vim.Actions.FIND.Pattern;