penguin/AstroJS

Javascript framework for my blog

commit fcfe5d9b6028dfb04d09a031510dfd18ea9726cc

author斟酌 鵬兄 <tgckpg@gmail.com>
date2016-04-04T18:57:40Z
subjectAdded VISUAL LINE
commit fcfe5d9b6028dfb04d09a031510dfd18ea9726cc
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2016-04-04T18:57:40Z

    Added VISUAL LINE
---
 botanjs/src/Components/Vim/Actions/DELETE.js |   3 +-
 botanjs/src/Components/Vim/Actions/TO.js     |   8 +-
 botanjs/src/Components/Vim/Actions/VISUAL.js | 227 ++++++++++++++++++++++++---
 botanjs/src/Components/Vim/Actions/YANK.js   |   3 +-
 botanjs/src/Components/Vim/Controls.js       |   5 +-
 botanjs/src/Components/Vim/Cursor.js         |   3 +-
 botanjs/src/Components/Vim/VimArea.js        |   5 +
 7 files changed, 220 insertions(+), 34 deletions(-)

diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js
index 30cb180..10a09c9 100644
--- a/botanjs/src/Components/Vim/Actions/DELETE.js
+++ b/botanjs/src/Components/Vim/Actions/DELETE.js
@@ -33,7 +33,7 @@
 		this.__cursor.unsuppressEvent();
 	};
 
