commit 78cb3d3610da35fc165ee3e84a12b03aa10d0e52
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2016-04-15T16:06:59Z |
| subject | Partial indent command |
commit 78cb3d3610da35fc165ee3e84a12b03aa10d0e52
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2016-04-15T16:06:59Z
Partial indent command
---
botanjs/src/Components/Vim/Actions/INSERT.js | 6 +
botanjs/src/Components/Vim/Actions/REDO.js | 4 +-
botanjs/src/Components/Vim/Actions/REPLACE.js | 7 +-
botanjs/src/Components/Vim/Actions/SHIFT_LINES.js | 242 ++++++++++++++++++++++
botanjs/src/Components/Vim/Actions/UNDO.js | 4 +-
botanjs/src/Components/Vim/Actions/YANK.js | 8 -
botanjs/src/Components/Vim/Controls.js | 31 ++-
botanjs/src/Components/Vim/Cursor.js | 4 +-
botanjs/src/Components/Vim/_this.js | 2 +
9 files changed, 284 insertions(+), 24 deletions(-)
diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js
index 5a4b30a..c35c167 100644
--- a/botanjs/src/Components/Vim/Actions/INSERT.js
+++ b/botanjs/src/Components/Vim/Actions/INSERT.js
@@ -204,6 +204,9 @@
{
var ind = this.__phantomIndent;
if( !this.__phantomIndent ) return;
+
+ debug.Info( "Realize Indentation: " + ind );
+
l = ind[ IN_END ];
for( var i = ind[ IN_START ]; i < l; i ++ )
{
@@ -261,6 +264,7 @@
a[ IN_DEL ] = inDel;
this.__phantomIndent = a;
+ debug.Info( "Phantom indent: " + a );
}
};
@@ -269,6 +273,8 @@
var ind = this.__phantomIndent;
if( !ind ) return "";
+ debug.Info( "Erase phantom indent: " + ind );
+
var cur = this.__cursor;
var feeder = cur.feeder;
diff --git a/botanjs/src/Components/Vim/Actions/REDO.js b/botanjs/src/Components/Vim/Actions/REDO.js
index 43ca88b..94485d2 100644
--- a/botanjs/src/Components/Vim/Actions/REDO.js
+++ b/botanjs/src/Components/Vim/Actions/REDO.js
@@ -24,8 +24,10 @@
var stack = this.__cursor.rec.redo();
if( stack )
{
+ this.__cursor.suppressEvent();
stack.play();
- this.__message = "<<TODO>>; before #" + stack.id + " " + stack.time;
+ this.__cursor.unsuppressEvent();
+ this.__message = Mesg( "NCHANGES", "<TODO>", stack.id, stack.time );
}
else
{
diff --git a/botanjs/src/Components/Vim/Actions/REPLACE.js b/botanjs/src/Components/Vim/Actions/REPLACE.js
index 1acf895..0161595 100644
--- a/botanjs/src/Components/Vim/Actions/REPLACE.js
+++ b/botanjs/src/Components/Vim/Actions/REPLACE.js
@@ -92,7 +92,7 @@
this.__cursor.unsuppressEvent();
};
- REPLACE.prototype.handler = function( e, p, range )
+ REPLACE.prototype.handler = function( e, p )
{
e.preventDefault();
@@ -143,13 +143,16 @@
var content = feeder.content.slice( 0, -1 )
.replace( search, this.__replCallback ) + "\n";
- if( !this.__replacedGroups.length )
+ var numSubs = this.__replacedGroups.length;
+ if( !numSubs )
{
this.__msg = VimError( "E486", spattern.join( "" ) );
}
feeder.content = content;
+ this.__msg = Mesg( "REPLACE", numSubs, "<TODO>" );
+
// Record this step for UNDO / REDO
this.__rec();
diff --git a/botanjs/src/Components/Vim/Actions/SHIFT_LINES.js b/botanjs/src/Components/Vim/Actions/SHIFT_LINES.js
new file mode 100644
index 0000000..a539c17
--- /dev/null
+++ b/botanjs/src/Components/Vim/Actions/SHIFT_LINES.js
@@ -0,0 +1,242 @@
+(function(){
+ var ns = __namespace( "Components.Vim.Actions" );
+
+ /** @type {System.Debug} */
+ var debug = __import( "System.Debug" );
+ var beep = __import( "Components.Vim.Beep" );
+
+ /** @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" );
+
+ var REPL_BEFORE = 0;
+ var REPL_OFFSET = 1;
+ var REPL_LENGTH = 2;
+
+ /** @type {Components.Vim.IAction}
+ * Cursor @param {Components.Vim.Cursor}
+ * e @param {Components.Vim.ActionEvent}
+ **/
+ var SHIFT_LINES = function( Cursor, e )
+ {
+ /** @type {Components.Vim.Cursor} */
+ this.__cursor = Cursor;
+ this.__startX = Cursor.aPos;
+ this.__msg = "<LINE_SHIFT>";
+
+ this.__slineNum = Cursor.getLine().lineNum;
+
+ this.__lines = e.count;
+ debug.Info( "Open shift: " + this.__lines + " line(s) below the cursor" );
+
+ this.__direction = e.kMap( ">" ) ? 1 : -1;
+ debug.Info( "Direction is: " + ( this.__direction == 1 ? ">" : "<" ) );
+
+ Cursor.suppressEvent();
+ };
+
+ SHIFT_LINES.prototype.allowMovement = true;
+
+ SHIFT_LINES.prototype.dispose = function()
+ {
+ this.__cursor.unsuppressEvent();
+ };
+
+ SHIFT_LINES.prototype.handler = function( e, sp )
+ {
+ e.preventDefault();
+
+ if( e.ModKeys || e.kMap( "i" ) ) return;
+
+ var cur = this.__cursor;
+ var feeder = cur.feeder;
+
+ var Triggered = false;
+ var dir = this.__direction;
+
+ var start = this.__slineNum;
+ var nline = this.__lines;
+
+ if( 1 < e.count )
+ {
+ nline += e.count;
+ }
+
+ var end = start;
+
+ var shiftCount = 1;
+ if( sp == undefined )
+ {
+ Triggered = true;
+
+ sp = this.__startX;
+
+ var currAp = cur.aPos;
+
+ if( this.__startX != currAp )
+ {
+ if( e.kMap( "h" ) || e.kMap( "l" ) ){}
+ else if( e.kMap( "j" ) )
+ {
+ end = start + nline;
+ }
+ else if( e.kMap( "k" ) )
+ {
+ start -= nline;
+ }
+ else // TODO: Dectect movement line count
+ {
+ }
+ }
+ else
+ {
+ if( !( ( 0 < dir && ( e.kMap( ">" ) || e.kMap( "l" ) ) )
+ || ( dir < 0 && ( e.kMap( "<" ) || e.kMap( "h" ) ) )
+ ) )
+ {
+ beep();
+ return true;
+ }
+ }
+ }
+
+ // last "\n" padding
+ var c = feeder.content.slice( 0, -1 );
+
+ var indents = c.match( /^[\t ]+/gm );
+ var indentChar = "\t";
+ var tabwidth = feeder.firstBuffer.tabWidth;
+
+ if( indents )
+ {
+ var l = indents.length - 1;
+
+ if( 1 < l )
+ {
+ debug.Info( "Guessing the tabstop:" );
+ var tabOccr = 0;
+ var spOccr = 0;
+
+ // Guess indent
+ var tabStat = {};
+
+ for( var i = 0; i < l; i ++ )
+ {
+ var ind = indents[ i ];
+ var indNext = indents[ i + 1 ];
+ tabOccr += occurence( ind, "\t" );
+ spOccr += occurence( ind, " " );
+ var d = indNext.length - ind.length;
+ if( d == 0 ) continue;
+
+ d = d < 0 ? -d : d;
+
+ if( !tabStat[ d ] ) tabStat[ d ] = 0;
+
+ tabStat[ d ] ++;
+ }
+
+ var upperDiff = 0;
+ var indentCLen = 0;
+ for( var i in tabStat )
+ {
+ var p = tabStat[ i ];
+ if( upperDiff < p )
+ {
+ upperDiff = p;
+ indentCLen = i;
+ }
+ }
+
+ spOccr /= indentCLen;
+
+ if( tabOccr < spOccr )
+ {
+ indentChar = "";
+ for( var i = 0; i < indentCLen; i ++ ) indentChar += " ";
+ }
+
+ tabwidth = indentCLen;
+
+ debug.Info( "\tTab count: " + tabOccr );
+ debug.Info( "\tSpace count: " + spOccr );
+ debug.Info( "\ti.e. indent using " + JSON.stringify( indentChar ) );
+ }
+ else
+ {
+ debug.Info( "Not enough tabs to determine the tabstop, using default" );
+ }
+ }
+
+ debug.Info( "Start: " + start, "End: " + end );
+ var rBlock = "";
+
+ var started = false;
+ var indentTimes = 1;
+
+ feeder.content = "";
+ nline = 0;
+
+ for( var i = 0, j = 0; 0 <= i; i = c.indexOf( "\n", i ), j ++ )
+ {
+ i ++;
+
+ if( j < start ) continue;
+ else if( !started )
+ {
+ started = true;
+ feeder.content = c.substring( 0, i - 1 );
+ }
+
+ if( end < j ) break;
+
+ var line = c.substring( 1 < i ? i : i - 1, c.indexOf( "\n", i ) );
+
+ if( 1 < i ) feeder.content += "\n";
+
+ if( line !== "" )
+ {
+ if( 0 < dir )
+ {
+ feeder.content += indentChar + line;
+ }
+ else
+ {
+ for( var si = 0, sj = 1; si < indentTimes; si ++ )
+ {
+ var startC = line[ si ];
+ if( startC == " " )
+ {
+ for( ; sj < tabwidth; sj ++ )
+ {
+ if( !~"\t ".indexOf( line[ si + sj ] ) ) break;
+ }
+ }
+ else if( startC != "\t" ) break;
+ }
+
+ feeder.content += line.substring( si + sj - 1 );
+ }
+ nline ++;
+ }
+ }
+
+ feeder.content += "\n" + c.substring( i ) + "\n";
+ feeder.pan();
+
+ this.__msg = Mesg( "LINES_SHIFTED", nline, dir < 0 ? "<" : ">", 1 );
+
+ return Triggered;
+ };
+
+ SHIFT_LINES.prototype.getMessage = function()
+ {
+ return this.__msg;
+ };
+
+ ns[ NS_EXPORT ]( EX_CLASS, "SHIFT_LINES", SHIFT_LINES );
+})();
diff --git a/botanjs/src/Components/Vim/Actions/UNDO.js b/botanjs/src/Components/Vim/Actions/UNDO.js
index 6f81f52..86db7cf 100644
--- a/botanjs/src/Components/Vim/Actions/UNDO.js
+++ b/botanjs/src/Components/Vim/Actions/UNDO.js
@@ -24,8 +24,10 @@
var stack = this.__cursor.rec.undo();
if( stack )
{
+ this.__cursor.suppressEvent();
stack.play();
- this.__message = "<<TODO>>; before #" + stack.id + " " + stack.time;
+ this.__cursor.unsuppressEvent();
+ this.__message = Mesg( "NCHANGES", "<TODO>", stack.id, stack.time );
}
else
{
diff --git a/botanjs/src/Components/Vim/Actions/YANK.js b/botanjs/src/Components/Vim/Actions/YANK.js
index 44e3c27..9c9a37a 100644
--- a/botanjs/src/Components/Vim/Actions/YANK.js
+++ b/botanjs/src/Components/Vim/Actions/YANK.js
@@ -50,22 +50,18 @@
var currAp = cur.aPos;
if( this.__startX != currAp )
{
- // Remove to start
if( e.kMap( "^" ) )
{
sp --;
}
- // Remove char in cursor
else if( e.kMap( "l" ) )
{
cur.moveX( -1 );
}
- // Remove char before cursor
else if( e.kMap( "h" ) )
{
sp = currAp;
}
- // Remove the current and the following line
else if( e.kMap( "j" ) )
{
newLine = true;
@@ -75,7 +71,6 @@
cur.lineStart();
this.__startX = cur.aPos;
}
- // Remove the current and the preceding line
else if( e.kMap( "k" ) )
{
newLine = true;
@@ -94,7 +89,6 @@
cur.moveTo( this.__startX );
}
}
- // Remove the current line
else
{
if( e.kMap( "y" ) )
@@ -111,8 +105,6 @@
}
else if( e.kMap( "^" ) )
{
- // Do nothing as nothing can be removed
- // since there is no successful movement
return true;
}
// this is the same as kMap( "h" ) above
diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js
index fac0875..c34a4d9 100644
--- a/botanjs/src/Components/Vim/Controls.js
+++ b/botanjs/src/Components/Vim/Controls.js
@@ -205,7 +205,7 @@
case A: // Append
ccur.moveX( 1, true, true );
case I: // Insert
- ccur.openAction( "INSERT" );
+ ccur.openAction( "INSERT", e );
break;
case S: // Delete Char and start insert
@@ -213,18 +213,18 @@
{
ccur.openRunAction( "DELETE", e, ccur.aPos );
}
- ccur.openAction( "INSERT" );
+ ccur.openAction( "INSERT", e );
break;
case SHIFT + O: // new line before insert
ccur.lineStart();
- ccur.openAction( "INSERT" );
+ ccur.openAction( "INSERT", e );
ccur.action.handler( new ActionEvent( e.sender, "Enter" ) );
ccur.moveY( -1 );
break;
case O: // new line insert
ccur.lineEnd( true );
- ccur.openAction( "INSERT" );
+ ccur.openAction( "INSERT", e );
ccur.action.handler( new ActionEvent( e.sender, "Enter" ) );
break;
@@ -236,10 +236,10 @@
break;
case D: // Del with motion
- ccur.openAction( "DELETE" );
+ ccur.openAction( "DELETE", e );
break;
case Y: // Yank with motion
- ccur.openAction( "YANK" );
+ ccur.openAction( "YANK", e );
break;
case P: // Put
@@ -272,7 +272,7 @@
case V: // Visual
case SHIFT + V: // Visual line
- ccur.openAction( "VISUAL" );
+ ccur.openAction( "VISUAL", e );
ccur.action.handler( e );
break;
@@ -281,6 +281,11 @@
this.__divedCCmd.handler( e );
break;
+ case SHIFT + COMMA: // <
+ case SHIFT + FULLSTOP: // >
+ ccur.openAction( "SHIFT_LINES", e );
+ break;
+
case F1: // F1, help
break;
default:
@@ -349,15 +354,21 @@
var Count = e.key;
var recurNum = function( e )
{
+ var intercept = e.ModKeys;
switch( e.keyCode )
{
case _0: case _1: case _2:
case _3: case _4: case _5:
case _6: case _7: case _8: case _9:
Count += e.key;
- _self.__composite( e, recurNum, ANY_KEY );
- e.cancel();
- return;
+ intercept = true;
+ }
+
+ if( intercept )
+ {
+ _self.__composite( e, recurNum, ANY_KEY );
+ e.cancel();
+ return;
}
e.__count = Number( Count );
diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js
index e9b6d13..f8d2dd9 100644
--- a/botanjs/src/Components/Vim/Cursor.js
+++ b/botanjs/src/Components/Vim/Cursor.js
@@ -376,13 +376,13 @@
// Open an action handler
// i.e. YANK, VISUAL, INSERT, UNDO, etc.
- Cursor.prototype.openAction = function( name )
+ Cursor.prototype.openAction = function( name, e )
{
if( this.action ) this.action.dispose();
debug.Info( "openAction: " + name );
- this.action = new (Actions[ name ])( this );
+ this.action = new (Actions[ name ])( this, e );
this.__pulseMsg = null;
this.__visualUpdate();
diff --git a/botanjs/src/Components/Vim/_this.js b/botanjs/src/Components/Vim/_this.js
index ab26706..dedf3f4 100644
--- a/botanjs/src/Components/Vim/_this.js
+++ b/botanjs/src/Components/Vim/_this.js
@@ -19,10 +19,12 @@ VIMRE_VERSION = "1.0.0b";
, "UNDO_LIMIT": "Already at oldest change"
, "REDO_LIMIT": "Already at newest change"
+ , "NCHANGES": "%1 change(s); before #%2 %3"
, "LINES_FEWER": "%1 fewer line(s)"
, "LINES_MORE": "%1 more line(s)"
, "LINES_YANKED": "%1 line(s) yanked"
+ , "LINES_SHIFTED": "%1 line(s) %2ed %3 time(s)"
, "SEARCH_HIT_BOTTOM": "search hit BOTTOM, continuing at TOP"
, "SEARCH_HIT_TOP": "search hit TOP, continuing at BOTTOM"