penguin/AstroJS

Javascript framework for my blog

commit bc3c511c6fdc3b61ea7216ab31c427f59e12504c

author斟酌 鵬兄 <tgckpg@gmail.com>
date2016-03-16T10:55:18Z
subjectFix wrong position of lines with tab character
commit bc3c511c6fdc3b61ea7216ab31c427f59e12504c
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2016-03-16T10:55:18Z

    Fix wrong position of lines with tab character
---
 botanjs/src/Components/Vim/Actions/INSERT.js       | 100 +++++++++++++++------
 botanjs/src/Components/Vim/Actions/UNDO.js         |  41 +++++++++
 botanjs/src/Components/Vim/Controls.js             |  12 ++-
 botanjs/src/Components/Vim/Cursor.js               |  58 ++++++++++--
 botanjs/src/Components/Vim/LineFeeder.js           |   2 +-
 botanjs/src/Components/Vim/State/Recorder.js       |  41 +++++++++
 botanjs/src/Components/Vim/State/Stack.js          |  19 ++++
 botanjs/src/Components/Vim/StatusBar.js            |   2 +-
 botanjs/src/Components/Vim/_this.js                |   2 +
 botanjs/src/externs/Components.Vim.Cursor.js       |   4 +
 .../src/externs/Components.Vim.State.Recorder.js   |   9 ++
 botanjs/src/externs/Components.Vim.State.Stack.js  |   7 ++
 botanjs/src/externs/Components.Vim.State.js        |   2 +
 13 files changed, 263 insertions(+), 36 deletions(-)

diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js
index e131c88..e082baf 100644
--- a/botanjs/src/Components/Vim/Actions/INSERT.js
+++ b/botanjs/src/Components/Vim/Actions/INSERT.js
@@ -1,10 +1,11 @@
 (function(){
 	var ns = __namespace( "Components.Vim.Actions" );
 
-	/** @type {System.Debug} */
-	var debug = __import( "System.Debug" );
 	var Mesg = __import( "Components.Vim.Message" );
 
+	/** @type {Components.Vim.State.Stack} */
+	var Stack = __import( "Components.Vim.State.Stack" );
+
 	var Translate = function( c )
 	{
 		switch( c )
@@ -16,15 +17,75 @@
 		}
 	};
 
+	/* @param {Components.Vim.LineFeeder} */
+	var ContentPosition = function( f )
+	{
+		var line = f.cursor.getLine();
+		var n = line.lineNum;
+
+		var p = 0;
+		if( 0 < n )
+		{
+			p = f.content.indexOf( "\n" );
+			for( i = 1; p != -1 && i < n; i ++ )
+			{
+				p = f.content.indexOf( "\n", p + 1 );
+			}
+
+			if( f.wrap )
+			{
+				// wordwrap offset
+				p ++;
+			}
+		}
+
+		p += f.cursor.aX;
+		return p;
+	};
+
 	/** @type {Components.Vim.Cursor.IAction} */
 	var INSERT = function( Cursor )
 	{
 		/** @type {Components.Vim.Cursor} */
-		this.cursor = Cursor;
+		this.__cursor = Cursor;
+
+		// Initialize this stack
+		this.__rec( "", true );
 	};
 
 	INSERT.prototype.dispose = function()
 	{
+
+	};
+
+	INSERT.prototype.__storeState = function( c, pos )
+	{
+		return function() {
+			debug.Inf( pos, c );
+		};
+	};
+
+	INSERT.prototype.__rec = function( c, newRec )
+	{
+		if( newRec || !this.__stack )
+		{
+			if( this.__stack )
+			{
+				var c = this.__content;
+
+				this.__stack.store(
+					this.__storeState( c, this.__startPosition )
+				);
+
+				this.__cursor.rec.store( this.__stack );
+			}
+
+			this.__content = "";
+			this.__stack = new Stack();
+			this.__startPosition = ContentPosition( this.__cursor.feeder );
+		}
+
+		this.__content += c;
 	};
 
 	INSERT.prototype.handler = function( e )
@@ -34,42 +95,27 @@
 
 		if( inputChar.length != 1 ) return;
 
-		var cur = this.cursor;
+		var cur = this.__cursor;
 		var feeder = cur.feeder;
 
-		var line = cur.getLine();
-		var n = line.lineNum;
-
-		var cont = feeder.content;
+		var f = ContentPosition( feeder );
 
-		var f = 0;
-		if( 0 < n )
-		{
-			f = cont.indexOf( "\n" );
-			for( i = 1; f != -1 && i < n; i ++ )
-			{
-				f = cont.indexOf( "\n", f + 1 );
-			}
+		feeder.content =
+			feeder.content.substring( 0, f )
+			+ inputChar
+			+ feeder.content.substring( f );
 
-			if( this.cursor.feeder.wrap )
-			{
-				// wordwrap offset
-				f ++;
-			}
-		}
-
-		f += cur.aX;
-
-		feeder.content = cont.substring( 0, f ) + inputChar +  cont.substring( f );
 		feeder.pan();
 		feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
 
+		this.__rec( inputChar );
+
 		cur.moveX( 1 );
 	};
 
 	INSERT.prototype.getMessage = function()
 	{
-		var l = this.cursor.feeder.firstBuffer.cols;
+		var l = this.__cursor.feeder.firstBuffer.cols;
 		var msg = Mesg( "INSERT" );
 
 		for( var i = msg.length; i < l; i ++ ) msg += " ";
diff --git a/botanjs/src/Components/Vim/Actions/UNDO.js b/botanjs/src/Components/Vim/Actions/UNDO.js
new file mode 100644
index 0000000..d7a76f8
--- /dev/null
+++ b/botanjs/src/Components/Vim/Actions/UNDO.js
@@ -0,0 +1,41 @@
+(function(){
+	var ns = __namespace( "Components.Vim.Actions" );
+
+	var Mesg = __import( "Components.Vim.Message" );
+
+	/** @type {Components.Vim.Cursor.IAction} */
+	var UNDO = function( Cursor )
+	{
+		/** @type {Components.Vim.Cursor} */
+		this.__cursor = Cursor;
+		this.__message = "UNDO COMMAND";
+	};
+
+	UNDO.prototype.dispose = function()
+	{
+
+	};
+
+	UNDO.prototype.handler = function( e )
+	{
+		e.preventDefault();
+
+		/** @type {Components.Vim.State.Stack} */
+		var stack = this.__cursor.rec.undo();
+		if( stack )
+		{
+			stack.play();
+		}
+		else
+		{
+			this.__message = Mesg( "UNDO_LIMIT" );
+		}
+	};
+
+	UNDO.prototype.getMessage = function()
+	{
+		return this.__message;
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "UNDO", UNDO );
+})();
diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js
index 33629ea..7e13dc6 100644
--- a/botanjs/src/Components/Vim/Controls.js
+++ b/botanjs/src/Components/Vim/Controls.js
@@ -62,10 +62,18 @@
 			case 65: // a
 				cfeeder.cursor.openAction( "INSERT" );
 				break;
-
+			case 73: // i
+				break;
+			case 85: // u, undo
+				cfeeder.cursor.openRunAction( "UNDO", e );
+				break;
+			case 88: // x, del
+				break;
 			case 1065: // A, append at the line end
 				break;
-			case 73: // i
+			case 1088: // X, delete before
+				break;
+			case 1085: // U, undo previous changes in oneline
 				break;
 			case 1073: // I, append before the line start, after spaces
 				break;
diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js
index b73c4ed..abb5f1b 100644
--- a/botanjs/src/Components/Vim/Cursor.js
+++ b/botanjs/src/Components/Vim/Cursor.js
@@ -4,6 +4,9 @@
 	/** @type {System.Debug} */
 	var debug = __import( "System.Debug" );
 
+	/** @type {Components.Vim.State.Recorder} */
+	var Recorder = __import( "Components.Vim.State.Recorder" );
+
 	var Actions = __import( "Components.Vim.Actions.*" );
 
 	var GetLine = function( buffs, l )
@@ -71,6 +74,9 @@
 		// The resulting position
 		this.P = 0;
 
+		// State recorder
+		this.rec = new Recorder();
+
 		this.action = null;
 	};
 