-	DELETE.prototype.handler = function( e, sp )
+	DELETE.prototype.handler = function( e, sp, newLine )
 	{
 		e.preventDefault();
 
@@ -46,7 +46,6 @@
 		var feeder = cur.feeder;
 
 		var Triggered = false;
-		var newLine = false;
 
 		if( sp == undefined )
 		{
diff --git a/botanjs/src/Components/Vim/Actions/TO.js b/botanjs/src/Components/Vim/Actions/TO.js
index 1ff0bcd..eb1e00e 100644
--- a/botanjs/src/Components/Vim/Actions/TO.js
+++ b/botanjs/src/Components/Vim/Actions/TO.js
@@ -40,9 +40,6 @@
 
 		var lowerLimmit = p;
 
-		var cX = cur.X;
-		var tX = cX;
-
 		var Char = et.key;
 		if( et.kMap( "Tab" ) )
 		{
@@ -55,15 +52,16 @@
 			return;
 		}
 
+		var tX = -1;
 		// Forward
 		if( em.kMap( "t" ) || em.kMap( "f" ) )
 		{
-			tX = f.content.indexOf( Char, p + cX + 1 );
+			tX = f.content.indexOf( Char, cur.aPos + 1 );
 		}
 		// backward
 		else
 		{
-			tX = f.content.lastIndexOf( Char, p + cX - 1 );
+			tX = f.content.lastIndexOf( Char, cur.aPos - 1 );
 		}
 
 		if( lowerLimmit <= tX && tX < upperLimit )
diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js
index bd9085d..b53b47d 100644
--- a/botanjs/src/Components/Vim/Actions/VISUAL.js
+++ b/botanjs/src/Components/Vim/Actions/VISUAL.js
@@ -11,12 +11,44 @@
 	/** @type {Components.Vim.IAction} */
 	var DELETE = ns[ NS_INVOKE ]( "DELETE" );
 
+	var MODE_NULL = -1;
+	var MODE_VISUAL = 0;
+	var MODE_LINE = 1;
+
+	// The offset of given line relative to content
+	var offsetY = function( cur, l )
+	{
+		if( l == 0 ) return 0;
+
+		var f = cur.feeder;
+
+		var j = 0;
+
+		var last = -1;
+		for( var i = f.content.indexOf( "\n" ); 0 <= i; i = f.content.indexOf( "\n", i + 1 ) )
+		{
+			last = i;
+			j ++;
+			if( l <= j ) break;
+		}
+
+		if( f.EOF ) i = last;
+
+		// "\n" compensation
+		var c = f.content[ i + 1 ];
+		if(!( c == undefined || c == "\n" ))
+		{
+			i ++;
+		}
+
+		return i;
+	};
+
 	/** @type {Components.Vim.IAction} */
 	var VISUAL = function( Cursor )
 	{
 		this.__reset( Cursor );
-		this.__msg = Mesg( "VISUAL" );
-
+		this.__msg = "";
 		Cursor.blink = false;
 		Cursor.pSpace = true;
 	};
@@ -25,9 +57,25 @@
 	{
 		/** @type {Components.Vim.Cursor} */
 		this.__cursor = Cursor;
-		this.__startaP = Cursor.aPos;
-		this.__start = Cursor.PStart;
-		this.__selStart = Cursor.PStart;
+
+		var s = {
+			lineNum: Cursor.getLine().lineNum
+			, X: Cursor.X
+			, aPos: Cursor.aPos
+			, pstart: Cursor.PStart
+		};
+
+		s.aStart = s.aPos - Cursor.aX;
+
+		Cursor.suppressEvent();
+		Cursor.lineEnd( true );
+
+		s.aEnd = Cursor.aPos;
+
+		Cursor.moveTo( s.aPos );
+		Cursor.unsuppressEvent();
+
+		this.__startLine = s;
 	};
 
 	VISUAL.prototype.allowMovement = true;
@@ -38,8 +86,7 @@
 
 		c.blink = true;
 		c.pSpace = false;
-		c.PStart = this.__selStart;
-		c.PEnd = this.__selStart + 1;
+		c.updatePosition();
 
 		// This fix the highlighting position of missing phantomSpace
 		// for maximum filled line
@@ -59,8 +106,11 @@
 		if( e.ModKeys ) return;
 
 		var cur = this.__cursor;
+		var feeder = cur.feeder;
 		var Action = null;
 
+		var dispatchUpdate = false;
+
 		if( e.kMap( "y" ) )
 		{
 			Action = new YANK( cur );
@@ -69,27 +119,73 @@
 		{
 			Action = new DELETE( cur );
 		}
+		else if( e.kMap( "V" ) )
+		{
+			if( this.__mode == MODE_LINE ) return true;
+			else
+			{
+				dispatchUpdate = true;
+				this.__mode = MODE_LINE;
+				this.__msg = Mesg( "VISLINE" );
+			}
+		}
+		else if( e.kMap( "v" ) )
+		{
+			if( this.__mode == MODE_VISUAL ) return true;
+			else
+			{
+				dispatchUpdate = true;
+				this.__mode = MODE_VISUAL;
+				this.__msg = Mesg( "VISUAL" );
+
+				cur.updatePosition();
+			}
+		}
+
+		if( dispatchUpdate )
+			feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
+
+		if( this.__mode == MODE_NULL )
+		{
+			debug.Error( new Error( "Mode is undefined" ) );
+			return true;
+		}
 
+		var startLine = this.__startLine;
 		if( Action )
 		{
 			cur.suppressEvent();
 
-			// Low-level cursor position adjustment
+			var lineMode = this.__mode == MODE_LINE;
+			if( lineMode )
+			{
+				if( startLine.aPos < cur.aPos )
+				{
+					cur.lineEnd( true );
+					startLine.aPos = startLine.aStart;
+				}
+				else
+				{
+					cur.lineStart();
+					startLine.aPos = startLine.aEnd;
+				}
+			}
+			// Cursor position adjustment
 			// this swap the cursor direction from LTR to RTL
 			// i.e. treat all delete as "e<----s" flow
 			// to keep the cursor position as the top on UNDO / REDO
-			if( Action.constructor == DELETE && this.__startaP < cur.aPos )
+			if( Action.constructor == DELETE && startLine.aPos < cur.aPos )
 			{
 				var o = cur.aPos;
-				cur.moveTo( this.__startaP, true );
-				this.__startaP = o;
+				cur.moveTo( startLine.aPos, true );
+				startLine.aPos = o;
 			}
 
-			Action.handler( e, this.__startaP );
+			Action.handler( e, startLine.aPos, lineMode );
 
 			if( Action.constructor != DELETE )
 			{
-				cur.moveTo( this.__startaP );
+				cur.moveTo( startLine.aPos );
 			}
 
 			this.__msg = Action.getMessage();
@@ -97,7 +193,7 @@
 			Action.dispose();
 			cur.unsuppressEvent();
 
-			this.__selStart = cur.PStart;
+			startLine.pstart = cur.PStart;
 
 			return true;
 		}
@@ -109,30 +205,119 @@
 
 				var r = e.range;
 
-				if( cur.aPos == this.__startaP )
+				if( cur.aPos == startLine.aPos )
 				{
 					cur.moveTo( r.open, true );
 					this.__reset( cur );
+					startLine = this.__startLine;
 				}
 
 				cur.unsuppressEvent();
 				cur.moveTo( r.close, true );
 			}
 
-			var prevPos = this.__start;
-			var newPos = cur.PStart;
+			var currAp = cur.aPos;
+
+			// Calculate the visible max min aPos of the current screen
+			var line = feeder.firstBuffer;
+			var firstLine = line.lineNum;
+			var minAp = offsetY( cur, firstLine );
+			var maxAp = offsetY( cur, firstLine + feeder.moreAt + 1 ) - 1;
+
+			debug.Info( "Min aPos: " + minAp, "Max aPos: " + maxAp );
+
+			var pstart = startLine.X;
+			var nstart = cur.PStart;
+
+			// highlight from the start
+			if( startLine.aPos < minAp )
+			{
+				pstart = 0;
+			}
+			// highlight from the end
+			else if( maxAp < startLine.aPos )
+			{
+				pstart = -2;
+				var i = 0;
+				do
+				{
+					if( line.placeholder ) break;
+					if( i <= feeder.moreAt )
+					{
+						pstart += line.toString().length + 1;
+					}
+					i ++;
+				}
+				while( line = line.next );
+			}
+			else
+			{
+				var l = startLine.lineNum;
+				if( this.__mode == MODE_LINE )
+				{
+					cur.suppressEvent();
+					pstart = 0;
+
+					if( currAp < startLine.aPos )
+					{
+						pstart = -1;
+						l ++;
+
+						cur.lineStart();
+						nstart = cur.PStart;
+					}
+					else if( startLine.aPos < currAp )
+					{
+						cur.lineEnd( true );
+						nstart = cur.PStart;
+					}
+					// aPos == currPos
+					else
+					{
+						cur.lineStart();
+						nstart = cur.PStart;
+						cur.lineEnd( true );
+						pstart = cur.PStart;
+						l = line.lineNum;
+					}
+
+					cur.moveTo( currAp, true );
+
+					cur.unsuppressEvent();
+				}
+				else if( this.__mode == MODE_VISUAL )
+				{
+					if( currAp == startLine.aPos ) return;
+				}
+
+				// Append the Y offset
+				var i = 0;
+				do
+				{
+					if( line.lineNum == l ) break;
+					pstart += line.toString().length + 1;
+				}
+				while( line = line.next );
+			}
+
+			var prevPos = pstart;
+			var newPos = nstart;
 
 			var posDiff = newPos - prevPos;
-			if( 0 <= posDiff )
+
+			var currAp = cur.aPos;
+
+			// Sets the visual position
+			// s-->e
+			if( 0 < posDiff )
 			{
-				this.__selStart = newPos;
 				newPos = newPos + 1;
 			}
+			// e<--s
 			else if( posDiff < 0 )
 			{
 				prevPos += posDiff;
-				newPos = this.__start + 1;
-				this.__selStart = prevPos;
+				newPos = pstart + 1;
 			}
 
 			cur.PStart = prevPos;
diff --git a/botanjs/src/Components/Vim/Actions/YANK.js b/botanjs/src/Components/Vim/Actions/YANK.js
index 86ba222..44e3c27 100644
--- a/botanjs/src/Components/Vim/Actions/YANK.js
+++ b/botanjs/src/Components/Vim/Actions/YANK.js
@@ -27,7 +27,7 @@
 		this.__cursor.unsuppressEvent();
 	};
 
-	YANK.prototype.handler = function( e, sp )
+	YANK.prototype.handler = function( e, sp, newLine )
 	{
 		e.preventDefault();
 
@@ -41,7 +41,6 @@
 
 		var Triggered = false;
 
-		var newLine = false;
 		if( sp == undefined )
 		{
 			Triggered = true;
diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js
index f38b847..aace42f 100644
--- a/botanjs/src/Components/Vim/Controls.js
+++ b/botanjs/src/Components/Vim/Controls.js
@@ -271,10 +271,9 @@
 				break;
 
 			case V: // Visual
-				ccur.openAction( "VISUAL" );
-				break;
 			case SHIFT + V: // Visual line
-				ccur.openAction( "VISUAL_LINE" );
+				ccur.openAction( "VISUAL" );
+				ccur.action.handler( e );
 				break;
 
 			case SHIFT + SEMI_COLON: // ":" Command line
diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js
index 3eaaac0..876614e 100644
--- a/botanjs/src/Components/Vim/Cursor.js
+++ b/botanjs/src/Components/Vim/Cursor.js
@@ -435,7 +435,7 @@
 		return null;
 	};
 
-	// The absX for current Line
+	// The position offset relative to current line
 	__readOnly( Cursor.prototype, "aX", function()
 	{
 		var X = this.X;
@@ -473,6 +473,7 @@
 				}
 			}
 		}
+		else return this.X;
 
 		return w;
 	} );
diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js
index 1817deb..f0899af 100644
--- a/botanjs/src/Components/Vim/VimArea.js
+++ b/botanjs/src/Components/Vim/VimArea.js
@@ -222,6 +222,11 @@
 			_self.select( cfeeder.cursor.position );
 		};
 
+		cfeeder.dispatcher.addEventListener( "SelectionChanged", function()
+		{
+			_self.select( cfeeder.cursor.position );
+		} );
+
 		cfeeder.dispatcher.addEventListener( "VisualUpdate", Update );
 		Update();