penguin/AstroJS

Javascript framework for my blog

commit 1511c709573047aedbf219cea0866747217378c7

author斟酌 鵬兄 <tgckpg@gmail.com>
date2017-03-17T05:34:31Z
subjectAdded :{number} support
commit 1511c709573047aedbf219cea0866747217378c7
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2017-03-17T05:34:31Z

    Added :{number} support
---
 .../src/Components/Vim/Actions/EDITOR_COMMAND.js   | 10 +++
 botanjs/src/Components/Vim/Actions/MARKS.js        | 83 ++++++++++++++++++++++
 botanjs/src/Components/Vim/Actions/PRINT.js        | 48 +++++++++++++
 botanjs/src/Components/Vim/Controls.js             | 35 ++++++---
 botanjs/src/Components/Vim/Cursor.js               | 81 +++++++++++++++------
 botanjs/src/Components/Vim/LineFeeder.js           | 40 +++++++++--
 botanjs/src/Components/Vim/State/Marks.js          | 31 ++++++++
 botanjs/src/Components/Vim/VimArea.js              |  3 +
 botanjs/src/externs/Components.Vim.Cursor.js       |  2 +
 botanjs/src/externs/Components.Vim.LineFeeder.js   |  2 +
 botanjs/src/externs/Components.Vim.State.Marks.js  | 10 +++
 botanjs/src/externs/Components.Vim.VimArea.js      |  5 ++
 12 files changed, 313 insertions(+), 37 deletions(-)

diff --git a/botanjs/src/Components/Vim/Actions/EDITOR_COMMAND.js b/botanjs/src/Components/Vim/Actions/EDITOR_COMMAND.js
index 7b2a6f1..78ff532 100644
--- a/botanjs/src/Components/Vim/Actions/EDITOR_COMMAND.js
+++ b/botanjs/src/Components/Vim/Actions/EDITOR_COMMAND.js
@@ -79,6 +79,9 @@
 			case "registers":
 				out[ CMD_TYPE ] = "REGISTERS";
 				break;
+			case "marks":
+				out[ CMD_TYPE ] = "MARKS";
+				break;
 			case "ver":
 			case "version":
 				out[ CMD_TYPE ] = "VERSION";
@@ -90,6 +93,13 @@
 			case "varec":
 				out[ CMD_TYPE ] = "VA_REC";
 				break;
+
+			case "": // Range jumping
+				pattern.push( true );
+			case "p":
+				allowRange = true;
+				out[ CMD_TYPE ] = "PRINT";
+				break;
 		}
 
 		if( range !== "" )