@@ -210,6 +216,7 @@
 	{
 		if( this.action ) this.action.dispose();
 		this.action = new (Actions[ name ])( this );
+		this.__pulseMsg = null;
 
 		this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
 	};
@@ -219,6 +226,18 @@
 		if( !this.action ) return;
 		this.action.dispose();
 		this.action = null;
+		this.__pulseMsg = null;
+
+		this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
+	};
+
+	Cursor.prototype.openRunAction = function( name, e )
+	{
+		/** @type {Components.Vim.IAction} */
+		var action = new (Actions[ name ])( this );
+		action.handler( e );
+		this.__pulseMsg = action.getMessage();
+		action.dispose();
 
 		this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
 	};
@@ -244,23 +263,52 @@
 		var X = this.X;
 		var f = this.feeder;
 
+		var w = 1;
+
 		// Calculate wordwrap offset
 		if( f.wrap )
 		{
-			var cols = f.firstBuffer.cols + 1;
-			var w = X < cols ? 0 : Math.floor( X / cols );
+			var lines = this.getLine().visualLines;
 
-			if( 0 < w )
+			for( var i in lines )
 			{
-				X -= w;
+				/** @type {Components.Vim.LineBuffer} */
+				var vline = lines[ i ];
+
+				// Actual length
+				var aLen = vline.content.toString().length;
+
+				// Visual length
+				var vLen = vline.toString().length;
+
+				// Plus the "\n" character
+				X -= vLen + 1;
+
+				if( 0 <= X )
+				{
+					w += aLen;
+				}
+				else if( X < 0 )
+				{
+					w += X + vLen;
+					break;
+				}
 			}
 		}
 
-		return X;
+		return w;
 	} );
 
 	__readOnly( Cursor.prototype, "message", function()
 	{
+		if( this.__pulseMsg )
+		{
+			var m = this.__pulseMsg;
+			this.__pulseMsg = null;
+
+			return m;
+		}
+
 		return this.action && this.action.getMessage();
 	} );
 
diff --git a/botanjs/src/Components/Vim/LineFeeder.js b/botanjs/src/Components/Vim/LineFeeder.js
index 059fd04..58258b4 100644
--- a/botanjs/src/Components/Vim/LineFeeder.js
+++ b/botanjs/src/Components/Vim/LineFeeder.js
@@ -195,7 +195,7 @@
 	} );
 
 	__readOnly( Feeder.prototype, "lineStat", function() {
-		var X = this.cursor.aX;
+		var X = this.cursor.aX + 1;
 		var line = this.cursor.getLine();
 		var tabStat = "";
 
diff --git a/botanjs/src/Components/Vim/State/Recorder.js b/botanjs/src/Components/Vim/State/Recorder.js
new file mode 100644
index 0000000..e6cfe6b
--- /dev/null
+++ b/botanjs/src/Components/Vim/State/Recorder.js
@@ -0,0 +1,41 @@
+(function(){
+	var ns = __namespace( "Components.Vim.State" );
+
+	var Recorder = function()
+	{
+		this.__steps = [];
+		this.__i = 0;
+	};
+
+	Recorder.prototype.undo = function()
+	{
+		var i = this.__i - 1;
+		if( i == -1 || !this.__steps.length ) return null;
+
+
+		return this.__steps[ this.__i = i ];
+	};
+
+	Recorder.prototype.redo = function()
+	{
+		var i = this.__i + 1;
+		if( i == -1 || !this.__steps.length ) return null;
+
+		var State = this.__steps[ i ];
+		if( State )
+		{
+			this.__i = i;
+			return State;
+		}
+
+		return null;
+	};
+
+	Recorder.prototype.record = function( StateObj )
+	{
+		this.__steps[ this.__i ] = StateObj;
+		delete this.__steps[ ++ this.__i ];
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "Recorder", Recorder );
+})();
diff --git a/botanjs/src/Components/Vim/State/Stack.js b/botanjs/src/Components/Vim/State/Stack.js
new file mode 100644
index 0000000..a58e99d
--- /dev/null
+++ b/botanjs/src/Components/Vim/State/Stack.js
@@ -0,0 +1,19 @@
+(function(){
+	var ns = __namespace( "Components.Vim.State" );
+
+	var Stack = function()
+	{
+	};
+
+	Stack.prototype.store = function( handler )
+	{
+		this.__handler = handler;
+	};
+
+	Stack.prototype.play = function()
+	{
+		if( this.__handler ) this.__handler();
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "Stack", Stack );
+})();
diff --git a/botanjs/src/Components/Vim/StatusBar.js b/botanjs/src/Components/Vim/StatusBar.js
index c02f82c..1a57d5c 100644
--- a/botanjs/src/Components/Vim/StatusBar.js
+++ b/botanjs/src/Components/Vim/StatusBar.js
@@ -32,7 +32,7 @@
 				if( text == undefined || text === "" ) continue;
 
 				display += text.substr( 0, avail );
-				i = display.length - 1;
+				i = display.length;
 			}
 			else display += " ";
 		}
