From 553cab97763cde3b3c54a324772d17aab3f42ca2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= <tgckpg@gmail.com>
Date: Thu, 31 Mar 2016 02:25:39 +0800
Subject: [PATCH] Visual viw, viw{bracket}, Ctrl+f, Ctcl+b

---
 botanjs/src/Components/Vim/Actions/DELETE.js  | 21 +++--
 botanjs/src/Components/Vim/Actions/VISUAL.js  | 12 +--
 botanjs/src/Components/Vim/Controls.js        | 65 +++++++++----
 botanjs/src/Components/Vim/Cursor.js          |  4 +-
 botanjs/src/Components/Vim/LineFeeder.js      |  4 +-
 botanjs/src/Components/Vim/Syntax/Analyzer.js | 92 ++++++++++++++-----
 .../externs/Components.Vim.Syntax.Analyzer.js |  2 +
 7 files changed, 139 insertions(+), 61 deletions(-)

diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js
index 922f703f..b509fb55 100644
--- a/botanjs/src/Components/Vim/Actions/DELETE.js
+++ b/botanjs/src/Components/Vim/Actions/DELETE.js
@@ -41,19 +41,22 @@
 		var feeder = cur.feeder;
 
 		var Triggered = false;
-		if( sp == undefined && this.__startX != cur.aPos )
+
+		if( sp == undefined )
 		{
-			Triggered = true;
-
-			if( e.kMap( "l" ) )
+			if( this.__startX != cur.aPos )
 			{
-				cur.moveX( -1 );
+				Triggered = true;
+
+				if( e.kMap( "l" ) )
+				{
+					cur.moveX( -1 );
+				}
+
+				sp = this.__startX;
 			}
-
-			sp = this.__startX;
+			else return;
 		}
-		else if( sp == undefined ) return;
-
 
 		var c = feeder.content;
 
diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js
index 1574d959..7cac7892 100644
--- a/botanjs/src/Components/Vim/Actions/VISUAL.js
+++ b/botanjs/src/Components/Vim/Actions/VISUAL.js
@@ -27,7 +27,6 @@
 		/** @type {Components.Vim.Cursor} */
 		this.__cursor = Cursor;
 		this.__startaP = Cursor.aPos;
-		this.__startP = { x: Cursor.X, y: Cursor.Y, p: Cursor.P };
 		this.__start = Cursor.PStart;
 		this.__selStart = Cursor.PStart;
 	};
@@ -71,10 +70,9 @@
 			// to keep the cursor position as the top on UNDO / REDO
 			if( Action.constructor == DELETE && this.__startaP < cur.aPos )
 			{
-				this.__startaP = cur.aPos;
-				cur.X = this.__startP.x;
-				cur.Y = this.__startP.y;
-				cur.P = this.__startP.p;
+				var o = cur.aPos;
+				cur.moveTo( this.__startaP, true );
+				this.__startaP = o;
 			}
 
 			Action.handler( e, this.__startaP );
@@ -97,12 +95,12 @@
 
 				if( cur.aPos == this.__startaP )
 				{
-					cur.moveX( r.open - this.__startaP );
+					cur.moveTo( r.open, true );
 					this.__reset( cur );
 				}
 
 				cur.unsuppressEvent();
-				cur.moveX( r.close - cur.aPos );
+				cur.moveTo( r.close, true );
 			}
 
 			var prevPos = this.__start;
diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js
index f3180600..950ddbc9 100644
--- a/botanjs/src/Components/Vim/Controls.js
+++ b/botanjs/src/Components/Vim/Controls.js
@@ -257,6 +257,8 @@
 		}
 
 		var ccur = this.__ccur;
+		var vima = this.__vimArea;
+		var cfeeder = ccur.feeder;
 
 		var cursorHandled = true;
 		switch( kCode )
@@ -267,6 +269,29 @@
 			case K: this.__cMoveY( -1 ); break; // Up
 			case J: this.__cMoveY( 1 ); break; // Down
 
+			case CTRL + F: // Page Down
+				if( cfeeder.firstBuffer.next.placeholder )
+				{
+					beep();
+					break;
+				}
+
+				var oPan = cfeeder.panY;
+				cfeeder.pan( undefined, vima.rows - 1 );
+				ccur.moveY( -ccur.Y );
+
+				break;
+			case CTRL + B: // Page Up
+				if( cfeeder.panY == 0 )
+				{
+					beep();
+					break;
+				}
+				cfeeder.pan( undefined, -vima.rows + 1 );
+				ccur.moveY( -ccur.Y );
+				if( !cfeeder.EOF ) ccur.moveY( cfeeder.moreAt );
+				break;
+
 			case SHIFT + H: // First line buffer
 				break;
 			case SHIFT + L: // Last line buffer
