Merge remote-tracking branch 'vim/master' into Astro

This commit is contained in:
斟酌 鵬兄 2016-04-02 20:09:19 +08:00
commit dd12efa607
22 changed files with 1171 additions and 130 deletions

View File

@ -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 "";

View 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 );
})();

View File

@ -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 );

View File

@ -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" );

View 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 );
})();

View File

@ -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();

View File

@ -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 );

View File

@ -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() {

View File

@ -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 )

View 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 );
})();

View File

@ -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 + " ";
}

View File

@ -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
{

View 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 );
})();

View File

@ -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 );
})();

View File

@ -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;

View File

@ -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();
};

View File

@ -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();

View File

@ -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 = {

View File

@ -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;

View 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;

View File

@ -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;

View File

@ -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;