diff --git a/botanjs/src/Components/Vim/Actions/FIND.js b/botanjs/src/Components/Vim/Actions/FIND.js index 461ddc4..f93aeaa 100644 --- a/botanjs/src/Components/Vim/Actions/FIND.js +++ b/botanjs/src/Components/Vim/Actions/FIND.js @@ -128,6 +128,8 @@ LoopGuard = r.index; } + this.__msg = PATTERN.join( "" ) + if( e.kMap( "N" ) ) { Hit = PrevStack[ PrevStack.length - 2 ]; @@ -144,10 +146,6 @@ Hit = FirstHit; this.__msg = Mesg( "SEARCH_HIT_BOTTOM" ); } - else - { - this.__msg = PATTERN.join( "" ) - } if( Hit == undefined ) { diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js index ed9ebd8..c35c167 100644 --- a/botanjs/src/Components/Vim/Actions/INSERT.js +++ b/botanjs/src/Components/Vim/Actions/INSERT.js @@ -10,6 +10,11 @@ var Mesg = __import( "Components.Vim.Message" ); + // Phantom indent + var IN_START = 0; + var IN_END = 1; + var IN_DEL = 2; + var Translate = function( c ) { switch( c ) @@ -47,6 +52,10 @@ INSERT.prototype.dispose = function() { + if( this.__cancelIndent() ) + { + this.__cursor.feeder.pan(); + } this.__msg = ""; this.__rec( "", true ); this.__cursor.moveX( -1 ); @@ -105,8 +114,9 @@ var feeder = cur.feeder; // Backspace - if( e.kMap( "BS" ) ) + if( e.kMap( "BS" ) || e.kMap( "S-BS" ) ) { + this.__realizeIndent(); var oY = feeder.panY + cur.Y; if( cur.X == 0 && feeder.panY == 0 && cur.Y == 0 ) return; @@ -128,8 +138,9 @@ if( 0 < this.__insertLen ) this.__insertLen --; this.__punch --; } - else if( e.kMap( "Del" ) ) + else if( e.kMap( "Del" ) || e.kMap( "S-Del" ) ) { + this.__realizeIndent(); var f = cur.aPos; this.__contentUndo += feeder.content.substr( f, 1 ); @@ -175,9 +186,11 @@ feeder.pan(); cur.moveY( 1 ); cur.lineStart(); + this.__autoIndent( e ); } else { + this.__realizeIndent(); feeder.pan(); cur.moveX( 1, false, true ); } @@ -187,6 +200,94 @@ this.__rec( inputChar ); }; + INSERT.prototype.__realizeIndent = function() + { + var ind = this.__phantomIndent; + if( !this.__phantomIndent ) return; + + debug.Info( "Realize Indentation: " + ind ); + + l = ind[ IN_END ]; + for( var i = ind[ IN_START ]; i < l; i ++ ) + { + this.__rec( this.__cursor.feeder.content[ i ] ); + } + this.__contentUndo = ind[ IN_DEL ] + this.__contentUndo; + this.__phantomIndent = null; + }; + + INSERT.prototype.__autoIndent = function( e ) + { + var carried = this.__cancelIndent(); + + var cur = this.__cursor; + var feeder = cur.feeder; + + var f = cur.aPos; + + // Get the last indent + var i = feeder.content.lastIndexOf( "\n", f - 2 ); + var line = feeder.content.substring( i + 1, f - 1 ) || carried; + + // Find Last indent + while( line == "" && 0 < i ) + { + var j = i; + i = feeder.content.lastIndexOf( "\n", j - 2 ); + line = feeder.content.substring( i + 1, j - 1 ); + } + + var inDel = ""; + // Indent removed + for( var ir = f; "\t ".indexOf( feeder.content[ ir ] ) != -1; ir ++ ) + { + inDel += feeder.content[ ir ]; + } + + // Copy the indentation + for( i = 0; "\t ".indexOf( line[i] ) != -1; i ++ ); + + if( line ) + { + feeder.content = + feeder.content.substring( 0, f ) + + line.substr( 0, i ) + + feeder.content.substring( ir ); + + feeder.softReset(); + feeder.pan(); + cur.moveX( i, false, true ); + + var a = []; + a[ IN_START ] = f; + a[ IN_END ] = f + i; + a[ IN_DEL ] = inDel; + + this.__phantomIndent = a; + debug.Info( "Phantom indent: " + a ); + } + }; + + INSERT.prototype.__cancelIndent = function() + { + var ind = this.__phantomIndent; + if( !ind ) return ""; + + debug.Info( "Erase phantom indent: " + ind ); + + var cur = this.__cursor; + var feeder = cur.feeder; + + var canceled = feeder.content.substring( ind[ IN_START ], ind[ IN_END ] ); + feeder.content = + feeder.content.substring( 0, ind[ IN_START ] ) + + feeder.content.substring( ind[ IN_END ] ); + + this.__phantomIndent = null; + + return canceled; + } + INSERT.prototype.getMessage = function() { return this.__msg; diff --git a/botanjs/src/Components/Vim/Actions/PUT.js b/botanjs/src/Components/Vim/Actions/PUT.js index 3152444..52a6891 100644 --- a/botanjs/src/Components/Vim/Actions/PUT.js +++ b/botanjs/src/Components/Vim/Actions/PUT.js @@ -29,20 +29,29 @@ { e.preventDefault(); - // TODO: Get the input for determinating registers - var inputStack = false; - - var cput = this.__cursor.Vim.registers.get( inputStack ); + var cput = this.__cursor.Vim.registers.get(); 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; + if( 1 < e.count ) + { + var oput = cput; + for( var i = 1; i < e.count; i ++ ) + { + oput += cput; + } + + cput = oput; + oput = null; + } + + var nLines = occurence( cput, "\n" ); + var clen = cput.length; + // Compensation var c = e.kMap( "P" ) ? 0 : -1; diff --git a/botanjs/src/Components/Vim/Actions/REDO.js b/botanjs/src/Components/Vim/Actions/REDO.js index 43ca88b..94485d2 100644 --- a/botanjs/src/Components/Vim/Actions/REDO.js +++ b/botanjs/src/Components/Vim/Actions/REDO.js @@ -24,8 +24,10 @@ var stack = this.__cursor.rec.redo(); if( stack ) { + this.__cursor.suppressEvent(); stack.play(); - this.__message = "<>; before #" + stack.id + " " + stack.time; + this.__cursor.unsuppressEvent(); + this.__message = Mesg( "NCHANGES", "", stack.id, stack.time ); } else { diff --git a/botanjs/src/Components/Vim/Actions/REGISTERS.js b/botanjs/src/Components/Vim/Actions/REGISTERS.js index 48f19d0..c94488d 100644 --- a/botanjs/src/Components/Vim/Actions/REGISTERS.js +++ b/botanjs/src/Components/Vim/Actions/REGISTERS.js @@ -37,7 +37,7 @@ var msg = ":register"; msg += "\n" + Mesg( "REGISTERS" ); - var regs = "\"0123456789-.:%/="; + var regs = "\"0123456789abcdefghijklmnopqrstuvwxyz-.:%/="; for( var i = 0, j = regs[ i ]; j != undefined; i ++, j = regs[ i ] ) { var r = reg.get( j ); diff --git a/botanjs/src/Components/Vim/Actions/REPLACE.js b/botanjs/src/Components/Vim/Actions/REPLACE.js index 1acf895..0161595 100644 --- a/botanjs/src/Components/Vim/Actions/REPLACE.js +++ b/botanjs/src/Components/Vim/Actions/REPLACE.js @@ -92,7 +92,7 @@ this.__cursor.unsuppressEvent(); }; - REPLACE.prototype.handler = function( e, p, range ) + REPLACE.prototype.handler = function( e, p ) { e.preventDefault(); @@ -143,13 +143,16 @@ var content = feeder.content.slice( 0, -1 ) .replace( search, this.__replCallback ) + "\n"; - if( !this.__replacedGroups.length ) + var numSubs = this.__replacedGroups.length; + if( !numSubs ) { this.__msg = VimError( "E486", spattern.join( "" ) ); } feeder.content = content; + this.__msg = Mesg( "REPLACE", numSubs, "" ); + // Record this step for UNDO / REDO this.__rec(); diff --git a/botanjs/src/Components/Vim/Actions/SHIFT_LINES.js b/botanjs/src/Components/Vim/Actions/SHIFT_LINES.js new file mode 100644 index 0000000..1202b6c --- /dev/null +++ b/botanjs/src/Components/Vim/Actions/SHIFT_LINES.js @@ -0,0 +1,349 @@ +(function(){ + var ns = __namespace( "Components.Vim.Actions" ); + + /** @type {System.Debug} */ + var debug = __import( "System.Debug" ); + var beep = __import( "Components.Vim.Beep" ); + + /** @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 VimError = __import( "Components.Vim.Error" ); + var Mesg = __import( "Components.Vim.Message" ); + + var occurence = __import( "System.utils.Perf.CountSubstr" ); + + var REPL_BEFORE = 0; + var REPL_OFFSET = 1; + var REPL_LENGTH = 2; + + /** @type {Components.Vim.IAction} + * Cursor @param {Components.Vim.Cursor} + * e @param {Components.Vim.ActionEvent} + **/ + var SHIFT_LINES = function( Cursor, e ) + { + /** @type {Components.Vim.Cursor} */ + this.__cursor = Cursor; + this.__startX = Cursor.aPos; + this.__msg = ""; + + this.__slineNum = Cursor.getLine().lineNum; + + this.__lines = e.count; + debug.Info( "Open shift: " + this.__lines + " line(s) below the cursor" ); + + this.__direction = e.kMap( ">" ) ? 1 : -1; + debug.Info( "Direction is: " + ( this.__direction == 1 ? ">" : "<" ) ); + + Cursor.suppressEvent(); + }; + + SHIFT_LINES.prototype.allowMovement = true; + + SHIFT_LINES.prototype.dispose = function() + { + this.__cursor.unsuppressEvent(); + }; + + SHIFT_LINES.prototype.handler = function( e, sp ) + { + e.preventDefault(); + + if( e.ModKeys || e.kMap( "i" ) ) return; + + var cur = this.__cursor; + var feeder = cur.feeder; + + var Triggered = false; + var dir = this.__direction; + + var start = this.__slineNum; + var nline = this.__lines; + var indentMult = 1; + + if( 1 < e.count ) + { + nline += e.count; + } + + // default: >>, <<, >l, " ) || e.kMap( "l" ) ) ); + else if( dir < 0 && ( e.kMap( "<" ) || e.kMap( "h" ) ) ); + else + { + beep(); + return true; + } + } + } + // VISUAL Mode + else + { + start = 0; + for( var i = 0; i < sp; i ++ ) + { + if( feeder.content[ i ] == "\n" ) start ++; + } + + end = this.__slineNum; + + indentMult = e.count; + } + + if( end < start ) + { + start = start + end; + end = start - end; + start = start - end; + } + + // last "\n" padding + var c = feeder.content.slice( 0, -1 ); + + var indents = c.match( /^[\t ]+/gm ); + var indentChar = "\t"; + var tabwidth = feeder.firstBuffer.tabWidth; + + if( indents ) + { + var l = indents.length - 1; + + if( 1 < l ) + { + debug.Info( "Guessing the tabstop:" ); + var tabOccr = 0; + var spOccr = 0; + + // Guess indent + var tabStat = []; + + for( var i = 0; i < l; i ++ ) + { + var ind = indents[ i ]; + var indNext = indents[ i + 1 ]; + tabOccr += occurence( ind, "\t" ); + spOccr += occurence( ind, " " ); + var d = indNext.length - ind.length; + if( d == 0 ) continue; + + d = d < 0 ? -d : d; + + if( !tabStat[ d ] ) tabStat[ d ] = 0; + + tabStat[ d ] ++; + } + + var upperDiff = 0; + var indentCLen = 0; + for( var i in tabStat ) + { + i = Number( i ); + var p = tabStat[ i ]; + if( upperDiff < p ) + { + upperDiff = p; + indentCLen = i; + } + } + + spOccr /= indentCLen; + + if( tabOccr < spOccr ) + { + indentChar = ""; + for( var i = 0; i < indentCLen; i ++ ) indentChar += " "; + } + + tabwidth = indentCLen; + + debug.Info( "\tTab count: " + tabOccr ); + debug.Info( "\tSpace count: " + spOccr ); + debug.Info( "\ti.e. indent using " + JSON.stringify( indentChar ) ); + } + else + { + debug.Info( "Not enough tabs to determine the tabstop, using default" ); + } + } + + debug.Info( "Start: " + start, "End: " + end ); + var rBlock = ""; + var nLen = 0; + + var started = false; + + var recStart = 0; + + feeder.content = ""; + nline = 0; + + var indented = ""; + for( var i = 0; i < indentMult; i ++ ) indented += indentChar; + + for( var i = 0, j = 0; 0 <= i; i = c.indexOf( "\n", i ), j ++ ) + { + i ++; + + if( j < start ) continue; + else if( !started ) + { + started = true; + feeder.content = c.substring( 0, i - 1 ); + recStart = feeder.content.length; + } + + if( end < j ) break; + + var line = c.substring( 1 < i ? i : i - 1, c.indexOf( "\n", i ) ); + + if( 1 < i ) + { + feeder.content += "\n"; + rBlock += "\n"; + nLen ++; + } + + rBlock += line; + + if( line !== "" ) + { + var indentedLine; + if( 0 < dir ) + { + indentedLine = indented + line; + } + else + { + for( var si = 0, sj = 0; si < indentMult; si ++ ) + { + var startC = line[ sj ]; + if( startC == " " ) + { + for( var swidth = tabwidth + ( sj ++ ); sj < swidth; sj ++ ) + { + if( !~"\t ".indexOf( line[ sj ] ) ) break; + } + } + else if( startC != "\t" ) break; + } + + indentedLine = line.substring( sj ); + } + + feeder.content += indentedLine; + + nLen += indentedLine.length; + nline ++; + } + } + + var nPos = feeder.content.length; + feeder.content += "\n" + c.substring( i ) + "\n"; + feeder.pan(); + + cur.moveTo( nPos ); + + var stator = new Stator( cur, recStart ); + var stack = new Stack(); + + recStart ++; + for( ; ~"\t ".indexOf( feeder.content[ recStart ] ); recStart ++ ); + + var f = stator.save( nLen, rBlock ); + stack.store( function() { + f(); + // Offset correction after REDO / UNDO + cur.moveTo( recStart ); + cur.lineStart(); + } ); + + cur.moveTo( recStart ); + + cur.rec.record( stack ); + + if( nline ) + { + this.__msg = Mesg( "LINES_SHIFTED", nline, dir < 0 ? "<" : ">", indentMult ); + } + else + { + this.__msg = Mesg( "NO_SHIFT", dir < 0 ? "<" : ">" ); + } + + return Triggered; + }; + + SHIFT_LINES.prototype.getMessage = function() + { + return this.__msg; + }; + + ns[ NS_EXPORT ]( EX_CLASS, "SHIFT_LINES", SHIFT_LINES ); +})(); diff --git a/botanjs/src/Components/Vim/Actions/UNDO.js b/botanjs/src/Components/Vim/Actions/UNDO.js index 6f81f52..86db7cf 100644 --- a/botanjs/src/Components/Vim/Actions/UNDO.js +++ b/botanjs/src/Components/Vim/Actions/UNDO.js @@ -24,8 +24,10 @@ var stack = this.__cursor.rec.undo(); if( stack ) { + this.__cursor.suppressEvent(); stack.play(); - this.__message = "<>; before #" + stack.id + " " + stack.time; + this.__cursor.unsuppressEvent(); + this.__message = Mesg( "NCHANGES", "", stack.id, stack.time ); } else { diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js index 1d4071d..5e0cbea 100644 --- a/botanjs/src/Components/Vim/Actions/VISUAL.js +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -10,6 +10,8 @@ var YANK = ns[ NS_INVOKE ]( "YANK" ); /** @type {Components.Vim.IAction} */ var DELETE = ns[ NS_INVOKE ]( "DELETE" ); + /** @type {Components.Vim.IAction} */ + var SHIFT_LINES = ns[ NS_INVOKE ]( "SHIFT_LINES" ); var MODE_NULL = -1; var MODE_VISUAL = 0; @@ -129,6 +131,10 @@ this.__msg = Mesg( "VISLINE" ); } } + else if( e.kMap( "<" ) || e.kMap( ">" ) ) + { + Action = new SHIFT_LINES( cur, e ); + } else if( e.kMap( "v" ) ) { if( this.__mode == MODE_VISUAL ) return true; diff --git a/botanjs/src/Components/Vim/Actions/YANK.js b/botanjs/src/Components/Vim/Actions/YANK.js index 44e3c27..9c9a37a 100644 --- a/botanjs/src/Components/Vim/Actions/YANK.js +++ b/botanjs/src/Components/Vim/Actions/YANK.js @@ -50,22 +50,18 @@ 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; @@ -75,7 +71,6 @@ cur.lineStart(); this.__startX = cur.aPos; } - // Remove the current and the preceding line else if( e.kMap( "k" ) ) { newLine = true; @@ -94,7 +89,6 @@ cur.moveTo( this.__startX ); } } - // Remove the current line else { if( e.kMap( "y" ) ) @@ -111,8 +105,6 @@ } 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 diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index aace42f..1173343 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -47,6 +47,8 @@ var COMMA = 188; var FULLSTOP = 190; var SLASH = 191; var BACK_SLASH = 220; + var QUOTE = 222; + var ANY_KEY = -1; var __maps = {}; @@ -177,9 +179,8 @@ { if( compReg.i == keys.length ) { - compReg.handler( e ); this.__compositeReg = null; - this.__cMovement = false; + compReg.handler( e ); } return true; @@ -188,7 +189,6 @@ if( this.__compositeReg ) beep(); this.__compositeReg = null; - this.__cMovement = false; return false; }; @@ -205,7 +205,7 @@ case A: // Append ccur.moveX( 1, true, true ); case I: // Insert - ccur.openAction( "INSERT" ); + ccur.openAction( "INSERT", e ); break; case S: // Delete Char and start insert @@ -213,19 +213,19 @@ { ccur.openRunAction( "DELETE", e, ccur.aPos ); } - ccur.openAction( "INSERT" ); + ccur.openAction( "INSERT", e ); break; case SHIFT + O: // new line before insert ccur.lineStart(); - ccur.openAction( "INSERT" ); - ccur.action.handler( new InputEvent( e.sender, "Enter" ) ); + ccur.openAction( "INSERT", e ); + ccur.action.handler( new ActionEvent( 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" ) ); + ccur.openAction( "INSERT", e ); + ccur.action.handler( new ActionEvent( e.sender, "Enter" ) ); break; case U: // Undo @@ -236,10 +236,10 @@ break; case D: // Del with motion - ccur.openAction( "DELETE" ); + ccur.openAction( "DELETE", e ); break; case Y: // Yank with motion - ccur.openAction( "YANK" ); + ccur.openAction( "YANK", e ); break; case P: // Put @@ -272,7 +272,7 @@ case V: // Visual case SHIFT + V: // Visual line - ccur.openAction( "VISUAL" ); + ccur.openAction( "VISUAL", e ); ccur.action.handler( e ); break; @@ -281,6 +281,11 @@ this.__divedCCmd.handler( e ); break; + case SHIFT + COMMA: // < + case SHIFT + FULLSTOP: // > + ccur.openAction( "SHIFT_LINES", e ); + break; + case F1: // F1, help break; default: @@ -295,8 +300,9 @@ var ccur = this.__ccur; var x = ccur.X; + var y = ccur.Y; ccur.moveX( a, b, c || ccur.pSpace ); - if( ccur.X == x ) + if( ccur.X == x && ccur.Y == y ) { beep(); return false; @@ -320,6 +326,70 @@ return false; }; + Controls.prototype.__modCommand = function( e ) + { + if( this.__mod ) + { + e.preventDefault(); + this.__composite( e ); + return; + } + + var _self = this; + var mod = true; + + var cur = this.__cursor; + switch( e.keyCode ) + { + case SHIFT + QUOTE: + this.__composite( e, function( e2 ) { + e2.target.registers.select( e2.key ); + e2.cancel(); + + _self.__mod = false; + }, ANY_KEY ); + break; + case _0: case _1: case _2: case _3: case _4: + case _5: case _6: case _7: case _8: case _9: + + var Count = e.key; + var recurNum = function( e ) + { + var intercept = e.ModKeys; + switch( e.keyCode ) + { + case _0: case _1: case _2: + case _3: case _4: case _5: + case _6: case _7: case _8: case _9: + Count += e.key; + intercept = true; + } + + if( intercept ) + { + _self.__composite( e, recurNum, ANY_KEY ); + e.cancel(); + return; + } + + e.__count = Number( Count ); + debug.Info( "Count is: " + Count ); + _self.__mod = false; + }; + + this.__composite( e, recurNum, ANY_KEY ); + break; + default: + mod = false; + } + + this.__mod = mod; + if( mod ) + { + e.cancel(); + } + }; + Controls.prototype.__cursorCommand = function( e ) { var kCode = e.keyCode; @@ -329,6 +399,7 @@ if( !e.ModKeys ) { this.__composite( e ); + this.__cMovement = false; return true; } } @@ -489,15 +560,32 @@ this.__composite( e, curlyBracket, SHIFT + S_BRACKET_R ); break; - case G: // Go to top + case G: + this.__cMovement = true; - this.__composite( e, function(){ + + // Go to top + this.__composite( e, function() { ccur.moveY( -Number.MAX_VALUE ); ccur.moveX( -Number.MAX_VALUE, true ); }, G ); - this.__composite( e, function(){ + + // Print Hex + this.__composite( e, function() { ccur.openRunAction( "PRINT_HEX", e ); }, _8 ); + + // to lowercase + this.__composite( e, function( e2 ) { + if( ccur.action ) { beep(); return; } + // TODO + }, U ); + + // to uppercase + this.__composite( e, function( e2 ) { + if( ccur.action ) { beep(); return; } + // TODO + }, SHIFT + U ); break; case SHIFT + N: // Next Search @@ -520,7 +608,7 @@ /** * sender @param {Components.Vim.VimArea} - * e @param {Components.Vim.Controls.InputEvent} + * e @param {Components.Vim.Controls.ActionEvent} * */ Controls.prototype.handler = function( sender, e ) { @@ -567,6 +655,9 @@ if( e.canceled ) return; } + this.__modCommand( e ); + if( e.canceled ) return; + var cfeeder = this.__cfeeder; var ccur = this.__ccur; @@ -607,7 +698,7 @@ if( this.__actionCommand( e ) ) return; }; - var InputEvent = function( sender, e ) + var ActionEvent = function( sender, e ) { this.__target = sender; this.__canceled = false; @@ -641,19 +732,18 @@ this.__key = e.key; } + this.__count = 1; this.__range = null; }; - InputEvent.prototype.cancel = function() { this.__canceled = true; }; + __readOnly( ActionEvent.prototype, "target", function() { return this.__target; } ); + __readOnly( ActionEvent.prototype, "key", function() { return this.__key; } ); + __readOnly( ActionEvent.prototype, "keyCode", function() { return this.__kCode; } ); + __readOnly( ActionEvent.prototype, "ModKeys", function() { return this.__modKeys; } ); + __readOnly( ActionEvent.prototype, "Escape", function() { return this.__escape; } ); + __readOnly( ActionEvent.prototype, "canceled", function() { return this.__canceled; } ); - __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() { + __readOnly( ActionEvent.prototype, "range", function() { /** @type {Components.Vim.Syntax.TokenMatch} */ var r = this.__range; @@ -666,16 +756,26 @@ return r; } ); - InputEvent.prototype.kMap = function( map ) + __readOnly( ActionEvent.prototype, "count", function() { + return this.__count; + } ); + + ActionEvent.prototype.kMap = function( map ) { return this.__kCode == Map( map ); }; - InputEvent.prototype.preventDefault = function() + ActionEvent.prototype.cancel = function() + { + this.preventDefault(); + this.__canceled = true; + }; + + ActionEvent.prototype.preventDefault = function() { if( this.__e ) this.__e.preventDefault(); }; ns[ NS_EXPORT ]( EX_CLASS, "Controls", Controls ); - ns[ NS_EXPORT ]( EX_CLASS, "InputEvent", InputEvent ); + ns[ NS_EXPORT ]( EX_CLASS, "ActionEvent", ActionEvent ); })(); diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index e9b6d13..f8d2dd9 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -376,13 +376,13 @@ // Open an action handler // i.e. YANK, VISUAL, INSERT, UNDO, etc. - Cursor.prototype.openAction = function( name ) + Cursor.prototype.openAction = function( name, e ) { if( this.action ) this.action.dispose(); debug.Info( "openAction: " + name ); - this.action = new (Actions[ name ])( this ); + this.action = new (Actions[ name ])( this, e ); this.__pulseMsg = null; this.__visualUpdate(); diff --git a/botanjs/src/Components/Vim/State/Registers.js b/botanjs/src/Components/Vim/State/Registers.js index ec6335b..ef9cafa 100644 --- a/botanjs/src/Components/Vim/State/Registers.js +++ b/botanjs/src/Components/Vim/State/Registers.js @@ -15,6 +15,9 @@ */ var ns = __namespace( "Components.Vim.State" ); + /** @type {System.Debug} */ + var debug = __import( "System.Debug" ); + var Register = function( str, n ) { this.__str = str + ""; @@ -43,7 +46,8 @@ { var reg = new Register( str, newLine ); this.__unnamed( reg ); - this.__registers[ 0 ] = reg; + this.__registers[ this.__selRegister || 0 ] = reg; + this.__selRegister = false; }; Registers.prototype.change = function( str, newLine ) @@ -60,16 +64,24 @@ } r[ 1 ] = reg; + this.__selRegister = false; }; Registers.prototype.get = function( r ) { // 0 is one of the registers - if( !r && r !== 0 ) r = "\""; + if( !r && r !== 0 ) r = this.__selRegister || "\""; + this.__selRegister = false; return this.__registers[ r ]; }; + Registers.prototype.select = function( r ) + { + debug.Info( "Selecting Register: " + r ); + this.__selRegister = r; + }; + ns[ NS_EXPORT ]( EX_CLASS, "Registers", Registers ); })(); diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index 563cd33..433be5a 100644 --- a/botanjs/src/Components/Vim/VimArea.js +++ b/botanjs/src/Components/Vim/VimArea.js @@ -23,7 +23,7 @@ var StatusBar = ns[ NS_INVOKE ]( "StatusBar" ); var VimControls = ns[ NS_INVOKE ]( "Controls" ); - var InputEvent = ns[ NS_INVOKE ]( "InputEvent" ); + var ActionEvent = ns[ NS_INVOKE ]( "ActionEvent" ); var mesg = ns[ NS_INVOKE ]( "Message" ); var Insts = []; @@ -38,7 +38,7 @@ if ( e.keyCode ) code = e.keyCode; else if ( e.which ) code = e.which; - handler( sender, new InputEvent( sender, e ) ); + handler( sender, new ActionEvent( sender, e ) ); }; }; diff --git a/botanjs/src/Components/Vim/_this.js b/botanjs/src/Components/Vim/_this.js index ab26706..bfa1b4b 100644 --- a/botanjs/src/Components/Vim/_this.js +++ b/botanjs/src/Components/Vim/_this.js @@ -19,10 +19,14 @@ VIMRE_VERSION = "1.0.0b"; , "UNDO_LIMIT": "Already at oldest change" , "REDO_LIMIT": "Already at newest change" + , "NCHANGES": "%1 change(s); before #%2 %3" , "LINES_FEWER": "%1 fewer line(s)" , "LINES_MORE": "%1 more line(s)" , "LINES_YANKED": "%1 line(s) yanked" + , "LINES_SHIFTED": "%1 line(s) %2ed %3 time(s)" + + , "NO_SHIFT": "No line to %1" , "SEARCH_HIT_BOTTOM": "search hit BOTTOM, continuing at TOP" , "SEARCH_HIT_TOP": "search hit TOP, continuing at BOTTOM" diff --git a/botanjs/src/externs/Components.Vim.Controls.ActionEvent.js b/botanjs/src/externs/Components.Vim.Controls.ActionEvent.js new file mode 100644 index 0000000..ca9e1e5 --- /dev/null +++ b/botanjs/src/externs/Components.Vim.Controls.ActionEvent.js @@ -0,0 +1,23 @@ +/** @constructor */ +Components.Vim.Controls.ActionEvent = function(){}; + +/** @type {Components.Vim.VimArea} */ +Components.Vim.Controls.ActionEvent.target; +/** @type {Components.Vim.Syntax.TokenMatch} */ +Components.Vim.Controls.ActionEvent.range; +/** @type {Components.Vim.Syntax.Number} */ +Components.Vim.Controls.ActionEvent.count; +/** @type String */ +Components.Vim.Controls.ActionEvent.key; +/** @type Boolean */ +Components.Vim.Controls.ActionEvent.ModKeys; +/** @type Boolean */ +Components.Vim.Controls.ActionEvent.Escape; +/** @type Boolean */ +Components.Vim.Controls.ActionEvent.canceled; +/** @type Number */ +Components.Vim.Controls.ActionEvent.keyCode; +/** @type Function */ +Components.Vim.Controls.ActionEvent.kMap; +/** @type Function */ +Components.Vim.Controls.ActionEvent.cancel; diff --git a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js deleted file mode 100644 index 6678a27..0000000 --- a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js +++ /dev/null @@ -1,21 +0,0 @@ -/** @constructor */ -Components.Vim.Controls.InputEvent = function(){}; - -/** @type {Components.Vim.VimArea} */ -Components.Vim.Controls.InputEvent.target; -/** @type {Components.Vim.Syntax.TokenMatch} */ -Components.Vim.Controls.InputEvent.range; -/** @type String */ -Components.Vim.Controls.InputEvent.key; -/** @type Boolean */ -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;