@@ -313,29 +338,33 @@
 				var analyzer = this.__vimArea.contentAnalyzer;
 
 				this.__cMovement = true;
+
 				// Word boundary
 				this.__comp( e, function( e2 ) {
 					var WordMatch = analyzer.wordAt( ccur.aPos );
-
-					debug.Info( "Word: "
-						+ ccur.feeder.content.substring( WordMatch.open, WordMatch.close + 1 )
-					);
-
 					e2.__range = WordMatch;
 				}, W );
-				this.__comp( e, function(){
-					debug.Info( "Bracket boundary [" );
-				}, S_BRACKET_L );
-				this.__comp( e, function(){
-					debug.Info( "Bracket boundary ]" );
-				}, S_BRACKET_R );
-				this.__comp( e, function(){
-					debug.Info( "Bracket boundary {" );
-				}, SHIFT + S_BRACKET_L );
-				this.__comp( e, function(){
-					debug.Info( "Bracket boundary }" );
-					analyzer.bracketAt( ccur.aPos );
-				}, SHIFT + S_BRACKET_R );
+
+				var bracket = function( e2 ) {
+					var BracketMatch = analyzer.bracketIn( "(", ccur.aPos );
+					e2.__range = BracketMatch;
+				};
+				var curlyBracket = function( e2 ) {
+					var BracketMatch = analyzer.bracketIn( "{", ccur.aPos );
+					e2.__range = BracketMatch;
+				};
+				var squareBracket = function( e2 ) {
+					var BracketMatch = analyzer.bracketIn( "[", ccur.aPos );
+					e2.__range = BracketMatch;
+				};
+
+				// Bracket boundaries
+				this.__comp( e, bracket , SHIFT + _0 );
+				this.__comp( e, bracket, SHIFT + _9 );
+				this.__comp( e, squareBracket, S_BRACKET_L );
+				this.__comp( e, squareBracket, S_BRACKET_R );
+				this.__comp( e, curlyBracket, SHIFT + S_BRACKET_L );
+				this.__comp( e, curlyBracket, SHIFT + S_BRACKET_R );
 				break;
 
 			case G: // Go to top
diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js
index c39080fc..47b10ad7 100644
--- a/botanjs/src/Components/Vim/Cursor.js
+++ b/botanjs/src/Components/Vim/Cursor.js
@@ -89,7 +89,7 @@
 	Cursor.prototype.Vim;
 
 	// Move to an absolute position
-	Cursor.prototype.moveTo = function( aPos )
+	Cursor.prototype.moveTo = function( aPos, phantomSpace )
 	{
 		var content = this.feeder.content;
 		var lastLineNum = this.getLine().lineNum;
@@ -115,7 +115,7 @@
 		if( 0 < this.getLine().lineNum && lineStart <= aPos ) jumpX --;
 
 		this.moveX( - Number.MAX_VALUE );
-		this.moveX( jumpX );
+		this.moveX( jumpX, false, phantomSpace );
 
 	};
 
diff --git a/botanjs/src/Components/Vim/LineFeeder.js b/botanjs/src/Components/Vim/LineFeeder.js
index e50f4acd..b27779f1 100644
--- a/botanjs/src/Components/Vim/LineFeeder.js
+++ b/botanjs/src/Components/Vim/LineFeeder.js
@@ -149,7 +149,9 @@
 				var a = this.content.indexOf( "\n", f + 1 );
 				if( a == -1 )
 				{
-					Y = i;
+					Y = i - 1;
+					// -2 to compensate the last "\n" content placeholder
+					f -= 2;
 					break;
 				}
 				f = a;
diff --git a/botanjs/src/Components/Vim/Syntax/Analyzer.js b/botanjs/src/Components/Vim/Syntax/Analyzer.js
index cb816cff..131ec754 100644
--- a/botanjs/src/Components/Vim/Syntax/Analyzer.js
+++ b/botanjs/src/Components/Vim/Syntax/Analyzer.js
@@ -117,6 +117,24 @@
 	};
 	/* End Private Class }}}*/
 
