diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js index b509fb55..1683c320 100644 --- a/botanjs/src/Components/Vim/Actions/DELETE.js +++ b/botanjs/src/Components/Vim/Actions/DELETE.js @@ -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 ""; diff --git a/botanjs/src/Components/Vim/Actions/FIND.js b/botanjs/src/Components/Vim/Actions/FIND.js new file mode 100644 index 00000000..c94f4f0c --- /dev/null +++ b/botanjs/src/Components/Vim/Actions/FIND.js @@ -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 ); +})(); diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js index 7eb38ebd..f119a30f 100644 --- a/botanjs/src/Components/Vim/Actions/INSERT.js +++ b/botanjs/src/Components/Vim/Actions/INSERT.js @@ -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 ); diff --git a/botanjs/src/Components/Vim/Actions/PRINT_HEX.js b/botanjs/src/Components/Vim/Actions/PRINT_HEX.js index c3efbbb0..185bb286 100644 --- a/botanjs/src/Components/Vim/Actions/PRINT_HEX.js +++ b/botanjs/src/Components/Vim/Actions/PRINT_HEX.js @@ -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" ); diff --git a/botanjs/src/Components/Vim/Actions/PUT.js b/botanjs/src/Components/Vim/Actions/PUT.js new file mode 100644 index 00000000..e58c9f98 --- /dev/null +++ b/botanjs/src/Components/Vim/Actions/PUT.js @@ -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 ); +})(); diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js index 7cac7892..6d32caa0 100644 --- a/botanjs/src/Components/Vim/Actions/VISUAL.js +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -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(); diff --git a/botanjs/src/Components/Vim/Actions/YANK.js b/botanjs/src/Components/Vim/Actions/YANK.js index ba987d7b..12f7a5c4 100644 --- a/botanjs/src/Components/Vim/Actions/YANK.js +++ b/botanjs/src/Components/Vim/Actions/YANK.js @@ -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 " YANK COMMAND"; + return this.__msg; }; ns[ NS_EXPORT ]( EX_CLASS, "YANK", YANK ); diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 950ddbc9..19c76bdc 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -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() { diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index ce66bd86..bd1967d9 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -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 ) diff --git a/botanjs/src/Components/Vim/Ex/Command.js b/botanjs/src/Components/Vim/Ex/Command.js new file mode 100644 index 00000000..d3bfe054 --- /dev/null +++ b/botanjs/src/Components/Vim/Ex/Command.js @@ -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 ); +})(); diff --git a/botanjs/src/Components/Vim/LineBuffer.js b/botanjs/src/Components/Vim/LineBuffer.js index 238cfb33..e097f839 100644 --- a/botanjs/src/Components/Vim/LineBuffer.js +++ b/botanjs/src/Components/Vim/LineBuffer.js @@ -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 + " "; } diff --git a/botanjs/src/Components/Vim/LineFeeder.js b/botanjs/src/Components/Vim/LineFeeder.js index b27779f1..2ead9a04 100644 --- a/botanjs/src/Components/Vim/LineFeeder.js +++ b/botanjs/src/Components/Vim/LineFeeder.js @@ -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 { diff --git a/botanjs/src/Components/Vim/State/History.js b/botanjs/src/Components/Vim/State/History.js new file mode 100644 index 00000000..18c67be1 --- /dev/null +++ b/botanjs/src/Components/Vim/State/History.js @@ -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 ); +})(); diff --git a/botanjs/src/Components/Vim/State/Registers.js b/botanjs/src/Components/Vim/State/Registers.js index 220fe709..ec6335b1 100644 --- a/botanjs/src/Components/Vim/State/Registers.js +++ b/botanjs/src/Components/Vim/State/Registers.js @@ -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 ); + })(); diff --git a/botanjs/src/Components/Vim/StatusBar.js b/botanjs/src/Components/Vim/StatusBar.js index 1a57d5c0..81af3579 100644 --- a/botanjs/src/Components/Vim/StatusBar.js +++ b/botanjs/src/Components/Vim/StatusBar.js @@ -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; diff --git a/botanjs/src/Components/Vim/Syntax/Analyzer.js b/botanjs/src/Components/Vim/Syntax/Analyzer.js index 54014ce9..c10e441e 100644 --- a/botanjs/src/Components/Vim/Syntax/Analyzer.js +++ b/botanjs/src/Components/Vim/Syntax/Analyzer.js @@ -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(); }; diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index 116bd46a..ee76302b 100644 --- a/botanjs/src/Components/Vim/VimArea.js +++ b/botanjs/src/Components/Vim/VimArea.js @@ -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(); diff --git a/botanjs/src/Components/Vim/_this.js b/botanjs/src/Components/Vim/_this.js index f9e572ed..345a8829 100644 --- a/botanjs/src/Components/Vim/_this.js +++ b/botanjs/src/Components/Vim/_this.js @@ -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 = { diff --git a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js index afe109df..6678a271 100644 --- a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js +++ b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js @@ -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; diff --git a/botanjs/src/externs/Components.Vim.State.History.js b/botanjs/src/externs/Components.Vim.State.History.js new file mode 100644 index 00000000..f28f3483 --- /dev/null +++ b/botanjs/src/externs/Components.Vim.State.History.js @@ -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; diff --git a/botanjs/src/externs/Components.Vim.State.Registers.js b/botanjs/src/externs/Components.Vim.State.Registers.js index 75b9c13c..61bdfc7f 100644 --- a/botanjs/src/externs/Components.Vim.State.Registers.js +++ b/botanjs/src/externs/Components.Vim.State.Registers.js @@ -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; diff --git a/botanjs/src/externs/Components.Vim.StatusBar.js b/botanjs/src/externs/Components.Vim.StatusBar.js index 356c9803..8e43a797 100644 --- a/botanjs/src/externs/Components.Vim.StatusBar.js +++ b/botanjs/src/externs/Components.Vim.StatusBar.js @@ -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;