diff --git a/botanjs/src/Components/Vim/Actions/MARKS.js b/botanjs/src/Components/Vim/Actions/MARKS.js
new file mode 100644
index 0000000..4ffe01f
--- /dev/null
+++ b/botanjs/src/Components/Vim/Actions/MARKS.js
@@ -0,0 +1,83 @@
+(function(){
+	var ns = __namespace( "Components.Vim.Actions" );
+
+	/** @type {System.Debug} */
+	var debug                                 = __import( "System.Debug" );
+
+	var VimError = __import( "Components.Vim.Error" );
+	var Mesg = __import( "Components.Vim.Message" );
+
+	/** @type {System.Debug} */
+	var Marks = __import( "Components.Vim.State.Marks" );
+	var Keys = Marks.Keys;
+
+	/** @type {Components.Vim.IAction} */
+	var MARKS = function( Cursor )
+	{
+		/** @type {Components.Vim.Cursor} */
+		this.__cursor = Cursor;
+		this.__msg = "";
+		Cursor.suppressEvent();
+	};
+
+	MARKS.prototype.dispose = function()
+	{
+		this.__cursor.unsuppressEvent();
+	};
+
+	MARKS.prototype.handler = function( e, p )
+	{
+		e.preventDefault();
+
+		/** @type {Components.Vim.State.Marks} */
+		var marks = e.target.marks;
+
+		var msg = ":marks";
+
+		/**
+		 * Regarding to marks 0-9, from Vim docs
+		 *   Numbered marks '0 to '9 are quite different.  They can not be set directly.
+		 *   They are only present when using a viminfo file viminfo-file.  Basically '0
+		 *   is the location of the cursor when you last exited Vim, '1 the last but one
+		 *   time, etc.  Use the "r" flag in 'viminfo' to specify files for which no
+		 *   Numbered mark should be stored.  See viminfo-file-marks.
+		 * TODO: Need to redefine marks 0-9
+		 **/
+
+		// Fuck this, use silly paddings
+		msg += "\nmark line  col file/text";
+
+		var feeder = this.__cursor.feeder;
+		for( var i = 0, j = Keys[ i ]; j != undefined; i ++, j = Keys[ i ] )
+		{
+			var r = marks.get( j );
+			if( !r ) continue;
+
+			var line = ( r[0] + 1 ) + "";
+			var col = ( r[1] + 1 ) + "";
+			var t = feeder.line( r[0] - 1 ).replace( /^[\t ]+/, "" );
+
+			var ll = 4 - line.length;
+			for( var il = 0; il < ll; il ++ ) line = " " + line;
+
+			var ll = 3 - col.length;
+			for( var il = 0; il < ll; il ++ ) col = " " + col;
+
+			msg += "\n " + j + "   " + line + "  " + col + " " + t;
+		}
+
+		var lastLine = Mesg( "WAIT_FOR_INPUT" );
+
+		var l = this.__cursor.feeder.firstBuffer.cols;
+		for( var i = msg.length; i < l; i ++ ) msg += " ";
+
+		this.__msg = msg + "\n" + lastLine;
+	};
+
+	MARKS.prototype.getMessage = function()
+	{
+		return this.__msg;
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "MARKS", MARKS );
+})();
diff --git a/botanjs/src/Components/Vim/Actions/PRINT.js b/botanjs/src/Components/Vim/Actions/PRINT.js
new file mode 100644
index 0000000..7e8a9ed
--- /dev/null
+++ b/botanjs/src/Components/Vim/Actions/PRINT.js
@@ -0,0 +1,48 @@
+(function(){
+	var ns = __namespace( "Components.Vim.Actions" );
+
+	/** @type {System.Debug} */
+	var debug                                 = __import( "System.Debug" );
+
+	/** @type {Components.Vim.IAction} */
+	var PRINT = function( Cursor )
+	{
+		/** @type {Components.Vim.Cursor} */
+		this.__cursor = Cursor;
+		this.__msg = "";
+	};
+
+	PRINT.prototype.dispose = function() { };
+	PRINT.prototype.handler = function( e, args, range )
+	{
+		e.preventDefault();
+
+		if( args[0] === true )
+		{
+			switch( range )
+			{
+				case "%":
+				case "$":
+					this.__cursor.moveY( Number.MAX_VALUE );
+					return;
+				case ".":
+					this.__cursor.lineStart( true );
+					break;
+				case "":
+				default:
+					var lineNum = parseInt( range ) - 1;
+					if( lineNum )
+					{
+						this.__cursor.gotoLine( 0 < lineNum ? lineNum : 0 );
+					}
+			}
+		}
+	};
+
+	PRINT.prototype.getMessage = function()
+	{
+		return this.__msg;
+	};
+
+	ns[ NS_EXPORT ]( EX_CLASS, "PRINT", PRINT );
+})();
diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js
index 20654f1..7057be3 100644
--- a/botanjs/src/Components/Vim/Controls.js
+++ b/botanjs/src/Components/Vim/Controls.js
@@ -478,16 +478,17 @@
 	{
 		var kCode = e.keyCode;
 
-		if( this.__cMovement )
+		if( this.__captureComp )
 		{
 			if( !e.ModKeys )
 			{
 				this.__composite( e );
-				this.__cMovement = false;
+				this.__captureComp = false;
 				return true;
 			}
 		}
 
+		var inst = this.__vimArea;
 		var ccur = this.__ccur;
 		var cfeeder = ccur.feeder;
 
@@ -610,10 +611,22 @@
 
 				break;
 
+			case M:
+				this.__captureComp = true;
+
+				var marks = this.__vimArea.marks;
+				this.__composite( e, function( e2 ) {
+					var line = ccur.getLine().lineNum;
+					if( !marks.set( e2.key, line, ccur.aX ) )
+					{
+						beep();
+					}
+				}, ANY_KEY );
+				break;
 
 			case SHIFT + T: // To
 			case T: // To
-				this.__cMovement = true;
+				this.__captureComp = true;
 
 				this.__composite( e, function( e2 ) {
 					var oX = ccur.X;
@@ -632,7 +645,7 @@
 				break;
 			case SHIFT + F: // To
 			case F: // To
-				this.__cMovement = true;
+				this.__captureComp = true;
 
 				this.__composite( e, function( e2 ) {
 					ccur.openRunAction( "TO", e, e2 );
@@ -659,7 +672,7 @@
 
 				var analyzer = this.__vimArea.contentAnalyzer;
 
-				this.__cMovement = true;
+				this.__captureComp = true;
 
 				// Word boundary
 				this.__composite( e, function( e2 ) {
@@ -703,7 +716,7 @@
 
 			case G:
 
-				this.__cMovement = true;
+				this.__captureComp = true;
 
 				// Go to top
 				this.__composite( e, function() {
@@ -775,7 +788,7 @@
 				break;
 
 			case SLASH: // "/" Search movement
-				this.__cMovement = true;
+				this.__captureComp = true;
 
 				this.__divedCCmd = new ExCommand( ccur, "/" );
 				this.__divedCCmd.handler( e );
@@ -788,7 +801,7 @@
 					break;
 				}
 
-				this.__cMovement = true;
+				this.__captureComp = true;
 
 				var exCmd = new ExCommand( ccur, ":" );
 				exCmd.handler( e );
@@ -827,7 +840,7 @@
 		if( e.Escape )
 		{
 			var b = false;
-			this.__cMovement = false;
+			this.__captureComp = false;
 
 			if( this.__compositeReg )
 			{
@@ -853,7 +866,7 @@
 			if( this.__divedCCmd.handler( e ) )
 			{
 				this.__divedCCmd.dispose();
-				this.__cMovement = false;
+				this.__captureComp = false;
 				this.__divedCCmd = null;
 				return;
 			}
@@ -864,7 +877,7 @@
 		var cfeeder = this.__cfeeder;
 		var ccur = this.__ccur;
 
-		if( !this.__cMovement && ( !ccur.action || ccur.action.allowMovement ) )
+		if( !this.__captureComp && ( !ccur.action || ccur.action.allowMovement ) )
 		{
 			this.__modCommand( e );
 			if( e.canceled ) return;
diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js
index ab3d28b..c1d7ceb 100644
--- a/botanjs/src/Components/Vim/Cursor.js
+++ b/botanjs/src/Components/Vim/Cursor.js
@@ -106,6 +106,8 @@
 	// Move to an absolute position
 	Cursor.prototype.moveTo = function( aPos, phantomSpace, skipTabs )
 	{
+		this.__suppressUpdate();
+
 		var content = this.feeder.content;
 		var pline = this.getLine();
 		var lastLineNum = pline.lineNum;
@@ -131,10 +133,7 @@
 		}
 
 		var jumpY = expLineNum - lastLineNum;
-		if( jumpY )
-		{
-			this.moveY( jumpY );
-		}
+		if( jumpY ) this.moveY( jumpY );
 
 		pline = this.getLine();
 
@@ -163,6 +162,50 @@
 
 		this.moveX( - Number.MAX_VALUE, false, false, true );
 		this.moveX( jumpX, false, phantomSpace, skipTabs );
+
+		this.__unsuppressUpdate();
+	};
+
+	// A line-only variant of moveTo, de-generalized for the sake of performance
+	Cursor.prototype.gotoLine = function( n )
+	{
+		this.__suppressUpdate();
+
+		var content = this.feeder.content;
+		var pline = this.getLine();
+		var lastLineNum = pline.lineNum;
+
+		if( pline.placeholder )
+		{
+			lastLineNum = 0;
+			this.Y = 0;
+		}
+
+		var expLineNum = 0;
+		var lineStart = 0;
+		for( var i = content.indexOf( "\n" ); 0 <= i ; i = content.indexOf( "\n", i ) )
+		{
+			if( expLineNum == n ) break;
+			lineStart = i;
+			i ++;
+			expLineNum ++;
+		}
+
+		if( expLineNum < n ) n = expLineNum;
+
+		var jumpY = expLineNum - lastLineNum;
+		if( jumpY ) this.moveY( jumpY );
+
+		pline = this.getLine();
+
+		if( pline.lineNum != expLineNum )
+		{
+			this.moveY( expLineNum - pline.lineNum );
+		}
+
+		this.lineStart( true );
+
+		this.__unsuppressUpdate();
 	};
 
 	// 0 will be treated as default ( 1 )
@@ -323,7 +366,7 @@
 		else if( c == "\n" )
 		{
 			x += d;
-		}
+	}
 
 		// Wordwrap phantomSpace movement compensation on max filled lines
 		if( feeder.wrap && boundary && !hasPhantomSpace && phantomSpace )
@@ -369,10 +412,12 @@
 		this.moveX( Number.MAX_VALUE, false, phantomSpace, true );
 	};
 
+	// Because LineOffset is costly, suppress unnecessary calls
 	Cursor.prototype.updatePosition = function()
 	{
-		var feeder = this.feeder;
-		var P = this.X + LineOffset( feeder.lineBuffers, this.Y ) + this.__off;
+		if( 0 < this.__suppUpdate ) return;
+
+		var P = this.X + LineOffset( this.feeder.lineBuffers, this.Y ) + this.__off;
 
 		this.PStart = P;
 		this.PEnd = P + 1;
@@ -538,6 +583,13 @@
 	Cursor.prototype.suppressEvent = function() { ++ this.__suppEvt; };
 	Cursor.prototype.unsuppressEvent = function() { -- this.__suppEvt; };
 
+	Cursor.prototype.__suppressUpdate = function() { ++ this.__suppUpdate; };
+	Cursor.prototype.__unsuppressUpdate = function()
+	{
+		-- this.__suppUpdate;
+		this.updatePosition();
+	};
+
 	Cursor.prototype.getLine = function( display )
 	{
 		var feeder = this.feeder;
@@ -568,20 +620,7 @@
 
 	__readOnly( Cursor.prototype, "rawLine", function()
 	{
-		var str = this.feeder.content;
-		var lineNum = this.getLine().lineNum - 1;
-		var i = str.indexOf( "\n" ), j = 0;
-
-		for( ; 0 <= i; i = str.indexOf( "\n", i ), j ++ )
-		{
-			if( lineNum == j ) break;
-			i ++;
-		}
-
-		if( j == 0 && i == -1 ) i = 0;
-
-		var end = str.indexOf( "\n", i + 1 );
-		return str.substring( i + 1, end );
+		return this.feeder.line( this.getLine().lineNum - 1 );
 	} );
 
 	// The position offset relative to current line
diff --git a/botanjs/src/Components/Vim/LineFeeder.js b/botanjs/src/Components/Vim/LineFeeder.js
index 4098773..b939daf 100644
--- a/botanjs/src/Components/Vim/LineFeeder.js
+++ b/botanjs/src/Components/Vim/LineFeeder.js
@@ -10,6 +10,7 @@
 	var Cursor = ns[ NS_INVOKE ]( "Cursor" );
 
 	var occurence = __import( "System.utils.Perf.CountSubstr" );
+	var C_LINE = 0;
 
 	var Feeder = function( rows, cols )
 	{
@@ -35,6 +36,7 @@
 		this.cursor = new Cursor( this );
 		this.dispatcher = new EventDispatcher();
 
+		this.__lineCache = [];
 		this.__clseLine = null;
 		this.__moreAt = -1;
 		this.__rows = rows;
@@ -42,7 +44,7 @@
 
 	Feeder.prototype.init = function( content, wrap )
 	{
-		this.content = content;
+		this.__content = content;
 		this.setWrap( wrap );
 
 		this.firstBuffer.Push( content, this.wrap, 0 ); 
@@ -142,7 +144,7 @@
 		if( Y < 0 ) Y = 0;
 
 		// Compensate the last "\n" content placeholder
-		var cont = this.content.slice( 0, -1 );
+		var cont = this.__content.slice( 0, -1 );
 		if( 0 < Y )
 		{
 			f = cont.indexOf( "\n" );
@@ -158,7 +160,7 @@
 			}
 		}
 
-		this.firstBuffer.Push( this.content.substr( f + 1 ), this.wrap, i );
+		this.firstBuffer.Push( this.__content.substr( f + 1 ), this.wrap, i );
 
 		this.panX = X;
 		this.panY = Y;
@@ -171,8 +173,27 @@
 		this.__softRender();
 	};
 
+	Feeder.prototype.line = function( n )
+	{
+		if( this.__lineCache[ n ] )
+			return this.__lineCache[ n ];
+		var str = this.__content;
+		var i = str.indexOf( "\n" ), j = 0;
+
+		for( ; 0 <= i; i = str.indexOf( "\n", i ), j ++ )
+		{
+			if( n == j ) break;
+			i ++;
+		}
+
+		if( j == 0 && i == -1 ) i = 0;
+
+		var end = str.indexOf( "\n", i + 1 );
+		return ( this.__lineCache[ n ] = str.substring( i + 1, end ) );
+	};
+
 	__readOnly( Feeder.prototype, "linesTotal", function() {
-		return occurence( this.content, "\n" );
+		return occurence( this.__content, "\n" );
 	} );
 
 	__readOnly( Feeder.prototype, "firstBuffer", function() {
@@ -216,7 +237,7 @@
 		var i = l - X;
 		do
 		{
-			if( this.content[ i + 1 ] == "\t" ) tabs ++;
+			if( this.__content[ i + 1 ] == "\t" ) tabs ++;
 			i ++;
 		}
 		while( i < l )
@@ -254,6 +275,15 @@
 		return pos;
 	} );
 
+	Object.defineProperty( Feeder.prototype, "content", {
+		get: function() { return this.__content; }
+		, set: function( v )
+		{
+			this.__lineCache = [];
+			this.__content = v;
+		}
+	} );
+
 	__readOnly( Feeder.prototype, "linesOccupied", function() {
 		var line = this.firstBuffer;
 		if( line.placeholder ) return 0;
diff --git a/botanjs/src/Components/Vim/State/Marks.js b/botanjs/src/Components/Vim/State/Marks.js
new file mode 100644
index 0000000..102af97
--- /dev/null
+++ b/botanjs/src/Components/Vim/State/Marks.js
@@ -0,0 +1,31 @@
+(function(){
+	var ns = __namespace( "Components.Vim.State" );
+
+	/** @type {System.Debug} */
+	var debug = __import( "System.Debug" );
+
+	var Keys = "'ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxy\"[]^.<>";
+
+	var Marks = function()
+	{
+		this.__marks = {};
+	};
+
+	Marks.prototype.set = function( t, line, col )
+	{
+		if( Keys.indexOf( t ) == -1 ) return false;
+
+		this.__marks[ t ] = [ line, col ];
+		return true;
+	};
+
+	Marks.prototype.get = function( t )
+	{
+		return this.__marks[ t ];
+	};
+
+	__readOnly( Marks, "Keys", function() { return Keys; } );
+
+	ns[ NS_EXPORT ]( EX_CLASS, "Marks", Marks );
+
+})();
diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js
index 27e5874..8986ce6 100644
--- a/botanjs/src/Components/Vim/VimArea.js
+++ b/botanjs/src/Components/Vim/VimArea.js
@@ -14,6 +14,8 @@
 
 	/** @type {Components.Vim.State.Registers} */
 	var Registers                                 = __import( "Components.Vim.State.Registers" );
+	/** @type {Components.Vim.State.Marks} */
+	var Marks                                     = __import( "Components.Vim.State.Marks" );
 	/** @type {Components.Vim.Syntax.Analyzer} */
 	var SyntaxAnalyzer                            = __import( "Components.Vim.Syntax.Analyzer" );
 
@@ -243,6 +245,7 @@
 		this.statusFeeder = sfeeder;
 		this.statusBar = statusBar;
 		this.registers = new Registers();
+		this.marks = new Marks();
 
 		this.__cursor = cfeeder.cursor;
 
diff --git a/botanjs/src/externs/Components.Vim.Cursor.js b/botanjs/src/externs/Components.Vim.Cursor.js
index 2b60b30..634cd47 100644
--- a/botanjs/src/externs/Components.Vim.Cursor.js
+++ b/botanjs/src/externs/Components.Vim.Cursor.js
@@ -13,6 +13,8 @@ Components.Vim.Cursor.rec;
 /** @type Function */
 Components.Vim.Cursor.moveTo;
 /** @type Function */
+Components.Vim.Cursor.gotoLine;
+/** @type Function */
 Components.Vim.Cursor.moveX;
 /** @type Function */
 Components.Vim.Cursor.moveY;
diff --git a/botanjs/src/externs/Components.Vim.LineFeeder.js b/botanjs/src/externs/Components.Vim.LineFeeder.js
index eb500db..5edec59 100644
--- a/botanjs/src/externs/Components.Vim.LineFeeder.js
+++ b/botanjs/src/externs/Components.Vim.LineFeeder.js
@@ -43,6 +43,8 @@ Components.Vim.LineFeeder.linesOccupied;
 /** @type String */
 Components.Vim.LineFeeder.docPos;
 /** @type String */
+Components.Vim.LineFeeder.line;
+/** @type String */
 Components.Vim.LineFeeder.lineStat;
 /** @type {String} */
 Components.Vim.LineFeeder.content;
diff --git a/botanjs/src/externs/Components.Vim.State.Marks.js b/botanjs/src/externs/Components.Vim.State.Marks.js
new file mode 100644
index 0000000..99a4ade
--- /dev/null
+++ b/botanjs/src/externs/Components.Vim.State.Marks.js
@@ -0,0 +1,10 @@
+/** @constructor */
+Components.Vim.State.Marks = function(){};
+
+/** @type Function */
+Components.Vim.State.Marks.set;
+/** @type Function */
+Components.Vim.State.Marks.get;
+
+/** @type String */
+Components.Vim.State.Marks.Keys;
diff --git a/botanjs/src/externs/Components.Vim.VimArea.js b/botanjs/src/externs/Components.Vim.VimArea.js
index 756c6c3..a2286d1 100644
--- a/botanjs/src/externs/Components.Vim.VimArea.js
+++ b/botanjs/src/externs/Components.Vim.VimArea.js
@@ -12,6 +12,11 @@ Components.Vim.VimArea.statusFeeder;
 /** @type {Components.Vim.StatusBar} */
 Components.Vim.VimArea.statusBar;
 
+/** @type {Components.Vim.State.Registers} */
+Components.Vim.VimArea.registers;
+/** @type {Components.Vim.State.Marks} */
+Components.Vim.VimArea.marks;
+
 /** @type Function */
 Components.Vim.VimArea.demo;
 /** @type Function */