diff --git a/botanjs/src/Components/Vim/_this.js b/botanjs/src/Components/Vim/_this.js
index 0ed8cd0..b568ced 100644
--- a/botanjs/src/Components/Vim/_this.js
+++ b/botanjs/src/Components/Vim/_this.js
@@ -13,6 +13,8 @@
 		, "BOTTOM": "Bot"
 		, "ALL": "All"
 		, "EXIT": "Type :quit<Enter>  to exit Vim"
+
+		, "UNDO_LIMIT": "Already at oldest change"
 	};
 
 	var errors = {
diff --git a/botanjs/src/externs/Components.Vim.Cursor.js b/botanjs/src/externs/Components.Vim.Cursor.js
index 4564825..13b5624 100644
--- a/botanjs/src/externs/Components.Vim.Cursor.js
+++ b/botanjs/src/externs/Components.Vim.Cursor.js
@@ -5,6 +5,8 @@ Components.Vim.Cursor = function(){};
 Components.Vim.Cursor.feeder;
 /** @type {Components.Vim.IAction} */
 Components.Vim.Cursor.action;
+/** @type {Components.Vim.State.Recorder} */
+Components.Vim.Cursor.rec;
 
 /** @type Function */
 Components.Vim.Cursor.moveX;
@@ -19,6 +21,8 @@ Components.Vim.Cursor.updatePosition;
 /** @type Function */
 Components.Vim.Cursor.openAction;
 /** @type Function */
+Components.Vim.Cursor.openRunAction;
+/** @type Function */
 Components.Vim.Cursor.closeAction;
 
 /** @type {Array} */
diff --git a/botanjs/src/externs/Components.Vim.State.Recorder.js b/botanjs/src/externs/Components.Vim.State.Recorder.js
new file mode 100644
index 0000000..0303938
--- /dev/null
+++ b/botanjs/src/externs/Components.Vim.State.Recorder.js
@@ -0,0 +1,9 @@
+/** @constructor */
+Components.Vim.State.Recorder = function(){};
+
+/** @type Function */
+Components.Vim.State.undo;
+/** @type Function */
+Components.Vim.State.redo;
+/** @type Function */
+Components.Vim.State.record;
diff --git a/botanjs/src/externs/Components.Vim.State.Stack.js b/botanjs/src/externs/Components.Vim.State.Stack.js
new file mode 100644
index 0000000..c572081
--- /dev/null
+++ b/botanjs/src/externs/Components.Vim.State.Stack.js
@@ -0,0 +1,7 @@
+/** @constructor */
+Components.Vim.State.Stack = function(){};
+
+/** @type Function */
+Components.Vim.State.Stack.play;
+/** @type Function */
+Components.Vim.State.Stack.store;
diff --git a/botanjs/src/externs/Components.Vim.State.js b/botanjs/src/externs/Components.Vim.State.js
new file mode 100644
index 0000000..884e6dc
--- /dev/null
+++ b/botanjs/src/externs/Components.Vim.State.js
@@ -0,0 +1,2 @@
+/** @object */
+Components.Vim.State = {};