forked from Botanical/BotanJS
Merge remote-tracking branch 'vim/master' into Astro
This commit is contained in:
commit
dd12efa607
@ -10,6 +10,7 @@
|
||||
var Stack = __import( "Components.Vim.State.Stack" );
|
||||
|
||||
var Mesg = __import( "Components.Vim.Message" );
|
||||
var beep = __import( "Components.Vim.Beep" );
|
||||
|
||||
var occurence = __import( "System.utils.Perf.CountSubstr" );
|
||||
|
||||
@ -20,19 +21,23 @@
|
||||
this.__cursor = Cursor;
|
||||
this.__nline = 0;
|
||||
this.__startX = Cursor.aPos;
|
||||
this.__panY = this.__cursor.feeder.panY;
|
||||
|
||||
Cursor.suppressEvent();
|
||||
};
|
||||
|
||||
DELETE.prototype.allowMovement = true;
|
||||
|
||||
DELETE.prototype.dispose = function()
|
||||
{
|
||||
|
||||
this.__cursor.unsuppressEvent();
|
||||
};
|
||||
|
||||
DELETE.prototype.handler = function( e, sp )
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
if( e.ModKeys ) return;
|
||||
|
||||
/** @type {Components.Vim.State.Registers} */
|
||||
var reg = e.target.registers;
|
||||
@ -41,21 +46,93 @@
|
||||
var feeder = cur.feeder;
|
||||
|
||||
var Triggered = false;
|
||||
var newLine = false;
|
||||
|
||||
if( sp == undefined )
|
||||
{
|
||||
if( this.__startX != cur.aPos )
|
||||
{
|
||||
Triggered = true;
|
||||
Triggered = true;
|
||||
|
||||
if( e.kMap( "l" ) )
|
||||
sp = this.__startX;
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
sp = this.__startX;
|
||||
// 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;
|
||||
cur.lineEnd( true );
|
||||
sp = cur.aPos;
|
||||
cur.moveY( -1 );
|
||||
cur.lineStart();
|
||||
this.__startX = cur.aPos;
|
||||
}
|
||||
// Remove the current and the preceding line
|
||||
else if( e.kMap( "k" ) )
|
||||
{
|
||||
newLine = true;
|
||||
cur.moveY( 1 );
|
||||
cur.lineEnd( true );
|
||||
sp = cur.aPos;
|
||||
cur.moveY( -1 );
|
||||
cur.lineStart();
|
||||
}
|
||||
else if( this.__startX < currAp )
|
||||
{
|
||||
// Swap the movement
|
||||
// This is to move the REDO / UNDO Cursor
|
||||
// position to the earlier position
|
||||
sp = currAp;
|
||||
cur.moveTo( this.__startX );
|
||||
}
|
||||
}
|
||||
// Remove the current line
|
||||
else
|
||||
{
|
||||
if( e.kMap( "d" ) )
|
||||
{
|
||||
newLine = true;
|
||||
cur.lineEnd( true );
|
||||
sp = cur.aPos;
|
||||
cur.lineStart();
|
||||
}
|
||||
else if( e.range )
|
||||
{
|
||||
sp = e.range.close;
|
||||
cur.moveTo( e.range.open, true );
|
||||
}
|
||||
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
|
||||
else if( e.kMap( "$" ) )
|
||||
{
|
||||
sp = cur.aPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
beep();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else return;
|
||||
}
|
||||
|
||||
var c = feeder.content;
|
||||
@ -70,23 +147,23 @@
|
||||
}
|
||||
|
||||
var removed = c.substring( s, e + 1 );
|
||||
reg.change( removed );
|
||||
reg.change( removed, newLine );
|
||||
|
||||
this.__nline = occurence( removed, "\n" );
|
||||
|
||||
feeder.content = c.substring( 0, s ) + c.substring( e + 1 );
|
||||
|
||||
// Try to keep the original panning if possible
|
||||
feeder.pan( undefined
|
||||
, this.__panY < feeder.panY
|
||||
? this.__panY - feeder.panY
|
||||
: undefined
|
||||
);
|
||||
cur.moveTo( s );
|
||||
|
||||
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();
|
||||
@ -96,8 +173,6 @@
|
||||
|
||||
cur.rec.record( stack );
|
||||
|
||||
feeder.pan();
|
||||
|
||||
return Triggered;
|
||||
};
|
||||
|
||||
@ -105,7 +180,7 @@
|
||||
{
|
||||
if( this.__nline )
|
||||
{
|
||||
return Mesg( "LINE_FEWER", this.__nline );
|
||||
return Mesg( "LINES_FEWER", this.__nline );
|
||||
}
|
||||
|
||||
return "";
|
||||
|
127
botanjs/src/Components/Vim/Actions/FIND.js
Normal file
127
botanjs/src/Components/Vim/Actions/FIND.js
Normal file
@ -0,0 +1,127 @@
|
||||
(function(){
|
||||
var ns = __namespace( "Components.Vim.Actions" );
|
||||
|
||||
/** @type {System.Debug} */
|
||||
var debug = __import( "System.Debug" );
|
||||
|
||||
var Mesg = __import( "Components.Vim.Message" );
|
||||
|
||||
// Private static
|
||||
var PATTERN = "";
|
||||
|
||||
var ParsePattern = function( pattern )
|
||||
{
|
||||
var parsed = "";
|
||||
var l = pattern.length;
|
||||
|
||||
for( var i = 1; i < l; i ++ )
|
||||
{
|
||||
switch( pattern[ i ] )
|
||||
{
|
||||
case "^I":
|
||||
parsed += "\t";
|
||||
break;
|
||||
case "\\":
|
||||
var tok = pattern[ ++ i ];
|
||||
debug.Error( "Unknown escaped token: " + tok );
|
||||
++ i;
|
||||
default:
|
||||
parsed += pattern[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
var RegEx = null;
|
||||
|
||||
try
|
||||
{
|
||||
var RegEx = new RegExp( parsed, "gm" );
|
||||
}
|
||||
catch( ex )
|
||||
{
|
||||
debug.Error( ex );
|
||||
}
|
||||
|
||||
return RegEx;
|
||||
};
|
||||
|
||||
/** @type {Components.Vim.Cursor.IAction} */
|
||||
var FIND = function( Cursor )
|
||||
{
|
||||
/** @type {Components.Vim.Cursor} */
|
||||
this.__cursor = Cursor;
|
||||
this.__msg = "";
|
||||
Cursor.suppressEvent();
|
||||
};
|
||||
|
||||
FIND.prototype.dispose = function()
|
||||
{
|
||||
this.__cursor.unsuppressEvent();
|
||||
};
|
||||
|
||||
FIND.prototype.handler = function( e, p )
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
if( p ) PATTERN = p;
|
||||
|
||||
var search = ParsePattern( PATTERN );
|
||||
|
||||
var content = this.__cursor.feeder.content;
|
||||
|
||||
var cur = this.__cursor;
|
||||
var p = cur.aPos;
|
||||
|
||||
var r;
|
||||
var Hit;
|
||||
var FirstHit;
|
||||
var PrevStack = [];
|
||||
|
||||
while( ( r = search.exec( content ) ) !== null )
|
||||
{
|
||||
if( !FirstHit ) FirstHit = r.index;
|
||||
if( p < r.index )
|
||||
{
|
||||
Hit = r.index;
|
||||
break;
|
||||
}
|
||||
|
||||
PrevStack.push( r.index );
|
||||
}
|
||||
|
||||
if( e.kMap( "N" ) )
|
||||
{
|
||||
Hit = PrevStack[ PrevStack.length - 2 ];
|
||||
if( Hit == undefined )
|
||||
{
|
||||
this.__msg = Mesg( "SEARCH_HIT_TOP" );
|
||||
|
||||
while( ( r = search.exec( content ) ) !== null ) Hit = r.index;
|
||||
}
|
||||
}
|
||||
else if( FirstHit != undefined && Hit == undefined )
|
||||
{
|
||||
// Search Hit Bottom
|
||||
Hit = FirstHit;
|
||||
this.__msg = Mesg( "SEARCH_HIT_BOTTOM" );
|
||||
}
|
||||
else
|
||||
{
|
||||
this.__msg = PATTERN.join( "" )
|
||||
}
|
||||
|
||||
if( Hit == undefined )
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
cur.moveTo( Hit );
|
||||
}
|
||||
};
|
||||
|
||||
FIND.prototype.getMessage = function()
|
||||
{
|
||||
return this.__msg;
|
||||
};
|
||||
|
||||
ns[ NS_EXPORT ]( EX_CLASS, "FIND", FIND );
|
||||
})();
|
@ -29,16 +29,23 @@
|
||||
/** @type {Components.Vim.Cursor} */
|
||||
this.__cursor = Cursor;
|
||||
|
||||
this.__Stator = new Stator( Cursor );
|
||||
this.__stator = new Stator( Cursor );
|
||||
|
||||
// Initialize this stack
|
||||
this.__rec( "", true );
|
||||
|
||||
var l = this.__cursor.feeder.firstBuffer.cols;
|
||||
var msg = Mesg( "INSERT" );
|
||||
|
||||
for( var i = msg.length; i < l; i ++ ) msg += " ";
|
||||
this.__msg = msg;
|
||||
};
|
||||
|
||||
INSERT.prototype.allowMovement = false;
|
||||
|
||||
INSERT.prototype.dispose = function()
|
||||
{
|
||||
this.__msg = "";
|
||||
this.__rec( "", true );
|
||||
this.__cursor.moveX( -1 );
|
||||
};
|
||||
@ -55,7 +62,7 @@
|
||||
) return;
|
||||
|
||||
this.__stack.store(
|
||||
this.__Stator.save( this.__insertLength, this.__contentUndo )
|
||||
this.__stator.save( this.__insertLength, this.__contentUndo )
|
||||
);
|
||||
|
||||
this.__cursor.rec.record( this.__stack );
|
||||
@ -158,11 +165,7 @@
|
||||
|
||||
INSERT.prototype.getMessage = function()
|
||||
{
|
||||
var l = this.__cursor.feeder.firstBuffer.cols;
|
||||
var msg = Mesg( "INSERT" );
|
||||
|
||||
for( var i = msg.length; i < l; i ++ ) msg += " ";
|
||||
return msg;
|
||||
return this.__msg;
|
||||
};
|
||||
|
||||
ns[ NS_EXPORT ]( EX_CLASS, "INSERT", INSERT );
|
||||
|
@ -1,8 +1,6 @@
|
||||
(function(){
|
||||
var ns = __namespace( "Components.Vim.Actions" );
|
||||
|
||||
/** @type {Components.Vim.State.Stack} */
|
||||
var Stack = __import( "Components.Vim.State.Stack" );
|
||||
/** @type {System.Debug} */
|
||||
var debug = __import( "System.Debug" );
|
||||
|
||||
|
101
botanjs/src/Components/Vim/Actions/PUT.js
Normal file
101
botanjs/src/Components/Vim/Actions/PUT.js
Normal file
@ -0,0 +1,101 @@
|
||||
(function(){
|
||||
var ns = __namespace( "Components.Vim.Actions" );
|
||||
|
||||
/** @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 PUT = function( Cursor )
|
||||
{
|
||||
/** @type {Components.Vim.Cursor} */
|
||||
this.__cursor = Cursor;
|
||||
this.__msg = "";
|
||||
Cursor.suppressEvent();
|
||||
};
|
||||
|
||||
PUT.prototype.allowMovement = false;
|
||||
|
||||
PUT.prototype.dispose = function()
|
||||
{
|
||||
this.__cursor.unsuppressEvent();
|
||||
};
|
||||
|
||||
PUT.prototype.handler = function( e )
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
// TODO: Get the input for determinating registers
|
||||
var inputStack = false;
|
||||
|
||||
var cput = this.__cursor.Vim.registers.get( inputStack );
|
||||
if( !cput ) return true;
|
||||
|
||||
var clen = cput.length;
|
||||
var nLines = occurence( cput, "\n" );
|
||||
|
||||
var cur = this.__cursor;
|
||||
var feeder = cur.feeder;
|
||||
|
||||
var newLine = cput.newLine;
|
||||
|
||||
// Compensation
|
||||
var c = e.kMap( "P" ) ? 0 : -1;
|
||||
|
||||
if( newLine )
|
||||
{
|
||||
cur.moveY( -c );
|
||||
cur.lineStart();
|
||||
}
|
||||
|
||||
var stator = new Stator( cur );
|
||||
var aP = cur.aPos;
|
||||
|
||||
feeder.content = feeder.content.substring( 0, aP )
|
||||
+ cput
|
||||
+ feeder.content.substring( aP );
|
||||
|
||||
feeder.pan();
|
||||
|
||||
cur.moveTo( 0 < nLines ? aP : aP + clen, true );
|
||||
|
||||
var stack = new Stack();
|
||||
|
||||
if( newLine )
|
||||
{
|
||||
var f = stator.save( clen, "" );
|
||||
stack.store( function()
|
||||
{
|
||||
f();
|
||||
cur.moveY( c );
|
||||
} );
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.store( stator.save( clen, "" ) );
|
||||
}
|
||||
cur.rec.record( stack );
|
||||
|
||||
this.__put = cput;
|
||||
|
||||
if( nLines )
|
||||
{
|
||||
this.__msg = Mesg( "LINES_MORE", nLines );
|
||||
}
|
||||
|
||||
cur.moveX( -1 );
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
PUT.prototype.getMessage = function()
|
||||
{
|
||||
return this.__msg;
|
||||
};
|
||||
|
||||
ns[ NS_EXPORT ]( EX_CLASS, "PUT", PUT );
|
||||
})();
|
@ -16,7 +16,6 @@
|
||||
{
|
||||
this.__reset( Cursor );
|
||||
this.__msg = Mesg( "VISUAL" );
|
||||
this.__leaveMesg = "";
|
||||
|
||||
Cursor.blink = false;
|
||||
Cursor.pSpace = true;
|
||||
@ -35,11 +34,22 @@
|
||||
|
||||
VISUAL.prototype.dispose = function()
|
||||
{
|
||||
this.__msg = this.__leaveMesg;
|
||||
this.__cursor.blink = true;
|
||||
this.__cursor.pSpace = false;
|
||||
this.__cursor.PStart = this.__selStart;
|
||||
this.__cursor.PEnd = this.__selStart + 1;
|
||||
var c = this.__cursor;
|
||||
|
||||
c.blink = true;
|
||||
c.pSpace = false;
|
||||
c.PStart = this.__selStart;
|
||||
c.PEnd = this.__selStart + 1;
|
||||
|
||||
// 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 )
|
||||
@ -76,7 +86,13 @@
|
||||
}
|
||||
|
||||
Action.handler( e, this.__startaP );
|
||||
this.__leaveMesg = Action.getMessage();
|
||||
|
||||
if( Action.constructor != DELETE )
|
||||
{
|
||||
cur.moveTo( this.__startaP );
|
||||
}
|
||||
|
||||
this.__msg = Action.getMessage();
|
||||
|
||||
Action.dispose();
|
||||
cur.unsuppressEvent();
|
||||
|
@ -1,31 +1,161 @@
|
||||
(function(){
|
||||
var ns = __namespace( "Components.Vim.Actions" );
|
||||
|
||||
/** @type {System.Debug} */
|
||||
var debug = __import( "System.Debug" );
|
||||
|
||||
var Mesg = __import( "Components.Vim.Message" );
|
||||
var beep = __import( "Components.Vim.Beep" );
|
||||
|
||||
var occurence = __import( "System.utils.Perf.CountSubstr" );
|
||||
|
||||
/** @type {Components.Vim.Cursor.IAction} */
|
||||
var YANK = function( Cursor )
|
||||
{
|
||||
/** @type {Components.Vim.Cursor} */
|
||||
this.__cursor = Cursor;
|
||||
this.__startX = Cursor.aPos;
|
||||
this.__msg = "";
|
||||
|
||||
Cursor.suppressEvent();
|
||||
};
|
||||
|
||||
YANK.prototype.allowMovement = true;
|
||||
|
||||
YANK.prototype.dispose = function()
|
||||
{
|
||||
|
||||
this.__cursor.unsuppressEvent();
|
||||
};
|
||||
|
||||
YANK.prototype.handler = function( e )
|
||||
YANK.prototype.handler = function( e, sp )
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
if( e.ModKeys || e.kMap( "i" ) ) return;
|
||||
|
||||
/** @type {Components.Vim.State.Registers} */
|
||||
var reg = e.target.registers;
|
||||
|
||||
var cur = this.__cursor;
|
||||
var feeder = cur.feeder;
|
||||
|
||||
var Triggered = false;
|
||||
|
||||
var newLine = false;
|
||||
if( sp == undefined )
|
||||
{
|
||||
Triggered = true;
|
||||
|
||||
sp = this.__startX;
|
||||
|
||||
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;
|
||||
cur.lineEnd( true );
|
||||
sp = cur.aPos;
|
||||
cur.moveY( -1 );
|
||||
cur.lineStart();
|
||||
this.__startX = cur.aPos;
|
||||
}
|
||||
// Remove the current and the preceding line
|
||||
else if( e.kMap( "k" ) )
|
||||
{
|
||||
newLine = true;
|
||||
cur.moveY( 1 );
|
||||
cur.lineEnd( true );
|
||||
sp = cur.aPos;
|
||||
cur.moveY( -1 );
|
||||
cur.lineStart();
|
||||
}
|
||||
else if( this.__startX < currAp )
|
||||
{
|
||||
// Swap the movement
|
||||
// This is to move the REDO / UNDO Cursor
|
||||
// position to the earlier position
|
||||
sp = currAp;
|
||||
cur.moveTo( this.__startX );
|
||||
}
|
||||
}
|
||||
// Remove the current line
|
||||
else
|
||||
{
|
||||
if( e.kMap( "y" ) )
|
||||
{
|
||||
newLine = true;
|
||||
cur.lineEnd( true );
|
||||
sp = cur.aPos;
|
||||
cur.lineStart();
|
||||
}
|
||||
else if( e.range )
|
||||
{
|
||||
sp = e.range.close;
|
||||
cur.moveTo( e.range.open, true );
|
||||
}
|
||||
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
|
||||
else if( e.kMap( "$" ) )
|
||||
{
|
||||
sp = cur.aPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
beep();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var s = sp;
|
||||
var e = cur.aPos;
|
||||
|
||||
if( e < s )
|
||||
{
|
||||
s = cur.aPos;
|
||||
e = sp;
|
||||
}
|
||||
|
||||
cur.moveTo( s );
|
||||
|
||||
var yText = cur.feeder.content.substring( s, e + 1 );
|
||||
|
||||
reg.yank( yText, newLine );
|
||||
|
||||
var nline = occurence( yText, "\n" );
|
||||
if( nline )
|
||||
{
|
||||
this.__msg = Mesg( "LINES_YANKED", nline );
|
||||
}
|
||||
|
||||
return Triggered;
|
||||
};
|
||||
|
||||
YANK.prototype.getMessage = function()
|
||||
{
|
||||
return "<TODO> YANK COMMAND";
|
||||
return this.__msg;
|
||||
};
|
||||
|
||||
ns[ NS_EXPORT ]( EX_CLASS, "YANK", YANK );
|
||||
|
@ -3,6 +3,10 @@
|
||||
|
||||
/** @type {System.Debug} */
|
||||
var debug = __import( "System.Debug" );
|
||||
|
||||
/** @type {Components.Vim.Ex.Command} */
|
||||
var ExCommand = __import( "Components.Vim.Ex.Command" );
|
||||
|
||||
var beep = ns[ NS_INVOKE ]( "Beep" );
|
||||
|
||||
var SHIFT = 1 << 9;
|
||||
@ -14,8 +18,12 @@
|
||||
var KEY_ALT = 18;
|
||||
|
||||
var BACKSPACE = 8;
|
||||
var TAB = 9;
|
||||
var ENTER = 13;
|
||||
var DELETE = 46;
|
||||
|
||||
var UP = 38; var DOWN = 40; var LEFT = 37; var RIGHT = 39;
|
||||
|
||||
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;
|
||||
|
||||
@ -35,6 +43,7 @@
|
||||
var F11 = 122; var F12 = 123;
|
||||
|
||||
var COMMA = 188; var FULLSTOP = 190;
|
||||
var SLASH = 191; var BACK_SLASH = 220;
|
||||
|
||||
var __maps = {};
|
||||
var Map = function( str )
|
||||
@ -69,6 +78,13 @@
|
||||
{
|
||||
case "BS": kCode = Mod + BACKSPACE; break;
|
||||
case "Del": kCode = Mod + DELETE; break;
|
||||
case "Enter": kCode = Mod + ENTER; break;
|
||||
case "Tab": kCode = Mod + TAB; break;
|
||||
|
||||
case "Up": kCode = Mod + UP; break;
|
||||
case "Down": kCode = Mod + DOWN; break;
|
||||
case "Left": kCode = Mod + LEFT; break;
|
||||
case "Right": kCode = Mod + RIGHT; break;
|
||||
|
||||
case "A": Mod = SHIFT; case "a": kCode = Mod + A; break;
|
||||
case "B": Mod = SHIFT; case "b": kCode = Mod + B; break;
|
||||
@ -126,14 +142,18 @@
|
||||
this.__sfeeder = vimArea.statusFeeder;
|
||||
|
||||
this.__ccur = this.__cfeeder.cursor;
|
||||
|
||||
// Dived composite command handler
|
||||
// Has full control of the key input, except Esc
|
||||
this.__divedCCmd = null;
|
||||
};
|
||||
|
||||
Controls.prototype.__comp = function( e, handler )
|
||||
Controls.prototype.__composite = function( e, handler )
|
||||
{
|
||||
if( handler )
|
||||
{
|
||||
if( !this.__compReg ) this.__compReg = [];
|
||||
this.__compReg.push({
|
||||
if( !this.__compositeReg ) this.__compositeReg = [];
|
||||
this.__compositeReg.push({
|
||||
keys: Array.prototype.slice.call( arguments, 2 )
|
||||
, handler: handler
|
||||
, i: 0
|
||||
@ -143,9 +163,9 @@
|
||||
|
||||
var kCode = e.keyCode;
|
||||
|
||||
for( var i = 0; i < this.__compReg.length; i ++ )
|
||||
for( var i = 0; i < this.__compositeReg.length; i ++ )
|
||||
{
|
||||
var compReg = this.__compReg[i];
|
||||
var compReg = this.__compositeReg[i];
|
||||
var keys = compReg.keys;
|
||||
|
||||
if( keys[ compReg.i ++ ] == kCode )
|
||||
@ -153,7 +173,7 @@
|
||||
if( compReg.i == keys.length )
|
||||
{
|
||||
compReg.handler( e );
|
||||
this.__compReg = null;
|
||||
this.__compositeReg = null;
|
||||
this.__cMovement = false;
|
||||
}
|
||||
|
||||
@ -161,8 +181,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
if( this.__compReg ) beep();
|
||||
this.__compReg = null;
|
||||
if( this.__compositeReg ) beep();
|
||||
this.__compositeReg = null;
|
||||
this.__cMovement = false;
|
||||
return false;
|
||||
};
|
||||
@ -178,22 +198,54 @@
|
||||
case SHIFT + A: // Append at the line end
|
||||
ccur.lineEnd();
|
||||
case A: // Append
|
||||
this.__cMoveX( 1, true, true );
|
||||
ccur.moveX( 1, true, true );
|
||||
case I: // Insert
|
||||
ccur.openAction( "INSERT" );
|
||||
break;
|
||||
|
||||
case SHIFT + O: // new line before insert
|
||||
ccur.lineStart();
|
||||
ccur.openAction( "INSERT" );
|
||||
ccur.action.handler( new InputEvent( e.sender, "Enter" ) );
|
||||
ccur.moveY( -1 );
|
||||
break;
|
||||
case O: // new line insert
|
||||
ccur.lineEnd( true );
|
||||
ccur.openAction( "INSERT" );
|
||||
ccur.action.handler( new InputEvent( e.sender, "Enter" ) );
|
||||
break;
|
||||
|
||||
case U: // Undo
|
||||
ccur.openRunAction( "UNDO", e );
|
||||
break;
|
||||
case CTRL + R: // Redo
|
||||
ccur.openRunAction( "REDO", e );
|
||||
break;
|
||||
|
||||
case D: // Del with motion
|
||||
ccur.openAction( "DELETE" );
|
||||
break;
|
||||
case X: // Del
|
||||
case Y: // Yank with motion
|
||||
ccur.openAction( "YANK" );
|
||||
break;
|
||||
|
||||
case P: // Put
|
||||
ccur.suppressEvent();
|
||||
ccur.moveX( 1, false, true );
|
||||
ccur.unsuppressEvent();
|
||||
case SHIFT + P: // Put before
|
||||
ccur.openRunAction( "PUT", e );
|
||||
break;
|
||||
|
||||
case SHIFT + X: // Delete before
|
||||
if( !this.__cMoveX( -1 ) ) break;
|
||||
case X: // Del
|
||||
if( ccur.getLine().content == "" )
|
||||
{
|
||||
beep();
|
||||
break;
|
||||
}
|
||||
ccur.openRunAction( "DELETE", e, ccur.aPos );
|
||||
break;
|
||||
case SHIFT + U: // Undo previous changes in oneline
|
||||
break;
|
||||
@ -226,7 +278,12 @@
|
||||
|
||||
var x = ccur.X;
|
||||
ccur.moveX( a, b, c || ccur.pSpace );
|
||||
if( ccur.X == x ) beep();
|
||||
if( ccur.X == x )
|
||||
{
|
||||
beep();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Controls.prototype.__cMoveY = function( a )
|
||||
@ -238,20 +295,22 @@
|
||||
ccur.moveY( a );
|
||||
if( y == ( ccur.Y + cfeeder.panY ) )
|
||||
{
|
||||
if( 0 < a && !cfeeder.EOF ) return;
|
||||
if( 0 < a && !cfeeder.EOF ) return true;
|
||||
beep();
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
Controls.prototype.__cursorCommand = function( e )
|
||||
{
|
||||
var kCode = e.keyCode;
|
||||
|
||||
if( this.__cMovement && this.__comp )
|
||||
if( this.__cMovement )
|
||||
{
|
||||
if( !e.ModKeys )
|
||||
{
|
||||
this.__comp( e );
|
||||
this.__composite( e );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -270,14 +329,16 @@
|
||||
case J: this.__cMoveY( 1 ); break; // Down
|
||||
|
||||
case CTRL + F: // Page Down
|
||||
if( cfeeder.firstBuffer.next.placeholder )
|
||||
if( cfeeder.firstBuffer.nextLine.placeholder )
|
||||
{
|
||||
beep();
|
||||
break;
|
||||
}
|
||||
|
||||
var oPan = cfeeder.panY;
|
||||
cfeeder.pan( undefined, vima.rows - 1 );
|
||||
cfeeder.pan( undefined, cfeeder.moreAt );
|
||||
cfeeder.softReset();
|
||||
|
||||
ccur.moveY( -ccur.Y );
|
||||
|
||||
break;
|
||||
@ -287,7 +348,9 @@
|
||||
beep();
|
||||
break;
|
||||
}
|
||||
cfeeder.pan( undefined, -vima.rows + 1 );
|
||||
cfeeder.pan( undefined, -cfeeder.moreAt );
|
||||
cfeeder.softReset();
|
||||
|
||||
ccur.moveY( -ccur.Y );
|
||||
if( !cfeeder.EOF ) ccur.moveY( cfeeder.moreAt );
|
||||
break;
|
||||
@ -340,7 +403,7 @@
|
||||
this.__cMovement = true;
|
||||
|
||||
// Word boundary
|
||||
this.__comp( e, function( e2 ) {
|
||||
this.__composite( e, function( e2 ) {
|
||||
var WordMatch = analyzer.wordAt( ccur.aPos );
|
||||
e2.__range = WordMatch;
|
||||
}, W );
|
||||
@ -359,25 +422,36 @@
|
||||
};
|
||||
|
||||
// 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 );
|
||||
this.__composite( e, bracket , SHIFT + _0 );
|
||||
this.__composite( e, bracket, SHIFT + _9 );
|
||||
this.__composite( e, squareBracket, S_BRACKET_L );
|
||||
this.__composite( e, squareBracket, S_BRACKET_R );
|
||||
this.__composite( e, curlyBracket, SHIFT + S_BRACKET_L );
|
||||
this.__composite( e, curlyBracket, SHIFT + S_BRACKET_R );
|
||||
break;
|
||||
|
||||
case G: // Go to top
|
||||
this.__cMovement = true;
|
||||
this.__comp( e, function(){
|
||||
this.__composite( e, function(){
|
||||
ccur.moveY( -Number.MAX_VALUE );
|
||||
ccur.moveX( -Number.MAX_VALUE, true );
|
||||
}, G );
|
||||
this.__comp( e, function(){
|
||||
this.__composite( e, function(){
|
||||
ccur.openRunAction( "PRINT_HEX", e );
|
||||
}, _8 );
|
||||
break;
|
||||
|
||||
case SHIFT + N: // Next Search
|
||||
case N: // Next Search
|
||||
ccur.openRunAction( "FIND", e );
|
||||
break;
|
||||
|
||||
case SLASH: // "/" Search movement
|
||||
this.__cMovement = true;
|
||||
|
||||
this.__divedCCmd = new ExCommand( ccur, "/" );
|
||||
this.__divedCCmd.handler( e );
|
||||
break;
|
||||
default:
|
||||
cursorHandled = false;
|
||||
}
|
||||
@ -398,12 +472,40 @@
|
||||
) return;
|
||||
|
||||
// Clear composite command
|
||||
if( e.Escape && this.__compReg )
|
||||
if( e.Escape )
|
||||
{
|
||||
this.__compReg = null;
|
||||
var b = false;
|
||||
this.__cMovement = false;
|
||||
beep();
|
||||
return;
|
||||
|
||||
if( this.__compositeReg )
|
||||
{
|
||||
b = true;
|
||||
this.__compositeReg = null;
|
||||
}
|
||||
else if( this.__divedCCmd )
|
||||
{
|
||||
b = true;
|
||||
this.__divedCCmd.dispose();
|
||||
this.__divedCCmd = null;
|
||||
}
|
||||
|
||||
if( b )
|
||||
{
|
||||
beep();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( this.__divedCCmd )
|
||||
{
|
||||
if( this.__divedCCmd.handler( e ) )
|
||||
{
|
||||
this.__divedCCmd.dispose();
|
||||
this.__cMovement = false;
|
||||
this.__divedCCmd = null;
|
||||
}
|
||||
|
||||
if( e.canceled ) return;
|
||||
}
|
||||
|
||||
var cfeeder = this.__cfeeder;
|
||||
@ -422,7 +524,15 @@
|
||||
else
|
||||
{
|
||||
if( ccur.action.allowMovement )
|
||||
{
|
||||
var SubCommand = !this.__compositeReg;
|
||||
this.__cursorCommand( e, kCode );
|
||||
if( SubCommand && this.__compositeReg )
|
||||
{
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( ccur.action.handler( e ) )
|
||||
{
|
||||
@ -440,28 +550,43 @@
|
||||
|
||||
var InputEvent = function( sender, e )
|
||||
{
|
||||
this.__e = e;
|
||||
this.__target = sender;
|
||||
this.__canceled = false;
|
||||
|
||||
var c = this.__e.keyCode;
|
||||
if( typeof( e ) == "string" )
|
||||
{
|
||||
this.__key = e;
|
||||
this.__modKeys = 0;
|
||||
this.__kCode = Map( e );
|
||||
this.__escape = this.__kCode == ESC;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.__e = e;
|
||||
|
||||
this.__escape = c == ESC || ( e.ctrlKey && c == C );
|
||||
this.__kCode = c
|
||||
+ ( e.shiftKey || e.getModifierState( "CapsLock" ) ? SHIFT : 0 )
|
||||
+ ( e.ctrlKey ? CTRL : 0 )
|
||||
+ ( e.altKey ? ALT : 0 );
|
||||
var c = this.__e.keyCode;
|
||||
|
||||
this.__modKeys = c == KEY_SHIFT || c == KEY_CTRL || c == KEY_ALT;
|
||||
this.__key = e.key;
|
||||
this.__escape = c == ESC || ( e.ctrlKey && c == C );
|
||||
this.__kCode = c
|
||||
+ ( e.shiftKey || e.getModifierState( "CapsLock" ) ? SHIFT : 0 )
|
||||
+ ( e.ctrlKey ? CTRL : 0 )
|
||||
+ ( e.altKey ? ALT : 0 );
|
||||
|
||||
this.__modKeys = c == KEY_SHIFT || c == KEY_CTRL || c == KEY_ALT;
|
||||
this.__key = e.key;
|
||||
}
|
||||
|
||||
this.__range = null;
|
||||
};
|
||||
|
||||
InputEvent.prototype.cancel = function() { this.__canceled = true; };
|
||||
|
||||
__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; } );
|
||||
__readOnly( InputEvent.prototype, "canceled", function() { return this.__canceled; } );
|
||||
|
||||
__readOnly( InputEvent.prototype, "range", function() {
|
||||
|
||||
|
@ -83,6 +83,9 @@
|
||||
this.pSpace = false;
|
||||
|
||||
this.__suppEvt = 0;
|
||||
|
||||
// Offset compensation for max filled wrapped line
|
||||
this.__off = 0;
|
||||
};
|
||||
|
||||
// Set by VimArea
|
||||
@ -92,7 +95,14 @@
|
||||
Cursor.prototype.moveTo = function( aPos, phantomSpace )
|
||||
{
|
||||
var content = this.feeder.content;
|
||||
var lastLineNum = this.getLine().lineNum;
|
||||
var pline = this.getLine();
|
||||
var lastLineNum = pline.lineNum;
|
||||
|
||||
if( pline.placeholder )
|
||||
{
|
||||
lastLineNum = 0;
|
||||
this.Y = 0;
|
||||
}
|
||||
|
||||
var expLineNum = 0;
|
||||
var lineStart = 0;
|
||||
@ -111,7 +121,11 @@
|
||||
var jumpY = expLineNum - lastLineNum;
|
||||
var jumpX = aPos < lineStart ? lineStart - aPos : aPos - lineStart;
|
||||
|
||||
jumpX += Math.ceil( jumpX / pline.cols ) - 1;
|
||||
|
||||
if( jumpY ) this.moveY( jumpY );
|
||||
|
||||
// This needed because first line does not contain first "\n" character
|
||||
if( 0 < this.getLine().lineNum && lineStart <= aPos ) jumpX --;
|
||||
|
||||
this.moveX( - Number.MAX_VALUE );
|
||||
@ -123,13 +137,20 @@
|
||||
Cursor.prototype.moveX = function( d, penetrate, phantomSpace )
|
||||
{
|
||||
var x = this.pX;
|
||||
|
||||
var updatePx = Boolean( d );
|
||||
|
||||
if( 0 < this.__off )
|
||||
{
|
||||
d += this.__off;
|
||||
this.__off = 0;
|
||||
}
|
||||
|
||||
if( updatePx ) x = this.X + d;
|
||||
|
||||
if( !d ) d = 1;
|
||||
|
||||
var buffs = this.feeder.lineBuffers;
|
||||
var feeder = this.feeder;
|
||||
var buffs = feeder.lineBuffers;
|
||||
|
||||
if( penetrate )
|
||||
{
|
||||
@ -146,23 +167,61 @@
|
||||
var content = line.visualLines.join( "\n" );
|
||||
var cLen = content.length;
|
||||
|
||||
var lineEnd = 0;
|
||||
var hasPhantomSpace = true;
|
||||
|
||||
// Empty lines has length of 1
|
||||
// If length larger than a, need to compensate the lineEnd
|
||||
// for phantomSpace
|
||||
if( 1 < cLen )
|
||||
{
|
||||
// Begin check if whether this line contains phantomSpace
|
||||
var lineNum = line.lineNum - 1;
|
||||
var str = feeder.content;
|
||||
for( var i = str.indexOf( "\n" ), j = 0; 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 );
|
||||
end = end == -1 ? str.length : end;
|
||||
|
||||
// Actual LineLength
|
||||
var hasPhantomSpace = 0 < ( end - i - 1 ) % line.cols;
|
||||
|
||||
if( hasPhantomSpace )
|
||||
{
|
||||
lineEnd = phantomSpace ? cLen - 1 : cLen - 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
lineEnd = phantomSpace ? cLen : cLen - 1;
|
||||
}
|
||||
}
|
||||
|
||||
var c = content[ x ];
|
||||
|
||||
// Motion includes empty lines before cursor end
|
||||
if( ( phantomSpace && cLen - 1 <= x ) || ( cLen == 1 && c == undefined ) )
|
||||
// Whether x is at line boundary
|
||||
var boundary = c == undefined || ( cLen == x + 1 && c == " " );
|
||||
|
||||
if( boundary )
|
||||
{
|
||||
x = 0 < d ? cLen - 1 : 0;
|
||||
}
|
||||
// ( 2 < cLen ) motion excludes empty lines at cursor end
|
||||
else if( ( 2 <= cLen && x == cLen - 1 && c == " " ) || c == undefined )
|
||||
{
|
||||
x = 0 < d ? cLen - 2 : 0;
|
||||
x = 0 < d ? lineEnd : 0;
|
||||
}
|
||||
else if( c == "\n" )
|
||||
{
|
||||
x += d;
|
||||
}
|
||||
|
||||
// Wordwrap phantomSpace movement compensation on max filled lines
|
||||
if( feeder.wrap && boundary && !hasPhantomSpace && phantomSpace )
|
||||
{
|
||||
this.__off = 1;
|
||||
}
|
||||
|
||||
this.X = x;
|
||||
|
||||
if( updatePx )
|
||||
@ -170,6 +229,7 @@
|
||||
this.pX = x;
|
||||
this.updatePosition();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Cursor.prototype.lineStart = function()
|
||||
@ -186,10 +246,11 @@
|
||||
|
||||
Cursor.prototype.updatePosition = function()
|
||||
{
|
||||
var P = this.X + LineOffset( this.feeder.lineBuffers, this.Y );
|
||||
var feeder = this.feeder;
|
||||
var P = this.X + LineOffset( feeder.lineBuffers, this.Y ) + this.__off;
|
||||
|
||||
this.PStart = P;
|
||||
this.PEnd = P + 1;
|
||||
this.__p = P;
|
||||
|
||||
this.__visualUpdate();
|
||||
};
|
||||
@ -310,6 +371,9 @@
|
||||
Cursor.prototype.openAction = function( name )
|
||||
{
|
||||
if( this.action ) this.action.dispose();
|
||||
|
||||
debug.Info( "openAction: " + name );
|
||||
|
||||
this.action = new (Actions[ name ])( this );
|
||||
this.__pulseMsg = null;
|
||||
|
||||
@ -323,6 +387,8 @@
|
||||
this.__pulseMsg = this.action.getMessage();
|
||||
this.action = null;
|
||||
|
||||
debug.Info( "closeAction: " + this.__pulseMsg );
|
||||
|
||||
// Reset the analyzed content
|
||||
this.Vim.contentAnalyzer.reset();
|
||||
|
||||
@ -330,11 +396,11 @@
|
||||
};
|
||||
|
||||
// Open, Run, then close an action
|
||||
Cursor.prototype.openRunAction = function( name, e )
|
||||
Cursor.prototype.openRunAction = function( name, e, arg1 )
|
||||
{
|
||||
/** @type {Components.Vim.IAction} */
|
||||
var action = new (Actions[ name ])( this );
|
||||
action.handler( e );
|
||||
action.handler( e, arg1 );
|
||||
this.__pulseMsg = action.getMessage();
|
||||
action.dispose();
|
||||
|
||||
@ -431,6 +497,8 @@
|
||||
return p;
|
||||
} );
|
||||
|
||||
__readOnly( Cursor.prototype, "face", function() { return "\u2588"; } );
|
||||
|
||||
__readOnly( Cursor.prototype, "message", function()
|
||||
{
|
||||
if( this.__pulseMsg )
|
||||
|
228
botanjs/src/Components/Vim/Ex/Command.js
Normal file
228
botanjs/src/Components/Vim/Ex/Command.js
Normal file
@ -0,0 +1,228 @@
|
||||
(function(){
|
||||
var ns = __namespace( "Components.Vim.Ex" );
|
||||
|
||||
/** @type {System.Cycle} */
|
||||
var Cycle = __import( "System.Cycle" );
|
||||
/** @type {System.Debug} */
|
||||
var debug = __import( "System.Debug" );
|
||||
/** @type {System.utils.Perf} */
|
||||
var Perf = __import( "System.utils.Perf" );
|
||||
|
||||
/** @type {Components.Vim.State.History} */
|
||||
var History = __import( "Components.Vim.State.History" );
|
||||
var Mesg = __import( "Components.Vim.Message" );
|
||||
var beep = __import( "Components.Vim.Beep" );
|
||||
|
||||
// This is for security & privacy concerns?
|
||||
var ZMap = {
|
||||
"/": Perf.uuid
|
||||
, ":" : Perf.uuid
|
||||
};
|
||||
|
||||
/** @type {Components.Vim.Cursor.IAction} */
|
||||
var Command = function( Cursor, Mode )
|
||||
{
|
||||
var _self = this;
|
||||
if( !ZMap[ Mode ] ) throw new Error( "Unsupport mode: " + Mode );
|
||||
|
||||
/** @type {Components.Vim.Cursor} */
|
||||
this.__cursor = Cursor;
|
||||
|
||||
this.__statusBar = Cursor.Vim.statusBar;
|
||||
|
||||
this.__mode = Mode;
|
||||
this.__hist = new History( ZMap[ Mode ] );
|
||||
|
||||
this.__command = [];
|
||||
this.__currentCommand = null;
|
||||
this.__blinkId = "ExCommandBlinkCycle" + Perf.uuid;
|
||||
this.__curPos = 0;
|
||||
|
||||
var feeder = Cursor.feeder;
|
||||
|
||||
var __blink = false;
|
||||
var __holdBlink = false;
|
||||
this.__blink = function()
|
||||
{
|
||||
__blink = true;
|
||||
__holdBlink = true
|
||||
};
|
||||
|
||||
Cycle.perma( this.__blinkId, function()
|
||||
{
|
||||
if( __holdBlink ) __holdBlink = false;
|
||||
else __blink = !__blink;
|
||||
|
||||
feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
|
||||
}, 600 );
|
||||
|
||||
this.__doBlink = function()
|
||||
{
|
||||
var c = "";
|
||||
var comm = _self.__command;
|
||||
var pos = _self.__curPos;
|
||||
var cLen = comm.length;
|
||||
var faced = true;
|
||||
|
||||
for( var i = 0; i < cLen; i ++ )
|
||||
{
|
||||
var v = comm[i];
|
||||
if( __blink && i == pos )
|
||||
{
|
||||
face = true;
|
||||
v = Cursor.face + v.substr( 1 );
|
||||
}
|
||||
|
||||
c+= v;
|
||||
}
|
||||
|
||||
if( __blink && cLen <= pos )
|
||||
{
|
||||
c += Cursor.face;
|
||||
}
|
||||
|
||||
return c;
|
||||
};
|
||||
|
||||
this.__statusBar.override = this.__doBlink;
|
||||
};
|
||||
|
||||
Command.prototype.dispose = function()
|
||||
{
|
||||
this.__statusBar.override = null;
|
||||
|
||||
Cycle.permaRemove( this.__blinkId );
|
||||
var feeder = this.__cursor.feeder;
|
||||
feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
|
||||
};
|
||||
|
||||
Command.prototype.handler = function( e )
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
if( e.ModKeys ) return;
|
||||
|
||||
this.__blink();
|
||||
|
||||
var InputKey = null;
|
||||
|
||||
var histNav = false;
|
||||
|
||||
if( e.kMap( "Tab" ) )
|
||||
{
|
||||
InputKey = "^I";
|
||||
}
|
||||
else if( e.kMap( "C-v" ) )
|
||||
{
|
||||
this.__direct = true;
|
||||
}
|
||||
else if( e.kMap( "BS" ) )
|
||||
{
|
||||
if( this.__curPos == 1 && 1 < this.__command.length )
|
||||
return false;
|
||||
|
||||
this.__command.splice( --this.__curPos, 1 );
|
||||
if( this.__command.length == 0 )
|
||||
{
|
||||
e.cancel();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if( e.kMap( "Del" ) )
|
||||
{
|
||||
this.__command.splice( this.__curPos, 1 );
|
||||
}
|
||||
else if( e.kMap( "Enter" ) )
|
||||
{
|
||||
this.__process( e );
|
||||
return true;
|
||||
}
|
||||
else if( e.kMap( "Left" ) )
|
||||
{
|
||||
if( 1 < this.__curPos ) this.__curPos --;
|
||||
}
|
||||
else if( e.kMap( "Right" ) )
|
||||
{
|
||||
if( this.__curPos < this.__command.length )
|
||||
this.__curPos ++;
|
||||
}
|
||||
|
||||
// History stepping
|
||||
else if( histNav = e.kMap( "Up" ) ) // History navigations
|
||||
{
|
||||
if( !this.__currentCommand )
|
||||
{
|
||||
this.__currentCommand = this.__command;
|
||||
}
|
||||
|
||||
var n = this.__hist.prev( this.__currentCommand );
|
||||
|
||||
if( n )
|
||||
{
|
||||
this.__command = n;
|
||||
this.__curPos = n.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
beep();
|
||||
}
|
||||
}
|
||||
else if( histNav = e.kMap( "Down" ) )
|
||||
{
|
||||
var n = this.__hist.next( this.__currentCommand );
|
||||
|
||||
if( n )
|
||||
{
|
||||
this.__command = n;
|
||||
this.__curPos = n.length;
|
||||
}
|
||||
else if( this.__currentCommand )
|
||||
{
|
||||
this.__command = this.__currentCommand;
|
||||
this.__currentCommand = null;
|
||||
}
|
||||
|
||||
else beep();
|
||||
}
|
||||
else
|
||||
{
|
||||
InputKey = e.key;
|
||||
}
|
||||
|
||||
if( InputKey != null )
|
||||
{
|
||||
this.__command.splice( this.__curPos ++, 0, InputKey );
|
||||
}
|
||||
|
||||
if( !histNav )
|
||||
{
|
||||
this.__hist.reset();
|
||||
if( this.__currentCommand ) this.__currentCommand = this.__command;
|
||||
}
|
||||
|
||||
e.cancel();
|
||||
|
||||
var feeder = this.__cursor.feeder;
|
||||
feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
|
||||
};
|
||||
|
||||
Command.prototype.__process = function( e )
|
||||
{
|
||||
this.__hist.push( this.__command );
|
||||
|
||||
var action = "";
|
||||
switch( this.__mode )
|
||||
{
|
||||
case "/":
|
||||
action = "FIND";
|
||||
break;
|
||||
}
|
||||
|
||||
var cur = this.__cursor;
|
||||
cur.suppressEvent();
|
||||
this.__cursor.openRunAction( action, e, this.__command.slice() );
|
||||
cur.unsuppressEvent();
|
||||
};
|
||||
|
||||
ns[ NS_EXPORT ]( EX_CLASS, "Command", Command );
|
||||
})();
|
@ -3,6 +3,8 @@
|
||||
|
||||
var debug = __import( "System.Debug" );
|
||||
|
||||
var occurence = __import( "System.utils.Perf.CountSubstr" );
|
||||
|
||||
var LineBuffer = function( cols, nextLineBuffer )
|
||||
{
|
||||
this.prev = null;
|
||||
@ -60,6 +62,12 @@
|
||||
|
||||
line += c;
|
||||
}
|
||||
|
||||
if( !br && i == this.cols && content[i] == "\n" )
|
||||
{
|
||||
i ++;
|
||||
br = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -97,7 +105,8 @@
|
||||
|
||||
LineBuffer.prototype.toString = function()
|
||||
{
|
||||
if( this.content.length < this.cols )
|
||||
var c = this.cols - occurence( this.content, "\t" ) * ( this.tabWidth - 1 );
|
||||
if( this.content.length < c )
|
||||
{
|
||||
return this.content + " ";
|
||||
}
|
||||
|
@ -141,17 +141,17 @@
|
||||
// Y cannot be negative
|
||||
if( Y < 0 ) Y = 0;
|
||||
|
||||
// Compensate the last "\n" content placeholder
|
||||
var cont = this.content.slice( 0, -1 );
|
||||
if( 0 < Y )
|
||||
{
|
||||
f = this.content.indexOf( "\n" );
|
||||
f = cont.indexOf( "\n" );
|
||||
for( i = 1; f != -1 && i < Y; i ++ )
|
||||
{
|
||||
var a = this.content.indexOf( "\n", f + 1 );
|
||||
var a = cont.indexOf( "\n", f + 1 );
|
||||
if( a == -1 )
|
||||
{
|
||||
Y = i - 1;
|
||||
// -2 to compensate the last "\n" content placeholder
|
||||
f -= 2;
|
||||
Y = i;
|
||||
break;
|
||||
}
|
||||
f = a;
|
||||
@ -226,9 +226,16 @@
|
||||
__readOnly( Feeder.prototype, "docPos", function() {
|
||||
var pos = "ALL";
|
||||
|
||||
if( 0 < this.panY && this.EOF )
|
||||
if( 0 < this.panY )
|
||||
{
|
||||
pos = "BOTTOM";
|
||||
if( this.EOF )
|
||||
{
|
||||
pos = "BOTTOM";
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = Math.floor( ( this.panY / ( this.linesTotal - ( this.__rows - 1 ) ) ) * 100 ) + "%";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
96
botanjs/src/Components/Vim/State/History.js
Normal file
96
botanjs/src/Components/Vim/State/History.js
Normal file
@ -0,0 +1,96 @@
|
||||
(function(){
|
||||
var ns = __namespace( "Components.Vim.State" );
|
||||
|
||||
/** @type {System.Debug} */
|
||||
var debug = __import( "System.Debug" );
|
||||
|
||||
// private static
|
||||
var Zones = {};
|
||||
|
||||
var PartialBA = function( a, b )
|
||||
{
|
||||
var l = b.length;
|
||||
if( a.length < l ) return false;
|
||||
|
||||
for( var i = 0; i < l; i ++ )
|
||||
{
|
||||
if( a[ i ] != b[ i ] ) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
var ExactAB = function( a, b )
|
||||
{
|
||||
var l = a.length < b.length ? b.length : a.length;
|
||||
for( var i = 0; i < l; i ++ )
|
||||
{
|
||||
if( a[ i ] != b[ i ] ) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
var History = function( z )
|
||||
{
|
||||
if( !Zones[ z ] ) Zones[ z ] = [];
|
||||
|
||||
this.__pi = 0;
|
||||
this.__zone = Zones[ z ];
|
||||
this.reset();
|
||||
};
|
||||
|
||||
History.prototype.push = function( stack )
|
||||
{
|
||||
if( this.__zone.length
|
||||
&& ExactAB( this.__zone[ this.__zone.length - 1 ], stack )
|
||||
) {
|
||||
debug.Info( "This is the previous command, skipping" );
|
||||
return;
|
||||
}
|
||||
this.__zone.push( stack );
|
||||
};
|
||||
|
||||
History.prototype.prev = function( stack )
|
||||
{
|
||||
if( this.__zone.length <= this.__i ) this.reset();
|
||||
|
||||
while( -1 < this.__i )
|
||||
{
|
||||
var st = this.__zone[ this.__i -- ];
|
||||
if( st && PartialBA( st, stack ) )
|
||||
{
|
||||
return st.slice();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
History.prototype.next = function( stack )
|
||||
{
|
||||
if( this.__i < 0 )
|
||||
{
|
||||
this.__i ++;
|
||||
this.next( stack );
|
||||
}
|
||||
|
||||
while( this.__i < this.__zone.length )
|
||||
{
|
||||
var st = this.__zone[ this.__i ++ ];
|
||||
if( st && PartialBA( st, stack ) )
|
||||
{
|
||||
return st.slice();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
History.prototype.reset = function()
|
||||
{
|
||||
this.__i = this.__zone.length - 1;
|
||||
};
|
||||
|
||||
ns[ NS_EXPORT ]( EX_CLASS, "History", History );
|
||||
})();
|
@ -15,25 +15,41 @@
|
||||
*/
|
||||
var ns = __namespace( "Components.Vim.State" );
|
||||
|
||||
var Register = function( str, n )
|
||||
{
|
||||
this.__str = str + "";
|
||||
this.newLine = Boolean( n );
|
||||
};
|
||||
|
||||
Register.prototype.newLine = false;
|
||||
|
||||
Register.prototype.toString = function() { return this.__str; };
|
||||
Register.prototype.indexOf = function( a, b ) { return this.__str.indexOf( a, b ); };
|
||||
|
||||
__readOnly( Register.prototype, "length", function() { return this.__str.length; } );
|
||||
|
||||
|
||||
var Registers = function()
|
||||
{
|
||||
this.__registers = {};
|
||||
};
|
||||
|
||||
Registers.prototype.unnamed = function( str )
|
||||
Registers.prototype.__unnamed = function( reg )
|
||||
{
|
||||
this.__registers[ "\"" ] = str;
|
||||
this.__registers[ "\"" ] = reg;
|
||||
};
|
||||
|
||||
Registers.prototype.yank = function( str )
|
||||
Registers.prototype.yank = function( str, newLine )
|
||||
{
|
||||
this.unnamed( str );
|
||||
this.__registers[ 0 ] = str;
|
||||
var reg = new Register( str, newLine );
|
||||
this.__unnamed( reg );
|
||||
this.__registers[ 0 ] = reg;
|
||||
};
|
||||
|
||||
Registers.prototype.change = function( str )
|
||||
Registers.prototype.change = function( str, newLine )
|
||||
{
|
||||
this.unnamed( str );
|
||||
var reg = new Register( str, newLine );
|
||||
this.__unnamed( reg );
|
||||
var r = this.__registers;
|
||||
for( var i = 9; 1 < i; i -- )
|
||||
{
|
||||
@ -43,8 +59,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
r[ 1 ] = str;
|
||||
r[ 1 ] = reg;
|
||||
};
|
||||
|
||||
Registers.prototype.get = function( r )
|
||||
{
|
||||
// 0 is one of the registers
|
||||
if( !r && r !== 0 ) r = "\"";
|
||||
|
||||
return this.__registers[ r ];
|
||||
};
|
||||
|
||||
ns[ NS_EXPORT ]( EX_CLASS, "Registers", Registers );
|
||||
|
||||
})();
|
||||
|
@ -10,6 +10,7 @@
|
||||
{
|
||||
this.cols = cols;
|
||||
this.statStamp = {};
|
||||
this.override = null;
|
||||
};
|
||||
|
||||
StatusBar.prototype.stamp = function( pos, func )
|
||||
@ -17,8 +18,12 @@
|
||||
this.statStamp[ pos ] = func;
|
||||
};
|
||||
|
||||
StatusBar.prototype.override;
|
||||
|
||||
__readOnly( StatusBar.prototype, "statusText", function()
|
||||
{
|
||||
if( this.override ) return this.override();
|
||||
|
||||
var display = "";
|
||||
var l = this.cols;
|
||||
|
||||
|
@ -249,15 +249,18 @@
|
||||
|
||||
}
|
||||
|
||||
var tMatch = SetParent( tokPairs, highest );
|
||||
var oMatch = tMatch;
|
||||
if( highest )
|
||||
{
|
||||
var tMatch = SetParent( tokPairs, highest );
|
||||
var oMatch = tMatch;
|
||||
|
||||
do {
|
||||
oMatch.__open ++;
|
||||
oMatch.__close --;
|
||||
} while( oMatch = oMatch.parent )
|
||||
do {
|
||||
oMatch.__open ++;
|
||||
oMatch.__close --;
|
||||
} while( oMatch = oMatch.parent )
|
||||
|
||||
if( highest ) return tMatch;
|
||||
return tMatch;
|
||||
}
|
||||
|
||||
return new TokenMatch();
|
||||
};
|
||||
|
@ -2,18 +2,18 @@
|
||||
var ns = __namespace( "Components.Vim" );
|
||||
|
||||
/** @type {Dandelion.IDOMElement} */
|
||||
var IDOMElement = __import( "Dandelion.IDOMElement" );
|
||||
var IDOMElement = __import( "Dandelion.IDOMElement" );
|
||||
/** @type {System.utils.DataKey} */
|
||||
var DataKey = __import( "System.utils.DataKey" );
|
||||
var DataKey = __import( "System.utils.DataKey" );
|
||||
/** @type {System.Cycle} */
|
||||
var Cycle = __import( "System.Cycle" );
|
||||
var Cycle = __import( "System.Cycle" );
|
||||
/** @type {System.Debug} */
|
||||
var debug = __import( "System.Debug" );
|
||||
var debug = __import( "System.Debug" );
|
||||
|
||||
/** @type {Components.Vim.State.Registers} */
|
||||
var Registers = __import( "Components.Vim.State.Registers" );
|
||||
var Registers = __import( "Components.Vim.State.Registers" );
|
||||
/** @type {Components.Vim.Syntax.Analyzer} */
|
||||
var SyntaxAnalyzer = __import( "Components.Vim.Syntax.Analyzer" );
|
||||
var SyntaxAnalyzer = __import( "Components.Vim.Syntax.Analyzer" );
|
||||
|
||||
/** @type {Components.Vim.LineFeeder} */
|
||||
var LineFeeder = ns[ NS_INVOKE ]( "LineFeeder" );
|
||||
@ -96,7 +96,7 @@
|
||||
|
||||
// Content feeder
|
||||
var cfeeder = new LineFeeder( cRange, c );
|
||||
var contentAnalyzer = new SyntaxAnalyzer( cfeeder );
|
||||
var contentAnalyzer = new SyntaxAnalyzer( cfeeder );
|
||||
|
||||
// Feed the contents to content feeder
|
||||
// This "\n" fixes the last line "\n" not displaying
|
||||
@ -104,7 +104,7 @@
|
||||
cfeeder.init( content + "\n" );
|
||||
|
||||
// Status can consumes up to full screen, I think
|
||||
sfeeder = new LineFeeder( r, c );
|
||||
var sfeeder = new LineFeeder( r, c );
|
||||
sfeeder.setRender( false );
|
||||
|
||||
// Set the Vim instance
|
||||
@ -123,9 +123,10 @@
|
||||
{
|
||||
sfeeder.init( statusBar.statusText );
|
||||
|
||||
var sLine = sfeeder.linesOccupied;
|
||||
element.value =
|
||||
cfeeder.render( 0, r - sfeeder.linesOccupied )
|
||||
+ "\n" + sfeeder.render();
|
||||
cfeeder.render( 0, r - sLine )
|
||||
+ "\n" + sfeeder.render( 0, sLine < r ? sLine : r );
|
||||
|
||||
_self.__blink = false;
|
||||
_self.select( cfeeder.cursor.position );
|
||||
@ -135,7 +136,7 @@
|
||||
Update();
|
||||
|
||||
this.contentFeeder = cfeeder;
|
||||
this.contentAnalyzer = contentAnalyzer;
|
||||
this.contentAnalyzer = contentAnalyzer;
|
||||
this.statusFeeder = sfeeder;
|
||||
this.statusBar = statusBar;
|
||||
this.registers = new Registers();
|
||||
|
@ -18,7 +18,12 @@
|
||||
, "UNDO_LIMIT": "Already at oldest change"
|
||||
, "REDO_LIMIT": "Already at newest change"
|
||||
|
||||
, "LINE_FEWER": "%1 fewer lines"
|
||||
, "LINES_FEWER": "%1 fewer line(s)"
|
||||
, "LINES_MORE": "%1 more line(s)"
|
||||
, "LINES_YANKED": "%1 line(s) yanked"
|
||||
|
||||
, "SEARCH_HIT_BOTTOM": "search hit BOTTOM, continuing at TOP"
|
||||
, "SEARCH_HIT_TOP": "search hit TOP, continuing at BOTTOM"
|
||||
};
|
||||
|
||||
var errors = {
|
||||
|
@ -11,7 +11,11 @@ Components.Vim.Controls.InputEvent.key;
|
||||
Components.Vim.Controls.InputEvent.ModKeys;
|
||||
/** @type Boolean */
|
||||
Components.Vim.Controls.InputEvent.Escape;
|
||||
/** @type Boolean */
|
||||
Components.Vim.Controls.InputEvent.canceled;
|
||||
/** @type Number */
|
||||
Components.Vim.Controls.InputEvent.keyCode;
|
||||
/** @type Function */
|
||||
Components.Vim.Controls.InputEvent.kMap;
|
||||
/** @type Function */
|
||||
Components.Vim.Controls.InputEvent.cancel;
|
||||
|
9
botanjs/src/externs/Components.Vim.State.History.js
Normal file
9
botanjs/src/externs/Components.Vim.State.History.js
Normal file
@ -0,0 +1,9 @@
|
||||
/** @constructor */
|
||||
Components.Vim.State.History = function(){};
|
||||
|
||||
/** @type Function */
|
||||
Components.Vim.State.History.push;
|
||||
/** @type Function */
|
||||
Components.Vim.State.History.prev;
|
||||
/** @type Function */
|
||||
Components.Vim.State.History.next;
|
@ -5,5 +5,9 @@ Components.Vim.State.Registers = function(){};
|
||||
Components.Vim.State.Registers.change;
|
||||
/** @type Function */
|
||||
Components.Vim.State.Registers.yank;
|
||||
/** @type Function */
|
||||
Components.Vim.State.Registers.unnamed;
|
||||
|
||||
/** @constructor */
|
||||
Components.Vim.State.Register = function(){};
|
||||
|
||||
/** @type Boolean */
|
||||
Components.Vim.State.Register.newLine;
|
||||
|
@ -3,5 +3,7 @@ Components.Vim.StatusBar = function(){};
|
||||
|
||||
/** @type Function */
|
||||
Components.Vim.StatusBar.stamp;
|
||||
/** @type Function */
|
||||
Components.Vim.StatusBar.override;
|
||||
/** @type String */
|
||||
Components.Vim.StatusBar.statusText;
|
||||
|
Loading…
Reference in New Issue
Block a user