+	var SetParent = function( BracketPairs, pair )
+	{
+		if( !pair ) throw new Error( "Parent not found" );
+
+		var tMatch = new TokenMatch();
+		tMatch.__level = pair[ TOK_LEVEL ];
+		tMatch.__open = pair[ TOK_OPEN ];
+		tMatch.__close = pair[ TOK_CLOSED ];
+
+		if( -1 < pair[ TOK_PARENT ] )
+		{
+			var rPair = BracketPairs.find( pair[ TOK_PARENT ] );
+			tMatch.__parent = SetParent( BracketPairs, rPair );
+		}
+
+		return tMatch;
+	};
+
 	var Analyzer = function( feeder )
 	{
 		/* @type {Components.Vim.LineFeeder} */
@@ -136,17 +154,17 @@
 		{
 			case "{": tokState = TOK_OPEN;
 			case "}":
-					  BracketPairs = this.GetPairs( TOK_JOIN( "{", "}" ) );
+					  BracketPairs = this.__getPairs( TOK_JOIN( "{", "}" ) );
 				break;
 
 			case "[": tokState = TOK_OPEN;
 			case "]":
-				BracketPairs = this.GetPairs( TOK_JOIN( "[", "]" ) );
+				BracketPairs = this.__getPairs( TOK_JOIN( "[", "]" ) );
 				break;
 
 			case "(": tokState = TOK_OPEN;
 			case ")":
-				BracketPairs = this.GetPairs( TOK_JOIN( "(", ")" ) );
+				BracketPairs = this.__getPairs( TOK_JOIN( "(", ")" ) );
 				break;
 
 			case "/":
@@ -186,39 +204,65 @@
 		{
 			case "/*": tokState = TOK_OPEN;
 			case "*/":
-				BracketPairs = this.GetPairs( TOK_JOIN( "/*", "*/" ) );
+				BracketPairs = this.__getPairs( TOK_JOIN( "/*", "*/" ) );
 				break;
 
 			default:
 				return new TokenMatch();
 		}
 
-		var SetParent = function( pair )
-		{
-			if( !pair ) throw new Error( "Parent not found" );
-
-			var tMatch = new TokenMatch();
-			tMatch.__level = pair[ TOK_LEVEL ];
-			tMatch.__open = pair[ TOK_OPEN ];
-			tMatch.__close = pair[ TOK_CLOSED ];
-
-			if( -1 < pair[ TOK_PARENT ] )
-			{
-				var rPair = BracketPairs.find( pair[ TOK_PARENT ] );
-				tMatch.__parent = SetParent( rPair );
-			}
-
-			return tMatch;
-		};
-
 		var rPair = BracketPairs.find( p, tokState );
-		var tMatch = SetParent( rPair )
+		var tMatch = SetParent( BracketPairs, rPair )
 		tMatch.__selected = p;
 
 		return tMatch;
 	};
 
-	Analyzer.prototype.GetPairs = function( def, reload )
+	Analyzer.prototype.bracketIn = function( b, p )
+	{
+		var bro = "{[(";
+		var brc = "}])";
+
+		var i = bro.indexOf( b );
+		if( i < 0 ) i = brc.indexOf( b );
+		if( i < 0 ) throw new Error( "Unsupported bracket: " + b );
+
+		var tokPairs = this.__getPairs( TOK_JOIN( bro[i], brc[i] ) );
+		var pairs = tokPairs.__pairs;
+
+		var l = pairs.length;
+
+		var highest = null;
+
+		// Find the range of highest level
+		for( var i = 0; i < l; i ++ )
+		{
+			var pair = pairs[ i ];
+
+			if( pair[ TOK_OPEN ] <= p && p <= pair[ TOK_CLOSED ] )
+			{
+				if( ( highest && highest[ TOK_LEVEL ] < pair[ TOK_LEVEL ] ) || !highest )
+				{
+					highest = pair;
+				}
+			}
+
+		}
+
+		var tMatch = SetParent( tokPairs, highest );
+		var oMatch = tMatch;
+
+		do {
+			oMatch.__open ++;
+			oMatch.__close --;
+		} while( oMatch = oMatch.parent )
+
+		if( highest ) return tMatch;
+
+		return new TokenMatch();
+	};
+
+	Analyzer.prototype.__getPairs = function( def, reload )
 	{
 		if( !reload && this.__tokpairs[ def ] )
 		{
diff --git a/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js b/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js
index 6be9e808..47a3b3b5 100644
--- a/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js
+++ b/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js
@@ -4,6 +4,8 @@ Components.Vim.Syntax.Analyzer = function(){};
 /** @type Function */
 Components.Vim.Syntax.Analyzer.bracketAt;
 /** @type Function */
+Components.Vim.Syntax.Analyzer.bracketIn;
+/** @type Function */
 Components.Vim.Syntax.Analyzer.wordAt;
 /** @type Function */
 Components.Vim.Syntax.Analyzer.quoteAt;