commit 78f8a20a5d77436720a8931480fda21b020a9218
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2016-03-22T19:15:02Z |
| subject | DELETE command in VISUAL, generalized Stator Obj |
commit 78f8a20a5d77436720a8931480fda21b020a9218
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2016-03-22T19:15:02Z
DELETE command in VISUAL, generalized Stator Obj
---
botanjs/src/Components/Vim/Actions/DELETE.js | 65 ++++++++++-
botanjs/src/Components/Vim/Actions/INSERT.js | 119 +++++----------------
botanjs/src/Components/Vim/Actions/VISUAL.js | 29 ++---
botanjs/src/Components/Vim/Controls.js | 47 ++++----
botanjs/src/Components/Vim/Cursor.js | 28 +++--
botanjs/src/Components/Vim/State/Stator.js | 71 ++++++++++++
botanjs/src/Components/Vim/VimArea.js | 2 +-
botanjs/src/Components/Vim/_this.js | 2 +
.../externs/Components.Vim.Controls.InputEvent.js | 2 +
botanjs/src/externs/Components.Vim.Cursor.js | 6 ++
10 files changed, 239 insertions(+), 132 deletions(-)
diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js
index 113a417..c42b2ef 100644
--- a/botanjs/src/Components/Vim/Actions/DELETE.js
+++ b/botanjs/src/Components/Vim/Actions/DELETE.js
@@ -1,13 +1,24 @@
(function(){
var ns = __namespace( "Components.Vim.Actions" );
+ /** @type {System.Debug} */
+ var debug = __import( "System.Debug" );
+
+ /** @type {Components.Vim.State.Stator} */
+ var Stator = __import( "Components.Vim.State.Stator" );
+ /** @type {Components.Vim.State.Stack} */
+ var Stack = __import( "Components.Vim.State.Stack" );
+
var Mesg = __import( "Components.Vim.Message" );
+ var occurence = __import( "System.utils.Perf.CountSubstr" );
+
/** @type {Components.Vim.Cursor.IAction} */
var DELETE = function( Cursor )
{
/** @type {Components.Vim.Cursor} */
this.__cursor = Cursor;
+ this.__nline = 0;
};
DELETE.prototype.allowMovement = true;
@@ -17,15 +28,65 @@
};
- DELETE.prototype.handler = function( e )
+ DELETE.prototype.handler = function( e, sp )
{
e.preventDefault();
+ /** @type {Components.Vim.State.Registers} */
+ var reg = e.target.registers;
+
+ var cur = this.__cursor;
+ var feeder = cur.feeder;
+
+ var c = feeder.content;
+
+ var s = sp;
+ var e = cur.aPos;
+
+ if( e < s )
+ {
+ s = cur.aPos;
+ e = sp;
+ }
+
+ var removed = c.substring( s, e + 1 );
+ reg.change( removed );
+
+ this.__nline = occurence( removed, "\n" );
+
+ feeder.content = c.substring( 0, s ) + c.substring( e + 1 );
+
+ var stator = new Stator( cur, s );
+ var stack = new Stack();
+
+ c = c[ e + 1 ];
+ if( c == "\n" || c == undefined )
+ {
+ cur.suppressEvent();
+ cur.moveX( -1 );
+ cur.unsuppressEvent();
+ }
+
+ var f = stator.save( 0, removed );
+ stack.store( function() {
+ f();
+ // Offset correction after REDO / UNDO
+ cur.moveX( 1 );
+ } );
+
+ cur.rec.record( stack );
+
+ feeder.pan();
};
DELETE.prototype.getMessage = function()
{
- return "<TODO> DELETE COMMAND";
+ if( this.__nline )
+ {
+ return Mesg( "LINE_FEWER", this.__nline );
+ }
+
+ return "";
};
ns[ NS_EXPORT ]( EX_CLASS, "DELETE", DELETE );
diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js
index a199a5f..e67f083 100644
--- a/botanjs/src/Components/Vim/Actions/INSERT.js
+++ b/botanjs/src/Components/Vim/Actions/INSERT.js
@@ -2,9 +2,11 @@
var ns = __namespace( "Components.Vim.Actions" );
/** @type {Components.Vim.State.Stack} */
- var Stack = __import( "Components.Vim.State.Stack" );
+ var Stack = __import( "Components.Vim.State.Stack" );
+ /** @type {Components.Vim.State.Stator} */
+ var Stator = __import( "Components.Vim.State.Stator" );
/** @type {System.Debug} */
- var debug = __import( "System.Debug" );
+ var debug = __import( "System.Debug" );
var Mesg = __import( "Components.Vim.Message" );
@@ -27,7 +29,7 @@
/** @type {Components.Vim.Cursor} */
this.__cursor = Cursor;
- this.__startState = this.__saveCur();
+ this.__Stator = new Stator( Cursor );
// Initialize this stack
this.__rec( "", true );
@@ -35,66 +37,12 @@
INSERT.prototype.allowMovement = false;
- INSERT.prototype.__saveCur = function()
- {
- var c = this.__cursor;
- var obj = {
- p: c.P
- , x: c.X
- , y: c.Y
- , px: c.feeder.panX
- , py: c.feeder.panY
- };
-
- if( 0 < obj.x )
- {
- obj.p -= 1;
- obj.x -= 1;
- }
-
- return obj;
- }
-
INSERT.prototype.dispose = function()
{
this.__cursor.moveX( -1 );
this.__rec( "", true );
};
- INSERT.prototype.__storeState = function()
- {
- var cur = this.__cursor;
- var feeder = cur.feeder;
- var insertLength = this.__insertLength;
- var contentUndo = this.__contentUndo;
- var startPos = this.__startPosition;
- var sSt = this.__startState;
- var eSt = this.__saveCur();
-
- var st = sSt;
- // Calling this repeatedly will swap between UNDO / REDO state
- return function() {
- var contentRedo = feeder.content.substr( startPos, insertLength );
- feeder.content =
- feeder.content.substring( 0, startPos )
- + contentUndo
- + feeder.content.substring( startPos + insertLength );
- insertLength = contentUndo.length;
- contentUndo = contentRedo;
-
- cur.PStart = st.p;
- cur.PEnd = st.p + 1;
- cur.X = st.x;
- cur.Y = st.y;
- feeder.panX = st.px;
- feeder.panY = st.py;
-
- feeder.pan();
-
- st = ( st == sSt ) ? eSt : sSt;
- };
- };
-
INSERT.prototype.__rec = function( c, newRec )
{
if( newRec || !this.__stack )
@@ -107,7 +55,7 @@
) return;
this.__stack.store(
- this.__storeState()
+ this.__Stator.save( this.__insertLength, this.__contentUndo )
);
this.__cursor.rec.record( this.__stack );
@@ -116,7 +64,6 @@
this.__insertLength = 0;
this.__contentUndo = "";
this.__stack = new Stack();
- this.__startPosition = this.__cursor.aPos;
}
if( c == "\n" )
@@ -132,45 +79,37 @@
var cur = this.__cursor;
var feeder = cur.feeder;
- switch( e.keyCode )
+ // Backspace
+ if( e.kMap( "BS" ) )
{
- case 8: // Backspace
- var oY = feeder.panY + cur.Y;
- if( cur.X == 0 && feeder.panY == 0 && cur.Y == 0 ) return;
-
- cur.moveX( -1, true, true );
+ var oY = feeder.panY + cur.Y;
+ if( cur.X == 0 && feeder.panY == 0 && cur.Y == 0 ) return;
- var f = cur.aPos;
+ cur.moveX( -1, true, true );
- if( this.__insertLength <= 0 )
- {
- this.__contentUndo = feeder.content.substr( f, 1 ) + this.__contentUndo;
- this.__startPosition --;
- }
- else
- {
- this.__insertLength --;
- }
+ var f = cur.aPos;
- feeder.content =
- feeder.content.substring( 0, f )
- + feeder.content.substring( f + 1 );
-
- break;
- case 46: // Delete
- var f = cur.aPos;
+ if( this.__insertLength <= 0 )
+ {
+ this.__contentUndo = feeder.content.substr( f, 1 ) + this.__contentUndo;
+ this.__insertLength --;
+ }
- this.__contentUndo += feeder.content.substr( f, 1 );
+ feeder.content =
+ feeder.content.substring( 0, f )
+ + feeder.content.substring( f + 1 );
+ }
+ else if( e.kMap( "Del" ) )
+ {
+ var f = cur.aPos;
- feeder.content =
- feeder.content.substring( 0, f )
- + feeder.content.substring( f + 1 );
+ this.__contentUndo += feeder.content.substr( f, 1 );
- break;
- default:
- // Do nothing
- return;
+ feeder.content =
+ feeder.content.substring( 0, f )
+ + feeder.content.substring( f + 1 );
}
+ else return;
feeder.pan();
feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js
index ab9671b..659b058 100644
--- a/botanjs/src/Components/Vim/Actions/VISUAL.js
+++ b/botanjs/src/Components/Vim/Actions/VISUAL.js
@@ -23,6 +23,7 @@
this.__leaveMesg = "";
Cursor.blink = false;
+ Cursor.pSpace = true;
};
VISUAL.prototype.allowMovement = true;
@@ -31,6 +32,7 @@
{
this.__msg = this.__leaveMesg;
this.__cursor.blink = true;
+ this.__cursor.pSpace = false;
this.__cursor.PStart = this.__selStart;
this.__cursor.PEnd = this.__selStart + 1;
};
@@ -41,27 +43,18 @@
if( e.ModKeys ) return;
+ var cur = this.__cursor;
var Action = null;
switch( true )
{
case e.kMap( "y" ):
- Action = new YANK( this.__cursor );
+ Action = new YANK( cur );
break;
case e.kMap( "d" ):
- Action = new DELETE( this.__cursor );
+ Action = new DELETE( cur );
break;
}
- if( Action )
- {
- Action.handler( e );
- this.__leaveMesg = Action.getMessage();
- Action.dispose();
-
- return true;
- }
-
- var cur = this.__cursor;
var prevPos = this.__start;
var newPos = cur.PStart;
@@ -78,6 +71,18 @@
this.__selStart = prevPos;
}
+ if( Action )
+ {
+ cur.suppressEvent();
+ Action.handler( e, this.__startaP );
+ this.__leaveMesg = Action.getMessage();
+ Action.dispose();
+ cur.unsuppressEvent();
+
+ this.__selStart = cur.PStart;
+ return true;
+ }
+
cur.PStart = prevPos;
cur.PEnd = newPos;
};
diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js
index ce1e947..7bcc195 100644
--- a/botanjs/src/Components/Vim/Controls.js
+++ b/botanjs/src/Components/Vim/Controls.js
@@ -14,6 +14,7 @@
var KEY_ALT = 18;
var BACKSPACE = 8;
+ var DELETE = 46;
var _0 = 48; var _1 = 49; var _2 = 50; var _3 = 51; var _4 = 52;
var _5 = 53; var _6 = 54; var _7 = 55; var _8 = 56; var _9 = 57;
@@ -31,6 +32,8 @@
var F6 = 117; var F7 = 118; var F8 = 119; var F9 = 120; var F10 = 121;
var F11 = 122; var F12 = 123;
+ var COMMA = 188; var FULLSTOP = 190;
+
var __maps = {};
var Map = function( str )
{
@@ -62,6 +65,9 @@
var kCode;
switch( sCode )
{
+ case "BS": kCode = Mod + BACKSPACE; break;
+ case "Del": kCode = Mod + DELETE; break;
+
case "A": Mod = SHIFT; case "a": kCode = Mod + A; break;
case "B": Mod = SHIFT; case "b": kCode = Mod + B; break;
case "C": Mod = SHIFT; case "c": kCode = Mod + C; break;
@@ -99,8 +105,11 @@
case "*": Mod = SHIFT; case "8": kCode = Mod + _8; break;
case "(": Mod = SHIFT; case "9": kCode = Mod + _9; break;
case ")": Mod = SHIFT; case "0": kCode = Mod + _0; break;
+ case "<": Mod = SHIFT; case ",": kCode = Mod + COMMA; break;
+ case ">": Mod = SHIFT; case ".": kCode = Mod + FULLSTOP; break;
+
default:
- throw new Error( "No such keys: " + str );
+ throw new Error( "Unsupport keys: " + str );
}
return __maps[ str ] = kCode;
@@ -209,7 +218,7 @@
var ccur = this.__ccur;
var x = ccur.X;
- ccur.moveX( a, b, c );
+ ccur.moveX( a, b, c || ccur.pSpace );
if( ccur.X == x ) beep();
};
@@ -245,7 +254,7 @@
var cursorHandled = true;
switch( kCode )
{
- case BACKSPACE: this.__cMoveX( -1, true ); break; // Backspace, go back 1 char, regardless of line
+ case BACKSPACE: this.__cMoveX( -1, true ); break; // Backspace, go back 1 char
case H: this.__cMoveX( -1 ); break; // Left
case L: this.__cMoveX( 1 ); break; // Right
case K: this.__cMoveY( -1 ); break; // Up
@@ -259,7 +268,7 @@
ccur.lineStart();
break;
case SHIFT + _4: // $, End
- ccur.lineEnd();
+ ccur.lineEnd( ccur.pSpace );
break;
case SHIFT + G: // Goto last line
ccur.moveY( Number.MAX_VALUE );
@@ -293,10 +302,10 @@
* */
Controls.prototype.handler = function( sender, e )
{
- // Neve capture these keys
- if( e.ModKeys
+ // Never capture these keys
+ if( e.keyCode == ( ALT + D )
// F2 - F12
- || ( F1 < e.keyCode && e.keyCode < 124 )
+ || ( F1 < e.keyCode && e.keyCode <= F12 )
) return;
// Clear composite command
@@ -334,36 +343,32 @@
return;
}
- if( this.__cursorCommand( e ) )
- {
- e.preventDefault();
- return;
- }
+ e.preventDefault();
- if( this.__actionCommand( e ) )
- {
- e.preventDefault();
- return;
- }
+ if( this.__cursorCommand( e ) ) return;
+ if( this.__actionCommand( e ) ) return;
};
- var InputEvent = function( e )
+ var InputEvent = function( sender, e )
{
this.__e = e;
+ this.__target = sender;
var c = this.__e.keyCode;
this.__escape = c == ESC || ( e.ctrlKey && c == C );
this.__kCode = c
+ ( e.shiftKey || e.getModifierState( "CapsLock" ) ? SHIFT : 0 )
- + ( e.ctrlKey ? CTRL : 0 );
+ + ( e.ctrlKey ? CTRL : 0 )
+ + ( e.altKey ? ALT : 0 );
this.__modKeys = c == KEY_SHIFT || c == KEY_CTRL || c == KEY_ALT;
this.__key = e.key;
};
- __readOnly( InputEvent.prototype, "key", function() { return this.__key; } );
- __readOnly( InputEvent.prototype, "keyCode", function() { return this.__kCode; } );
+ __readOnly( InputEvent.prototype, "target", function() { return this.__target; } );
+ __readOnly( InputEvent.prototype, "key", function() { return this.__key; } );
+ __readOnly( InputEvent.prototype, "keyCode", function() { return this.__kCode; } );
__readOnly( InputEvent.prototype, "ModKeys", function() { return this.__modKeys; } );
__readOnly( InputEvent.prototype, "Escape", function() { return this.__escape; } );
diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js
index 0481f30..5f99f5d 100644
--- a/botanjs/src/Components/Vim/Cursor.js
+++ b/botanjs/src/Components/Vim/Cursor.js
@@ -80,6 +80,9 @@
this.action = null;
this.blink = true;
+ this.pSpace = false;
+
+ this.__suppEvt = 0;
};
// Set by VimArea
@@ -118,12 +121,12 @@
// Include empty lines befor cursor end
if( ( phantomSpace && cLen - 1 <= x ) || ( cLen == 1 && c == undefined ) )
{
- x = d > 0 ? cLen - 1 : 0;
+ x = 0 < d ? cLen - 1 : 0;
}
// ( 2 < cLen ) Exclude empty lines at cursor end
- else if( ( 2 < cLen && x == cLen - 1 && c == " " ) || c == undefined )
+ else if( ( 2 <= cLen && x == cLen - 1 && c == " " ) || c == undefined )
{
- x = d > 0 ? cLen - 2 : 0;
+ x = 0 < d ? cLen - 2 : 0;
}
else if( c == "\n" )
{
@@ -158,6 +161,16 @@
this.PEnd = P + 1;
this.__p = P;
+ this.__fireUpdate();
+ };
+
+ Cursor.prototype.__fireUpdate = function()
+ {
+ if( 0 < this.__suppEvt )
+ {
+ debug.Info( "Event suppressed, suppression level is: " + this.__suppEvt );
+ return;
+ }
this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
};
@@ -270,7 +283,7 @@
this.action = new (Actions[ name ])( this );
this.__pulseMsg = null;
- this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
+ this.__fireUpdate();
};
Cursor.prototype.closeAction = function()
@@ -280,7 +293,7 @@
this.__pulseMsg = this.action.getMessage();
this.action = null;
- this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
+ this.__fireUpdate();
};
// Open, Run, then close an action
@@ -292,9 +305,12 @@
this.__pulseMsg = action.getMessage();
action.dispose();
- this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
+ this.__fireUpdate();
};
+ Cursor.prototype.suppressEvent = function() { ++ this.__suppEvt; };
+ Cursor.prototype.unsuppressEvent = function() { -- this.__suppEvt; };
+
Cursor.prototype.getLine = function()
{
var feeder = this.feeder;
diff --git a/botanjs/src/Components/Vim/State/Stator.js b/botanjs/src/Components/Vim/State/Stator.js
new file mode 100644
index 0000000..a17687d
--- /dev/null
+++ b/botanjs/src/Components/Vim/State/Stator.js
@@ -0,0 +1,71 @@
+(function(){
+ var ns = __namespace( "Components.Vim.State" );
+
+ var Stator = function( cur, start )
+ {
+ this.__cursor = cur;
+ this.__startPosition = start == undefined ? cur.aPos : start;
+ this.__startState = this.__saveCur();
+ };
+
+ Stator.prototype.save = function( insertLength, contentUndo )
+ {
+ var cur = this.__cursor;
+ var feeder = cur.feeder;
+ var startPos = this.__startPosition;
+
+ if( insertLength < 0 )
+ {
+ startPos += insertLength;
+ insertLength = 0;
+ }
+
+ var sSt = this.__startState;
+ var eSt = this.__saveCur();
+
+ var st = sSt;
+ // Calling this repeatedly will swap between UNDO / REDO state
+ return function() {
+ var contentRedo = feeder.content.substr( startPos, insertLength );
+ feeder.content =
+ feeder.content.substring( 0, startPos )
+ + contentUndo
+ + feeder.content.substring( startPos + insertLength );
+ insertLength = contentUndo.length;
+ contentUndo = contentRedo;
+
+ cur.PStart = st.p;
+ cur.PEnd = st.p + 1;
+ cur.X = st.x;
+ cur.Y = st.y;
+ feeder.panX = st.px;
+ feeder.panY = st.py;
+
+ feeder.pan();
+
+ st = ( st == sSt ) ? eSt : sSt;
+ };
+ };
+
+ Stator.prototype.__saveCur = function()
+ {
+ var c = this.__cursor;
+ var obj = {
+ p: c.PStart
+ , x: c.X
+ , y: c.Y
+ , px: c.feeder.panX
+ , py: c.feeder.panY
+ };
+
+ if( 0 < obj.x )
+ {
+ obj.p -= 1;
+ obj.x -= 1;
+ }
+
+ return obj;
+ };
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Stator", Stator );
+})();
diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js
index 368cecb..54c7864 100644
--- a/botanjs/src/Components/Vim/VimArea.js
+++ b/botanjs/src/Components/Vim/VimArea.js
@@ -32,7 +32,7 @@
if ( e.keyCode ) code = e.keyCode;
else if ( e.which ) code = e.which;
- handler( sender, new InputEvent( e ) );
+ handler( sender, new InputEvent( sender, e ) );
};
};
diff --git a/botanjs/src/Components/Vim/_this.js b/botanjs/src/Components/Vim/_this.js
index 04060da..f9e572e 100644
--- a/botanjs/src/Components/Vim/_this.js
+++ b/botanjs/src/Components/Vim/_this.js
@@ -17,6 +17,8 @@
, "UNDO_LIMIT": "Already at oldest change"
, "REDO_LIMIT": "Already at newest change"
+
+ , "LINE_FEWER": "%1 fewer lines"
};
var errors = {
diff --git a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js
index ffd085e..a8e1bdb 100644
--- a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js
+++ b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js
@@ -1,6 +1,8 @@
/** @constructor */
Components.Vim.Controls.InputEvent = function(){};
+/** @type {Components.Vim.VimArea} */
+Components.Vim.Controls.InputEvent.target;
/** @type String */
Components.Vim.Controls.InputEvent.key;
/** @type Boolean */
diff --git a/botanjs/src/externs/Components.Vim.Cursor.js b/botanjs/src/externs/Components.Vim.Cursor.js
index 76fef9d..2f42cdb 100644
--- a/botanjs/src/externs/Components.Vim.Cursor.js
+++ b/botanjs/src/externs/Components.Vim.Cursor.js
@@ -26,9 +26,15 @@ Components.Vim.Cursor.openAction;
Components.Vim.Cursor.openRunAction;
/** @type Function */
Components.Vim.Cursor.closeAction;
+/** @type Function */
+Components.Vim.Cursor.suppressEvent;
+/** @type Function */
+Components.Vim.Cursor.unsuppressEvent;
/** @type {Boolean} */
Components.Vim.Cursor.blink;
+/** @type {Boolean} */
+Components.Vim.Cursor.pSpace;
/** @type {Array} */
Components.Vim.Cursor.lineBuffers;
/** @type Number */