forked from Botanical/BotanJS
347 lines
6.3 KiB
JavaScript
347 lines
6.3 KiB
JavaScript
(function(){
|
|
var ns = __namespace( "Components.Vim.Actions" );
|
|
|
|
/** @type {System.Debug} */
|
|
var debug = __import( "System.Debug" );
|
|
|
|
var Mesg = __import( "Components.Vim.Message" );
|
|
|
|
/** @type {Components.Vim.IAction} */
|
|
var YANK = ns[ NS_INVOKE ]( "YANK" );
|
|
/** @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 = "";
|
|
Cursor.blink = false;
|
|
Cursor.pSpace = true;
|
|
};
|
|
|
|
VISUAL.prototype.__reset = function( Cursor )
|
|
{
|
|
/** @type {Components.Vim.Cursor} */
|
|
this.__cursor = Cursor;
|
|
|
|
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;
|
|
|
|
VISUAL.prototype.dispose = function()
|
|
{
|
|
var c = this.__cursor;
|
|
|
|
c.blink = true;
|
|
c.pSpace = false;
|
|
c.updatePosition();
|
|
|
|
// This fix the highlighting position of missing phantomSpace
|
|
// for maximum filled line
|
|
if( c.feeder.wrap && 0 < c.X )
|
|
{
|
|
c.suppressEvent();
|
|
c.moveX( -1 );
|
|
c.moveX( 1 );
|
|
c.unsuppressEvent();
|
|
}
|
|
};
|
|
|
|
VISUAL.prototype.handler = function( e, done )
|
|
{
|
|
e.preventDefault();
|
|
|
|
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 );
|
|
}
|
|
else if( e.kMap( "d" ) )
|
|
{
|
|
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();
|
|
|
|
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 && startLine.aPos < cur.aPos )
|
|
{
|
|
var o = cur.aPos;
|
|
cur.moveTo( startLine.aPos, true );
|
|
startLine.aPos = o;
|
|
}
|
|
|
|
Action.handler( e, startLine.aPos, lineMode );
|
|
|
|
if( Action.constructor != DELETE )
|
|
{
|
|
cur.moveTo( startLine.aPos );
|
|
}
|
|
|
|
this.__msg = Action.getMessage();
|
|
|
|
Action.dispose();
|
|
cur.unsuppressEvent();
|
|
|
|
startLine.pstart = cur.PStart;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if( e.range )
|
|
{
|
|
cur.suppressEvent();
|
|
|
|
var r = e.range;
|
|
|
|
if( cur.aPos == startLine.aPos )
|
|
{
|
|
cur.moveTo( r.open, true );
|
|
this.__reset( cur );
|
|
startLine = this.__startLine;
|
|
}
|
|
|
|
cur.unsuppressEvent();
|
|
cur.moveTo( r.close, true );
|
|
}
|
|
|
|
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;
|
|
|
|
if( this.__mode == MODE_LINE )
|
|
{
|
|
cur.suppressEvent();
|
|
|
|
cur.lineEnd( true );
|
|
nstart = cur.PStart;
|
|
|
|
cur.moveTo( currAp, true );
|
|
|
|
cur.unsuppressEvent();
|
|
}
|
|
}
|
|
// 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 || line.placeholder ) break;
|
|
pstart += line.toString().length + 1;
|
|
}
|
|
while( line = line.next );
|
|
}
|
|
|
|
var prevPos = pstart;
|
|
var newPos = nstart;
|
|
|
|
var posDiff = newPos - prevPos;
|
|
|
|
var currAp = cur.aPos;
|
|
|
|
// Sets the visual position
|
|
// s-->e
|
|
if( 0 <= posDiff )
|
|
{
|
|
newPos = newPos + 1;
|
|
}
|
|
// e<--s
|
|
else if( posDiff < 0 )
|
|
{
|
|
prevPos += posDiff;
|
|
newPos = pstart + 1;
|
|
}
|
|
|
|
cur.PStart = prevPos;
|
|
cur.PEnd = newPos;
|
|
}
|
|
};
|
|
|
|
VISUAL.prototype.getMessage = function()
|
|
{
|
|
return this.__msg;
|
|
};
|
|
|
|
ns[ NS_EXPORT ]( EX_CLASS, "VISUAL", VISUAL );
|
|
})();
|