From 8c5f50ec2cd75ba452a4171361fb6516ce49e423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Thu, 31 Mar 2016 04:34:08 +0800 Subject: [PATCH 01/11] Bug fix for Ctrl + b/f --- botanjs/src/Components/Vim/Controls.js | 10 +++++++--- botanjs/src/Components/Vim/LineFeeder.js | 10 +++++----- botanjs/src/Components/Vim/VimArea.js | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 950ddbc..e6f529a 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -270,14 +270,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 +289,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; diff --git a/botanjs/src/Components/Vim/LineFeeder.js b/botanjs/src/Components/Vim/LineFeeder.js index b27779f..1385ee8 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; diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index 116bd46..95b02c6 100644 --- a/botanjs/src/Components/Vim/VimArea.js +++ b/botanjs/src/Components/Vim/VimArea.js @@ -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 From c24f74f70c406205c57e71fc949d518863192fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Thu, 31 Mar 2016 18:52:20 +0800 Subject: [PATCH 02/11] Delete (Tested): dd diw d{bracket} dG dgg --- botanjs/src/Components/Vim/Actions/DELETE.js | 99 ++++++++++++++++++-- botanjs/src/Components/Vim/Cursor.js | 9 +- 2 files changed, 98 insertions(+), 10 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js index b509fb5..f5af2e0 100644 --- a/botanjs/src/Components/Vim/Actions/DELETE.js +++ b/botanjs/src/Components/Vim/Actions/DELETE.js @@ -20,6 +20,7 @@ this.__cursor = Cursor; this.__nline = 0; this.__startX = Cursor.aPos; + this.__panY = this.__cursor.feeder.panY; }; DELETE.prototype.allowMovement = true; @@ -33,6 +34,7 @@ { e.preventDefault(); + if( e.ModKeys ) return; /** @type {Components.Vim.State.Registers} */ var reg = e.target.registers; @@ -44,18 +46,90 @@ if( sp == undefined ) { - if( this.__startX != cur.aPos ) - { - Triggered = true; + Triggered = true; - if( e.kMap( "l" ) ) + sp = this.__startX; + + cur.suppressEvent(); + + 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" ) ) + { + 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" ) ) + { + 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 ); + } } - else return; + // Remove the current line + else + { + if( e.kMap( "d" ) ) + { + 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 + { + cur.unsuppressEvent(); + return false; + } + } + + cur.unsuppressEvent(); } var c = feeder.content; @@ -76,6 +150,15 @@ feeder.content = c.substring( 0, s ) + c.substring( e + 1 ); + // Try to keep the original panning if possible + cur.feeder.pan( undefined + , this.__panY < cur.feeder.panY + ? this.__panY - cur.feeder.panY + : undefined + ); + + cur.moveTo( s ); + var stator = new Stator( cur, s ); var stack = new Stack(); @@ -96,8 +179,6 @@ cur.rec.record( stack ); - feeder.pan(); - return Triggered; }; diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index ce66bd8..3f884cc 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -92,7 +92,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; From cfa2e45e3df176a56f6b0c197fc8e3cec3519516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Thu, 31 Mar 2016 20:25:43 +0800 Subject: [PATCH 03/11] Put command --- botanjs/src/Components/Vim/Actions/DELETE.js | 6 +- botanjs/src/Components/Vim/Actions/INSERT.js | 4 +- botanjs/src/Components/Vim/Actions/PUT.js | 81 +++++++++++++++++++ botanjs/src/Components/Vim/Actions/VISUAL.js | 6 ++ botanjs/src/Components/Vim/Controls.js | 55 ++++++++----- botanjs/src/Components/Vim/State/Registers.js | 8 ++ botanjs/src/Components/Vim/_this.js | 1 + 7 files changed, 135 insertions(+), 26 deletions(-) create mode 100644 botanjs/src/Components/Vim/Actions/PUT.js diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js index f5af2e0..1f05cbb 100644 --- a/botanjs/src/Components/Vim/Actions/DELETE.js +++ b/botanjs/src/Components/Vim/Actions/DELETE.js @@ -151,9 +151,9 @@ feeder.content = c.substring( 0, s ) + c.substring( e + 1 ); // Try to keep the original panning if possible - cur.feeder.pan( undefined - , this.__panY < cur.feeder.panY - ? this.__panY - cur.feeder.panY + feeder.pan( undefined + , this.__panY < feeder.panY + ? this.__panY - feeder.panY : undefined ); diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js index 7eb38eb..0e805ee 100644 --- a/botanjs/src/Components/Vim/Actions/INSERT.js +++ b/botanjs/src/Components/Vim/Actions/INSERT.js @@ -29,7 +29,7 @@ /** @type {Components.Vim.Cursor} */ this.__cursor = Cursor; - this.__Stator = new Stator( Cursor ); + this.__stator = new Stator( Cursor ); // Initialize this stack this.__rec( "", true ); @@ -55,7 +55,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 ); diff --git a/botanjs/src/Components/Vim/Actions/PUT.js b/botanjs/src/Components/Vim/Actions/PUT.js new file mode 100644 index 0000000..164899b --- /dev/null +++ b/botanjs/src/Components/Vim/Actions/PUT.js @@ -0,0 +1,81 @@ +(function(){ + var ns = __namespace( "Components.Vim.Actions" ); + + var Mesg = __import( "Components.Vim.Message" ); + + /** @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.__stator = new Stator( Cursor ); + this.__msg = ""; + }; + + PUT.prototype.allowMovement = false; + + PUT.prototype.dispose = function() + { + }; + + 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 aP = cur.aPos; + + feeder.content = feeder.content.substring( 0, aP ) + + cput + + feeder.content.substring( aP ); + + cur.suppressEvent(); + feeder.pan(); + + cur.moveTo( 0 < nLines ? aP : aP + clen, true ); + + var stack = new Stack(); + + stack.store( this.__stator.save( clen, "" ) ); + cur.rec.record( stack ); + + this.__put = cput; + + if( nLines ) + { + this.__msg = Mesg( "LINE_MORE", nLines ); + } + + cur.moveX( -1 ); + cur.unsuppressEvent(); + + return true; + }; + + PUT.prototype.getMessage = function() + { + console.log( this.__msg ); + 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 7cac789..caac607 100644 --- a/botanjs/src/Components/Vim/Actions/VISUAL.js +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -76,6 +76,12 @@ } Action.handler( e, this.__startaP ); + + if( Action.constructor != DELETE ) + { + cur.moveTo( this.__startaP ); + } + this.__leaveMesg = Action.getMessage(); Action.dispose(); diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index e6f529a..522e35d 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -35,6 +35,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 ) @@ -128,12 +129,12 @@ this.__ccur = this.__cfeeder.cursor; }; - 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 +144,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 +154,7 @@ if( compReg.i == keys.length ) { compReg.handler( e ); - this.__compReg = null; + this.__compositeReg = null; this.__cMovement = false; } @@ -161,8 +162,8 @@ } } - if( this.__compReg ) beep(); - this.__compReg = null; + if( this.__compositeReg ) beep(); + this.__compositeReg = null; this.__cMovement = false; return false; }; @@ -191,6 +192,15 @@ case D: // Del with motion ccur.openAction( "DELETE" ); break; + + case P: // Put + ccur.suppressEvent(); + ccur.moveX( 1, false, true ); + ccur.unsuppressEvent(); + case SHIFT + P: // Put before + ccur.openRunAction( "PUT", e ); + break; + case X: // Del break; case SHIFT + X: // Delete before @@ -247,11 +257,11 @@ { var kCode = e.keyCode; - if( this.__cMovement && this.__comp ) + if( this.__cMovement && this.__composite ) { if( !e.ModKeys ) { - this.__comp( e ); + this.__composite( e ); return true; } } @@ -344,7 +354,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 ); @@ -363,25 +373,28 @@ }; // 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 SLASH: // "/" Seach movement + this.__cMovement = true; + break; default: cursorHandled = false; } @@ -402,9 +415,9 @@ ) return; // Clear composite command - if( e.Escape && this.__compReg ) + if( e.Escape && this.__compositeReg ) { - this.__compReg = null; + this.__compositeReg = null; this.__cMovement = false; beep(); return; diff --git a/botanjs/src/Components/Vim/State/Registers.js b/botanjs/src/Components/Vim/State/Registers.js index 220fe70..48ca4a3 100644 --- a/botanjs/src/Components/Vim/State/Registers.js +++ b/botanjs/src/Components/Vim/State/Registers.js @@ -46,5 +46,13 @@ r[ 1 ] = str; }; + 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/_this.js b/botanjs/src/Components/Vim/_this.js index f9e572e..8a5b199 100644 --- a/botanjs/src/Components/Vim/_this.js +++ b/botanjs/src/Components/Vim/_this.js @@ -19,6 +19,7 @@ , "REDO_LIMIT": "Already at newest change" , "LINE_FEWER": "%1 fewer lines" + , "LINE_MORE": "%1 more lines" }; var errors = { From af2da6e023b43d2049e5c3490d7985dc61bdedd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Thu, 31 Mar 2016 23:37:44 +0800 Subject: [PATCH 04/11] Added YANK --- botanjs/src/Components/Vim/Actions/DELETE.js | 24 ++-- botanjs/src/Components/Vim/Actions/PUT.js | 30 +++- botanjs/src/Components/Vim/Actions/YANK.js | 136 +++++++++++++++++- botanjs/src/Components/Vim/Controls.js | 4 + botanjs/src/Components/Vim/State/Registers.js | 33 +++-- botanjs/src/Components/Vim/_this.js | 5 +- .../externs/Components.Vim.State.Registers.js | 8 +- 7 files changed, 207 insertions(+), 33 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js index 1f05cbb..16daab5 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" ); @@ -21,13 +22,15 @@ 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 ) @@ -43,6 +46,7 @@ var feeder = cur.feeder; var Triggered = false; + var newLine = false; if( sp == undefined ) { @@ -50,8 +54,6 @@ sp = this.__startX; - cur.suppressEvent(); - var currAp = cur.aPos; if( this.__startX != currAp ) { @@ -73,6 +75,7 @@ // Remove the current and the following line else if( e.kMap( "j" ) ) { + newLine = true; cur.lineEnd( true ); sp = cur.aPos; cur.moveY( -1 ); @@ -82,6 +85,7 @@ // Remove the current and the preceding line else if( e.kMap( "k" ) ) { + newLine = true; cur.moveY( 1 ); cur.lineEnd( true ); sp = cur.aPos; @@ -102,6 +106,7 @@ { if( e.kMap( "d" ) ) { + newLine = true; cur.lineEnd( true ); sp = cur.aPos; cur.lineStart(); @@ -124,12 +129,10 @@ } else { - cur.unsuppressEvent(); - return false; + beep(); + return true; } } - - cur.unsuppressEvent(); } var c = feeder.content; @@ -144,7 +147,7 @@ } var removed = c.substring( s, e + 1 ); - reg.change( removed ); + reg.change( removed, newLine ); this.__nline = occurence( removed, "\n" ); @@ -156,7 +159,6 @@ ? this.__panY - feeder.panY : undefined ); - cur.moveTo( s ); var stator = new Stator( cur, s ); @@ -165,9 +167,7 @@ c = c[ e + 1 ]; if( c == "\n" || c == undefined ) { - cur.suppressEvent(); cur.moveX( -1 ); - cur.unsuppressEvent(); } var f = stator.save( 0, removed ); @@ -186,7 +186,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/PUT.js b/botanjs/src/Components/Vim/Actions/PUT.js index 164899b..81ff721 100644 --- a/botanjs/src/Components/Vim/Actions/PUT.js +++ b/botanjs/src/Components/Vim/Actions/PUT.js @@ -16,14 +16,15 @@ { /** @type {Components.Vim.Cursor} */ this.__cursor = Cursor; - this.__stator = new Stator( Cursor ); this.__msg = ""; + Cursor.suppressEvent(); }; PUT.prototype.allowMovement = false; PUT.prototype.dispose = function() { + this.__cursor.unsuppressEvent(); }; PUT.prototype.handler = function( e ) @@ -42,38 +43,55 @@ var cur = this.__cursor; var feeder = cur.feeder; + var newLine = cput.newLine; + if( newLine ) + { + cur.moveY( 1 ); + cur.lineStart(); + } + + var stator = new Stator( cur ); var aP = cur.aPos; feeder.content = feeder.content.substring( 0, aP ) + cput + feeder.content.substring( aP ); - cur.suppressEvent(); feeder.pan(); cur.moveTo( 0 < nLines ? aP : aP + clen, true ); var stack = new Stack(); - stack.store( this.__stator.save( clen, "" ) ); + if( newLine ) + { + var f = stator.save( clen, "" ); + stack.store( function() + { + f(); + cur.moveY( -1 ); + } ); + } + else + { + stack.store( stator.save( clen, "" ) ); + } cur.rec.record( stack ); this.__put = cput; if( nLines ) { - this.__msg = Mesg( "LINE_MORE", nLines ); + this.__msg = Mesg( "LINES_MORE", nLines ); } cur.moveX( -1 ); - cur.unsuppressEvent(); return true; }; PUT.prototype.getMessage = function() { - console.log( this.__msg ); return this.__msg; }; diff --git a/botanjs/src/Components/Vim/Actions/YANK.js b/botanjs/src/Components/Vim/Actions/YANK.js index ba987d7..12f7a5c 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 522e35d..1163433 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -189,9 +189,13 @@ case CTRL + R: // Redo ccur.openRunAction( "REDO", e ); break; + case D: // Del with motion ccur.openAction( "DELETE" ); break; + case Y: // Yank with motion + ccur.openAction( "YANK" ); + break; case P: // Put ccur.suppressEvent(); diff --git a/botanjs/src/Components/Vim/State/Registers.js b/botanjs/src/Components/Vim/State/Registers.js index 48ca4a3..ec6335b 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,7 +59,7 @@ } } - r[ 1 ] = str; + r[ 1 ] = reg; }; Registers.prototype.get = function( r ) @@ -55,4 +71,5 @@ }; ns[ NS_EXPORT ]( EX_CLASS, "Registers", Registers ); + })(); diff --git a/botanjs/src/Components/Vim/_this.js b/botanjs/src/Components/Vim/_this.js index 8a5b199..e387304 100644 --- a/botanjs/src/Components/Vim/_this.js +++ b/botanjs/src/Components/Vim/_this.js @@ -18,8 +18,9 @@ , "UNDO_LIMIT": "Already at oldest change" , "REDO_LIMIT": "Already at newest change" - , "LINE_FEWER": "%1 fewer lines" - , "LINE_MORE": "%1 more lines" + , "LINES_FEWER": "%1 fewer line(s)" + , "LINES_MORE": "%1 more line(s)" + , "LINES_YANKED": "%1 line(s) yanked" }; var errors = { diff --git a/botanjs/src/externs/Components.Vim.State.Registers.js b/botanjs/src/externs/Components.Vim.State.Registers.js index 75b9c13..61bdfc7 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; From a927c901345730e8053cea9ec89cc27e9f910d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Fri, 1 Apr 2016 04:05:43 +0800 Subject: [PATCH 05/11] Rewrite moveX function --- botanjs/src/Components/Vim/Actions/PUT.js | 8 ++- botanjs/src/Components/Vim/Actions/VISUAL.js | 18 +++-- botanjs/src/Components/Vim/Controls.js | 9 +++ botanjs/src/Components/Vim/Cursor.js | 73 +++++++++++++++++--- botanjs/src/Components/Vim/LineBuffer.js | 11 ++- 5 files changed, 101 insertions(+), 18 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/PUT.js b/botanjs/src/Components/Vim/Actions/PUT.js index 81ff721..2dbb263 100644 --- a/botanjs/src/Components/Vim/Actions/PUT.js +++ b/botanjs/src/Components/Vim/Actions/PUT.js @@ -44,9 +44,13 @@ var feeder = cur.feeder; var newLine = cput.newLine; + + // Compensation + var c = e.kMap( "P" ) ? 0 : -1; + if( newLine ) { - cur.moveY( 1 ); + cur.moveY( -c ); cur.lineStart(); } @@ -69,7 +73,7 @@ stack.store( function() { f(); - cur.moveY( -1 ); + cur.moveY( c ); } ); } else diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js index caac607..060cdbf 100644 --- a/botanjs/src/Components/Vim/Actions/VISUAL.js +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -36,10 +36,20 @@ 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 ) + { + c.moveX( -1 ); + c.moveX( 1 ); + } }; VISUAL.prototype.handler = function( e, done ) diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 1163433..8dc0ac3 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -396,6 +396,7 @@ }, _8 ); break; + case SLASH: // "/" Seach movement case SLASH: // "/" Seach movement this.__cMovement = true; break; @@ -443,7 +444,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 ) ) { diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index 3f884cc..3c4927f 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 @@ -118,6 +121,8 @@ var jumpY = expLineNum - lastLineNum; var jumpX = aPos < lineStart ? lineStart - aPos : aPos - lineStart; + jumpX += Math.ceil( jumpX / pline.cols ) - 1; + if( jumpY ) this.moveY( jumpY ); if( 0 < this.getLine().lineNum && lineStart <= aPos ) jumpX --; @@ -130,13 +135,19 @@ Cursor.prototype.moveX = function( d, penetrate, phantomSpace ) { var x = this.pX; + if( 0 < this.__off ) + { + d += this.__off; + this.__off = 0; + } var updatePx = Boolean( d ); 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 ) { @@ -153,23 +164,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 ) @@ -177,6 +226,7 @@ this.pX = x; this.updatePosition(); } + }; Cursor.prototype.lineStart = function() @@ -193,10 +243,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(); }; diff --git a/botanjs/src/Components/Vim/LineBuffer.js b/botanjs/src/Components/Vim/LineBuffer.js index 238cfb3..e097f83 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 + " "; } From 62d2de3800fa71d92b43f58a474b1ed0a76008aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Fri, 1 Apr 2016 04:52:41 +0800 Subject: [PATCH 06/11] fixed DEL the end resulting wrong pos --- botanjs/src/Components/Vim/Actions/DELETE.js | 6 ------ botanjs/src/Components/Vim/Actions/INSERT.js | 13 ++++++++----- botanjs/src/Components/Vim/Controls.js | 2 +- botanjs/src/Components/Vim/Cursor.js | 10 +++++++++- botanjs/src/Components/Vim/Syntax/Analyzer.js | 17 ++++++++++------- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js index 16daab5..1683c32 100644 --- a/botanjs/src/Components/Vim/Actions/DELETE.js +++ b/botanjs/src/Components/Vim/Actions/DELETE.js @@ -164,12 +164,6 @@ var stator = new Stator( cur, s ); var stack = new Stack(); - c = c[ e + 1 ]; - if( c == "\n" || c == undefined ) - { - cur.moveX( -1 ); - } - var f = stator.save( 0, removed ); stack.store( function() { f(); diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js index 0e805ee..f119a30 100644 --- a/botanjs/src/Components/Vim/Actions/INSERT.js +++ b/botanjs/src/Components/Vim/Actions/INSERT.js @@ -33,12 +33,19 @@ // 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 ); }; @@ -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/Controls.js b/botanjs/src/Components/Vim/Controls.js index 8dc0ac3..587cda3 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -179,7 +179,7 @@ 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; diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index 3c4927f..07e07b4 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -124,6 +124,8 @@ 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 ); @@ -135,13 +137,14 @@ 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; } - var updatePx = Boolean( d ); if( updatePx ) x = this.X + d; if( !d ) d = 1; @@ -368,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; @@ -381,6 +387,8 @@ this.__pulseMsg = this.action.getMessage(); this.action = null; + debug.Info( "closeAction" ); + // Reset the analyzed content this.Vim.contentAnalyzer.reset(); diff --git a/botanjs/src/Components/Vim/Syntax/Analyzer.js b/botanjs/src/Components/Vim/Syntax/Analyzer.js index 54014ce..c10e441 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(); }; From ddc7058964684cd45a0339fabbf891bf49914933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Fri, 1 Apr 2016 05:22:02 +0800 Subject: [PATCH 07/11] calculate document position --- botanjs/src/Components/Vim/Actions/VISUAL.js | 8 ++++---- botanjs/src/Components/Vim/Cursor.js | 2 +- botanjs/src/Components/Vim/LineFeeder.js | 11 +++++++++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js index 060cdbf..6d32caa 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,7 +34,6 @@ VISUAL.prototype.dispose = function() { - this.__msg = this.__leaveMesg; var c = this.__cursor; c.blink = true; @@ -45,10 +43,12 @@ // This fix the highlighting position of missing phantomSpace // for maximum filled line - if( c.feeder.wrap ) + if( c.feeder.wrap && 0 < c.X ) { + c.suppressEvent(); c.moveX( -1 ); c.moveX( 1 ); + c.unsuppressEvent(); } }; @@ -92,7 +92,7 @@ cur.moveTo( this.__startaP ); } - this.__leaveMesg = Action.getMessage(); + this.__msg = Action.getMessage(); Action.dispose(); cur.unsuppressEvent(); diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index 07e07b4..be80a42 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -387,7 +387,7 @@ this.__pulseMsg = this.action.getMessage(); this.action = null; - debug.Info( "closeAction" ); + debug.Info( "closeAction: " + this.__pulseMsg ); // Reset the analyzed content this.Vim.contentAnalyzer.reset(); diff --git a/botanjs/src/Components/Vim/LineFeeder.js b/botanjs/src/Components/Vim/LineFeeder.js index 1385ee8..2ead9a0 100644 --- a/botanjs/src/Components/Vim/LineFeeder.js +++ b/botanjs/src/Components/Vim/LineFeeder.js @@ -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 { From c7249c071c9b3aa6787b930e0d9c4ce3e5dd42b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Sat, 2 Apr 2016 14:25:25 +0800 Subject: [PATCH 08/11] Search Ex Draft --- botanjs/src/Components/Vim/Actions/PUT.js | 2 - botanjs/src/Components/Vim/Controls.js | 123 +++++++++++--- botanjs/src/Components/Vim/Cursor.js | 6 +- botanjs/src/Components/Vim/Ex/Search.js | 150 ++++++++++++++++++ botanjs/src/Components/Vim/StatusBar.js | 5 + botanjs/src/Components/Vim/VimArea.js | 5 +- .../src/externs/Components.Vim.StatusBar.js | 2 + 7 files changed, 268 insertions(+), 25 deletions(-) create mode 100644 botanjs/src/Components/Vim/Ex/Search.js diff --git a/botanjs/src/Components/Vim/Actions/PUT.js b/botanjs/src/Components/Vim/Actions/PUT.js index 2dbb263..e58c9f9 100644 --- a/botanjs/src/Components/Vim/Actions/PUT.js +++ b/botanjs/src/Components/Vim/Actions/PUT.js @@ -1,8 +1,6 @@ (function(){ var ns = __namespace( "Components.Vim.Actions" ); - var Mesg = __import( "Components.Vim.Message" ); - /** @type {Components.Vim.State.Stator} */ var Stator = __import( "Components.Vim.State.Stator" ); /** @type {Components.Vim.State.Stack} */ diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 587cda3..e129357 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.Search} */ + var ExSearch = __import( "Components.Vim.Ex.Search" ); + 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; @@ -70,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; @@ -127,6 +142,10 @@ 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.__composite = function( e, handler ) @@ -183,6 +202,19 @@ 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; @@ -205,9 +237,15 @@ ccur.openRunAction( "PUT", e ); break; - case X: // Del - 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; @@ -240,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 ) @@ -252,16 +295,18 @@ 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.__composite ) + if( this.__cMovement ) { if( !e.ModKeys ) { @@ -396,9 +441,11 @@ }, _8 ); break; - case SLASH: // "/" Seach movement case SLASH: // "/" Seach movement this.__cMovement = true; + + this.__divedCCmd = new ExSearch( ccur ); + this.__divedCCmd.handler( e ); break; default: cursorHandled = false; @@ -420,12 +467,39 @@ ) return; // Clear composite command - if( e.Escape && this.__compositeReg ) + if( e.Escape ) { - this.__compositeReg = 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; + } + else return; } var cfeeder = this.__cfeeder; @@ -470,19 +544,30 @@ var InputEvent = function( sender, e ) { - this.__e = e; this.__target = sender; - 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; }; diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index be80a42..bd1967d 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -396,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(); @@ -497,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/Search.js b/botanjs/src/Components/Vim/Ex/Search.js new file mode 100644 index 0000000..169f5a5 --- /dev/null +++ b/botanjs/src/Components/Vim/Ex/Search.js @@ -0,0 +1,150 @@ +(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" ); + + var Mesg = __import( "Components.Vim.Message" ); + + /** @type {Components.Vim.Cursor.IAction} */ + var Search = function( Cursor ) + { + var _self = this; + + /** @type {Components.Vim.Cursor} */ + this.__cursor = Cursor; + + this.__statusBar = Cursor.Vim.statusBar; + + this.__command = []; + this.__blinkId = "ExSearchBlinkCycle" + Perf.uuid; + this.__curPos = 0; + + this.__disp = function() + { + }; + + 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; + }; + + Search.prototype.dispose = function() + { + this.__statusBar.override = null; + + Cycle.permaRemove( this.__blinkId ); + var feeder = this.__cursor.feeder; + feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); + }; + + Search.prototype.handler = function( e ) + { + e.preventDefault(); + + if( e.ModKeys ) return; + + this.__blink(); + + var InputKey = null; + + if( e.kMap( "Tab" ) ) + { + InputKey = "^I"; + } + else if( e.kMap( "C-v" ) ) + { + this.__direct = true; + } + else if( e.kMap( "BS" ) ) + { + this.__command.splice( --this.__curPos, 1 ); + if( this.__command.length == 0 ) return true; + } + else if( e.kMap( "Del" ) ) + { + this.__command.splice( this.__curPos, 1 ); + } + else if( e.kMap( "Enter" ) ) + { + return true; + } + else if( e.kMap( "Up" ) ) // History navigations + { + } + else if( e.kMap( "Down" ) ) + { + } + else if( e.kMap( "Left" ) ) + { + if( 0 < this.__curPos ) this.__curPos --; + } + else if( e.kMap( "Right" ) ) + { + if( this.__curPos < this.__command.length ) + this.__curPos ++; + } + else + { + InputKey = e.key; + } + + if( InputKey != null ) + { + this.__command.splice( this.__curPos ++, 0, InputKey ); + } + + var feeder = this.__cursor.feeder; + feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); + } + + ns[ NS_EXPORT ]( EX_CLASS, "Search", Search ); +})(); diff --git a/botanjs/src/Components/Vim/StatusBar.js b/botanjs/src/Components/Vim/StatusBar.js index 1a57d5c..81af357 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/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index 95b02c6..c5480ec 100644 --- a/botanjs/src/Components/Vim/VimArea.js +++ b/botanjs/src/Components/Vim/VimArea.js @@ -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 ); diff --git a/botanjs/src/externs/Components.Vim.StatusBar.js b/botanjs/src/externs/Components.Vim.StatusBar.js index 356c980..8e43a79 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; From 3ed7d2be846607d28ebdc417711d45a1d99b416d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Sat, 2 Apr 2016 18:35:12 +0800 Subject: [PATCH 09/11] History stacks --- botanjs/src/Components/Vim/Controls.js | 15 ++- .../Vim/Ex/{Search.js => Command.js} | 105 ++++++++++++++---- botanjs/src/Components/Vim/State/History.js | 96 ++++++++++++++++ botanjs/src/Components/Vim/VimArea.js | 16 +-- .../Components.Vim.Controls.InputEvent.js | 4 + .../externs/Components.Vim.State.History.js | 9 ++ 6 files changed, 212 insertions(+), 33 deletions(-) rename botanjs/src/Components/Vim/Ex/{Search.js => Command.js} (55%) create mode 100644 botanjs/src/Components/Vim/State/History.js create mode 100644 botanjs/src/externs/Components.Vim.State.History.js diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index e129357..631d736 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -4,8 +4,8 @@ /** @type {System.Debug} */ var debug = __import( "System.Debug" ); - /** @type {Components.Vim.Ex.Search} */ - var ExSearch = __import( "Components.Vim.Ex.Search" ); + /** @type {Components.Vim.Ex.Command} */ + var ExCommand = __import( "Components.Vim.Ex.Command" ); var beep = ns[ NS_INVOKE ]( "Beep" ); @@ -441,10 +441,10 @@ }, _8 ); break; - case SLASH: // "/" Seach movement + case SLASH: // "/" Search movement this.__cMovement = true; - this.__divedCCmd = new ExSearch( ccur ); + this.__divedCCmd = new ExCommand( ccur, "/" ); this.__divedCCmd.handler( e ); break; default: @@ -499,7 +499,8 @@ this.__cMovement = false; this.__divedCCmd = null; } - else return; + + if( e.canceled ) return; } var cfeeder = this.__cfeeder; @@ -545,6 +546,7 @@ var InputEvent = function( sender, e ) { this.__target = sender; + this.__canceled = false; if( typeof( e ) == "string" ) { @@ -572,11 +574,14 @@ 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/Ex/Search.js b/botanjs/src/Components/Vim/Ex/Command.js similarity index 55% rename from botanjs/src/Components/Vim/Ex/Search.js rename to botanjs/src/Components/Vim/Ex/Command.js index 169f5a5..cd8600b 100644 --- a/botanjs/src/Components/Vim/Ex/Search.js +++ b/botanjs/src/Components/Vim/Ex/Command.js @@ -8,27 +8,37 @@ /** @type {System.utils.Perf} */ var Perf = __import( "System.utils.Perf" ); - var Mesg = __import( "Components.Vim.Message" ); + /**j@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 Search = function( Cursor ) + 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.__blinkId = "ExSearchBlinkCycle" + Perf.uuid; + this.__currentCommand = null; + this.__blinkId = "ExCommandBlinkCycle" + Perf.uuid; this.__curPos = 0; - this.__disp = function() - { - }; - - var feeder = Cursor.feeder; + var feeder = Cursor.feeder; var __blink = false; var __holdBlink = false; @@ -77,7 +87,7 @@ this.__statusBar.override = this.__doBlink; }; - Search.prototype.dispose = function() + Command.prototype.dispose = function() { this.__statusBar.override = null; @@ -86,7 +96,7 @@ feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); }; - Search.prototype.handler = function( e ) + Command.prototype.handler = function( e ) { e.preventDefault(); @@ -96,6 +106,8 @@ var InputKey = null; + var histNav = false; + if( e.kMap( "Tab" ) ) { InputKey = "^I"; @@ -106,8 +118,15 @@ } 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 ) return true; + if( this.__command.length == 0 ) + { + e.cancel(); + return true; + } } else if( e.kMap( "Del" ) ) { @@ -115,23 +134,56 @@ } else if( e.kMap( "Enter" ) ) { + this.__process(); return true; } - else if( e.kMap( "Up" ) ) // History navigations - { - } - else if( e.kMap( "Down" ) ) - { - } else if( e.kMap( "Left" ) ) { - if( 0 < this.__curPos ) this.__curPos --; + 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; @@ -142,9 +194,22 @@ 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" ) ); - } + }; - ns[ NS_EXPORT ]( EX_CLASS, "Search", Search ); + Command.prototype.__process = function() + { + this.__hist.push( this.__command ); + }; + + ns[ NS_EXPORT ]( EX_CLASS, "Command", Command ); })(); diff --git a/botanjs/src/Components/Vim/State/History.js b/botanjs/src/Components/Vim/State/History.js new file mode 100644 index 0000000..18c67be --- /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/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index c5480ec..ee76302 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 @@ -136,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/externs/Components.Vim.Controls.InputEvent.js b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js index afe109d..6678a27 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 0000000..f28f348 --- /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; From 062e9d8bb94cbf431296e0584b72cda96a381c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Sat, 2 Apr 2016 20:00:23 +0800 Subject: [PATCH 10/11] FIND command --- botanjs/src/Components/Vim/Actions/FIND.js | 127 ++++++++++++++++++ .../src/Components/Vim/Actions/PRINT_HEX.js | 2 - botanjs/src/Components/Vim/Controls.js | 5 + botanjs/src/Components/Vim/Ex/Command.js | 19 ++- botanjs/src/Components/Vim/_this.js | 3 + 5 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 botanjs/src/Components/Vim/Actions/FIND.js diff --git a/botanjs/src/Components/Vim/Actions/FIND.js b/botanjs/src/Components/Vim/Actions/FIND.js new file mode 100644 index 0000000..c94f4f0 --- /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/PRINT_HEX.js b/botanjs/src/Components/Vim/Actions/PRINT_HEX.js index c3efbbb..185bb28 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/Controls.js b/botanjs/src/Components/Vim/Controls.js index 631d736..19c76bd 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -441,6 +441,11 @@ }, _8 ); break; + case SHIFT + N: // Next Search + case N: // Next Search + ccur.openRunAction( "FIND", e ); + break; + case SLASH: // "/" Search movement this.__cMovement = true; diff --git a/botanjs/src/Components/Vim/Ex/Command.js b/botanjs/src/Components/Vim/Ex/Command.js index cd8600b..d3bfe05 100644 --- a/botanjs/src/Components/Vim/Ex/Command.js +++ b/botanjs/src/Components/Vim/Ex/Command.js @@ -8,7 +8,7 @@ /** @type {System.utils.Perf} */ var Perf = __import( "System.utils.Perf" ); - /**j@type {Components.Vim.State.History} */ + /** @type {Components.Vim.State.History} */ var History = __import( "Components.Vim.State.History" ); var Mesg = __import( "Components.Vim.Message" ); var beep = __import( "Components.Vim.Beep" ); @@ -134,7 +134,7 @@ } else if( e.kMap( "Enter" ) ) { - this.__process(); + this.__process( e ); return true; } else if( e.kMap( "Left" ) ) @@ -206,9 +206,22 @@ feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); }; - Command.prototype.__process = function() + 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/_this.js b/botanjs/src/Components/Vim/_this.js index e387304..345a882 100644 --- a/botanjs/src/Components/Vim/_this.js +++ b/botanjs/src/Components/Vim/_this.js @@ -21,6 +21,9 @@ , "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 = { From 63575ce2e6c83650291857f827742262e1422a1f Mon Sep 17 00:00:00 2001 From: tgckpg Date: Sat, 2 Apr 2016 20:08:04 +0800 Subject: [PATCH 11/11] Update README.md --- README.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fb2bbb2..4fd1003 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,23 @@ hjkl G Ctrl + f, Ctrl + b %$^ +nN +/{pattern} INSERT: aA i +oO commands that starts with g: gg g8 VISUAL: -v ( viw, vi{bracket} ) +v{motion} ( viw, vi{bracket} ) +v{motion}y // YANK + +YANK / DELETE: +iw, i{bracket} Undo / Redo: ( might have bugs, please file issue if bugged. But I think I will finish other features first if it is not very critical ) u / Ctrl + r @@ -40,9 +47,8 @@ This is based on a framework I wrote called BotanJS. Which is a frontend framewo If you are also interested in [BotanJS](https://github.com/tgckpg/BotanJS). Please head to the project page [here](https://github.com/tgckpg/BotanJS). *Warning*, it might NOT be easy to understand. -### I made this -You made this? I made this. -Because I LOVE Vim, and [wasavi](https://github.com/akahuku/wasavi) in firefox does not work for me. I know peoples are busy so fuck me I am going to make one myself, alright? +### Why make another one? +Because [wasavi](https://github.com/akahuku/wasavi) in firefox does not work for me. I know peoples are busy so fuck me I am going to make one myself, alright? ### Use wasavi if you can #### [wasavi! wasavi! wasavi!](https://github.com/akahuku/wasavi) @@ -52,3 +58,6 @@ I tried porting it into the browser tho. But I am too stupid to do that. But sti ### How can I contribute? First, you need to understand the framework behind it. Then file a merge request. Sounds easy right?... prepare to DIEEE! + +### You made this? I made this. +Licensed under GNU Pubic License