From f227c7e16b2b43feb875d8e93adf3d428607b17b 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, 18 Mar 2016 05:55:04 +0800 Subject: [PATCH 01/11] G / gg commands --- botanjs/src/Components/Vim/Cursor.js | 50 ++++++++++++------- botanjs/src/Components/Vim/LineFeeder.js | 4 +- .../src/externs/Components.Vim.LineFeeder.js | 2 + 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index fc3d4e1..09765b1 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -168,29 +168,45 @@ else if( this.feeder.moreAt < Y ) { var feeder = this.feeder; - var lastLine = feeder.lastBuffer.lineNum; - var lineShift = Y - feeder.moreAt; - var i = 0; - while( !feeder.EOF ) - { - feeder.pan( undefined, lineShift + i ); - // if it turns out to be the same line - // before after panning - // we keep scrolling it ( panning ) - // until the entire line cosumes the screen - if( feeder.lastBuffer.lineNum == lastLine ) + if( penentrate ) + { + feeder.pan( undefined, Y - moreAt ); + } + else if( feeder.linesTotal < Y ) + { + while( !feeder.EOF ) { - i ++; + feeder.pan( undefined, 1 ); } - else break; + } + else + { + var lastLine = feeder.lastBuffer.lineNum; + var lineShift = Y - feeder.moreAt; + + i = lineShift; + while( !feeder.EOF ) + { + feeder.pan( undefined, i ); + + // if it turns out to be the same line + // before after panning + // we keep scrolling it ( panning ) + // until the entire line cosumes the screen + if( feeder.lastBuffer.lineNum == lastLine ) + { + i ++; + } + else break; + } + + // The line number cursor need to be in + Y = lastLine + lineShift; } - // The line number cursor need to be in - Y = lastLine + lineShift; - - // Calculate the visual line position + // Calculate the visual line position "i" for( i = 0, line = feeder.firstBuffer; line != feeder.lastBuffer; line = line.next ) diff --git a/botanjs/src/Components/Vim/LineFeeder.js b/botanjs/src/Components/Vim/LineFeeder.js index 3d0ca71..7bedae0 100644 --- a/botanjs/src/Components/Vim/LineFeeder.js +++ b/botanjs/src/Components/Vim/LineFeeder.js @@ -9,6 +9,8 @@ /** @type {Components.Vim.Cursor} */ var Cursor = ns[ NS_INVOKE ]( "Cursor" ); + var occurence = __import( "System.utils.Perf.CountSubstr" ); + var Feeder = function( rows, cols ) { var lineBuffers = []; @@ -170,7 +172,7 @@ }; __readOnly( Feeder.prototype, "linesTotal", function() { - return this.content.match( "\n" ); + return occurence( this.content, "\n" ); } ); __readOnly( Feeder.prototype, "firstBuffer", function() { diff --git a/botanjs/src/externs/Components.Vim.LineFeeder.js b/botanjs/src/externs/Components.Vim.LineFeeder.js index 1730d42..eb500db 100644 --- a/botanjs/src/externs/Components.Vim.LineFeeder.js +++ b/botanjs/src/externs/Components.Vim.LineFeeder.js @@ -37,6 +37,8 @@ Components.Vim.LineFeeder.panY; /** @type Number */ Components.Vim.LineFeeder.moreAt; /** @type Number */ +Components.Vim.LineFeeder.linesTotal; +/** @type Number */ Components.Vim.LineFeeder.linesOccupied; /** @type String */ Components.Vim.LineFeeder.docPos; From b17ca21420261025607816441764f165c6380005 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, 18 Mar 2016 08:34:36 +0800 Subject: [PATCH 02/11] fixed G / gg issue on some cases --- botanjs/src/Components/Vim/Cursor.js | 62 +++++++++++++++++++--------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index 09765b1..682232a 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -55,6 +55,25 @@ return offset; }; + // Rush cursor to wanted position "d" then get the actual position + var GetRushPos = function( c, d ) + { + var line = c.getLine(); + var l = c.Y + d; + var i = c.Y; + + // First line ( visual ) does not count + if( line != c.feeder.firstBuffer ) i --; + + for( ; i < l; line = line.nextLine ) + { + if( line.placeholder ) break; + if( line.br ) i ++; + } + + return i; + }; + var Cursor = function( feeder ) { /** @type {Components.Vim.LineFeeder} */ @@ -151,24 +170,26 @@ Cursor.prototype.moveY = function( d, penentrate ) { + var i; var Y = this.Y + d; + var feeder = this.feeder; var line; if( Y < 0 ) { - this.feeder.pan( undefined, d ); + feeder.pan( undefined, d ); this.Y = 0; this.moveX(); this.updatePosition(); - this.feeder.softReset(); + feeder.softReset(); return; } - else if( this.feeder.moreAt < Y ) + // More at bottom, start panning + else if( !feeder.EOF && feeder.moreAt < Y ) { var feeder = this.feeder; - var i = 0; if( penentrate ) { @@ -180,6 +201,8 @@ { feeder.pan( undefined, 1 ); } + + i = GetRushPos( this, d ); } else { @@ -204,15 +227,16 @@ // The line number cursor need to be in Y = lastLine + lineShift; - } - // Calculate the visual line position "i" - for( i = 0, line = feeder.firstBuffer; - line != feeder.lastBuffer; - line = line.next ) - { - if( line.br ) i ++; - if( line.lineNum == Y || line.next.placeholder ) break; + // Calculate the visual line position "i" + for( i = 0, line = feeder.firstBuffer; + line != feeder.lastBuffer; + line = line.next ) + { + if( line.br ) i ++; + if( line.lineNum == Y || line.next.placeholder ) break; + } + } this.Y = i; @@ -220,20 +244,18 @@ this.moveX(); this.updatePosition(); + // Because it is panned, soft reset is needed feeder.softReset(); return; } else if ( 0 < d ) { - // If panning is forward - // and next line does not exists - line = this.getLine().nextLine; - if( !line || line.placeholder ) - { - // do nothing - return; - } + var line = this.getLine(); + // If already at bottom + if( line.nextLine.placeholder ) return; + + Y = GetRushPos( this, d ); } this.Y = Y; From dc63a882e38a2143904ff10487136ac686b5420a 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, 19 Mar 2016 19:05:07 +0800 Subject: [PATCH 03/11] Aaiu, cursor position --- botanjs/src/Components/Vim/Actions/INSERT.js | 37 ++++++++++++++++++-- botanjs/src/Components/Vim/Controls.js | 9 ++--- botanjs/src/Components/Vim/Cursor.js | 11 +++--- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js index 0628330..d97d34c 100644 --- a/botanjs/src/Components/Vim/Actions/INSERT.js +++ b/botanjs/src/Components/Vim/Actions/INSERT.js @@ -27,12 +27,24 @@ /** @type {Components.Vim.Cursor} */ this.__cursor = Cursor; - this.__startX = Cursor.aPos; + this.__startState = this.__saveCur(); // Initialize this stack this.__rec( "", true ); }; + INSERT.prototype.__saveCur = function() + { + var c = this.__cursor; + return { + p: c.P + , x: c.X + , y: c.Y + , px: c.feeder.panX + , py: c.feeder.panY + }; + } + INSERT.prototype.dispose = function() { this.__cursor.moveX( -1 ); @@ -46,8 +58,11 @@ var insertLength = this.__insertLength; var contentUndo = this.__contentUndo; var startPos = this.__startPosition; - var startX = this.__startX; + var sSt = this.__startState; + var eSt = this.__saveCur(); + var st = sSt; + // Calling this repeatedly will swap between UNDO / REDO state return function() { var contentRedo = feeder.content.substr( startPos, insertLength ); feeder.content = @@ -57,7 +72,15 @@ insertLength = contentUndo.length; contentUndo = contentRedo; + cur.P = st.p; + cur.X = st.x; + cur.Y = st.y; + feeder.panX = st.px; + feeder.panY = st.py; + feeder.pan(); + + st = ( st == sSt ) ? eSt : sSt; }; }; @@ -168,7 +191,15 @@ this.__rec( inputChar ); - cur.moveX( 1 ); + if( inputChar == "\n" ) + { + cur.moveY( 1 ); + cur.lineStart(); + } + else + { + cur.moveX( 1 ); + } }; INSERT.prototype.getMessage = function() diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index dcaa9dc..5e4d892 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -55,6 +55,7 @@ }; Controls.prototype.__comboT = function( e ) { return false; }; + Controls.prototype.__comboD = function( e ) { return false; }; // < Controls.prototype.__comboLeftShift = function( e ) { return false; }; @@ -65,6 +66,7 @@ Controls.prototype.__comboKey = function( e ) { return this.__comboG( e ) + || this.__comboD( e ) || this.__comboT( e ) || this.__comboLeftShift( e ) || this.__comboRightShift( e ); @@ -159,11 +161,12 @@ break; // Insert + case SHIFT + A: // Append at the line end + ccur.lineEnd(); case A: // Append cMoveX( 1, true, true ); - ccur.openAction( "INSERT" ); - break; case I: // Insert + ccur.openAction( "INSERT" ); break; case U: // Undo ccur.openRunAction( "UNDO", e ); @@ -173,8 +176,6 @@ break; case X: // Del break; - case SHIFT + A: // Append at the line end - break; case SHIFT + X: // Delete before break; case SHIFT + U: // Undo previous changes in oneline diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index 682232a..b57650f 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -112,11 +112,14 @@ var buffs = this.feeder.lineBuffers; - if( penentrate && x < 0 && ( 0 < this.feeder.panY || 0 < this.Y ) ) + if( penentrate ) { - this.moveY( -1 ); - this.lineEnd( phantomSpace ); - return; + if( x < 0 && ( 0 < this.feeder.panY || 0 < this.Y ) ) + { + this.moveY( -1 ); + this.lineEnd( phantomSpace ); + return; + } } /** @type {Components.Vim.LineBuffer} */ From 8769e69f35aa3117cf1d325ae4f211fef73c84e2 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: Sun, 20 Mar 2016 07:52:44 +0800 Subject: [PATCH 04/11] Insert mode line break bug fix --- botanjs/src/Components/Vim/Actions/INSERT.js | 25 ++++-- botanjs/src/Components/Vim/Controls.js | 85 +++++++++++--------- botanjs/src/Components/Vim/Cursor.js | 72 +++++++---------- botanjs/src/Components/Vim/LineFeeder.js | 2 +- botanjs/src/Components/Vim/VimArea.js | 6 +- botanjs/src/Components/Vim/_this.js | 1 + 6 files changed, 102 insertions(+), 89 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js index d97d34c..e6d4615 100644 --- a/botanjs/src/Components/Vim/Actions/INSERT.js +++ b/botanjs/src/Components/Vim/Actions/INSERT.js @@ -36,13 +36,21 @@ INSERT.prototype.__saveCur = function() { var c = this.__cursor; - return { + var obj = { p: c.P , x: c.X , y: c.Y , px: c.feeder.panX , py: c.feeder.panY }; + + if( 0 < obj.x ) + { + obj.p -= 1; + obj.x -= 1; + } + + return obj; } INSERT.prototype.dispose = function() @@ -186,20 +194,23 @@ + inputChar + feeder.content.substring( f ); - feeder.pan(); - feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); - - this.__rec( inputChar ); - if( inputChar == "\n" ) { + feeder.softReset(); + feeder.pan(); cur.moveY( 1 ); cur.lineStart(); } else { - cur.moveX( 1 ); + feeder.pan(); + cur.moveX( 1, false, true ); } + + feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); + + this.__rec( inputChar ); + }; INSERT.prototype.getMessage = function() diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 5e4d892..7f37de7 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -19,6 +19,12 @@ var U = 85; var V = 86; var W = 87; var X = 88; var Y = 89; var Z = 90; + var ESC = 27; + + var F1 = 112; var F2 = 113; var F3 = 114; var F4 = 115; var F5 = 116; + var F6 = 117; var F7 = 118; var F8 = 119; var F9 = 120; var F10 = 121; + var F11 = 122; var F12 = 123; + var Controls = function( vimArea ) { /** @type {Components.Vim.VimArea} */ @@ -77,7 +83,7 @@ // Neve capture these keys if( e.altKey // F2 - F12 - || ( 112 < e.keyCode && e.keyCode < 124 ) + || ( F1 < e.keyCode && e.keyCode < 124 ) ) return; var vArea = this.__vimArea; @@ -85,7 +91,7 @@ var cfeeder = vArea.contentFeeder; // Esc OR Ctrl + c - var Escape = e.keyCode == 27 || ( e.ctrlKey && e.keyCode == 67 ); + var Escape = e.keyCode == ESC || ( e.ctrlKey && e.keyCode == C ); // Clear the keychains in combo commands if( Escape && this.__keyChains.length ) @@ -141,26 +147,10 @@ } }; + var ActionHandled = true; + // Action Commands switch( kCode ) { - // Cursor movements - case BACKSPACE: // Backspace, go back 1 char, regardless of line - cMoveX( -1, true ); - break; - case H: // Left - cMoveX( -1 ); - break; - case L: // Right - cMoveX( 1 ); - break; - case K: // Up - cMoveY( -1 ); - break; - case J: // Down - cMoveY( 1 ); - break; - - // Insert case SHIFT + A: // Append at the line end ccur.lineEnd(); case A: // Append @@ -182,34 +172,57 @@ break; case SHIFT + I: // Append before the line start, after spaces break; + case SHIFT + J: // Join lines + break; + case SHIFT + K: // Find the manual entry + break; - case SHIFT + G: // Goto last line - ccur.moveY( Number.MAX_VALUE ); - ccur.moveX( Number.MAX_VALUE, true ); - break; - // remove characters - case X: // Remove in cursor - break; - case SHIFT + X: // Remove before cursor + case F1: // F1, help break; + default: + ActionHandled = false; + } + + if( ActionHandled ) return; + + // Cursor Commands + switch( kCode ) + { + case BACKSPACE: cMoveX( -1, true ); break; // Backspace, go back 1 char, regardless of line + case H: cMoveX( -1 ); break; // Left + case L: cMoveX( 1 ); break; // Right + case K: cMoveY( -1 ); break; // Up + case J: cMoveY( 1 ); break; // Down case SHIFT + H: // First line buffer break; case SHIFT + L: // Last line buffer break; - case SHIFT + _4: // $, End - ccur.lineEnd(); - break; - case SHIFT + _5: // %, Find next item - break; case SHIFT + _6: // ^, Start ccur.lineStart(); break; - case SHIFT + J: // Join lines + case SHIFT + _4: // $, End + ccur.lineEnd(); break; - case SHIFT + K: // manual entry + case SHIFT + G: // Goto last line + ccur.moveY( Number.MAX_VALUE ); + ccur.moveX( Number.MAX_VALUE, true ); + break; + + case SHIFT + _5: // %, Find next item + break; + } + + + // Integrated commands + switch( kCode ) + { + case V: // Visual + ccur.openAction( "VISUAL" ); + break; + case SHIFT + V: // Visual line + ccur.openAction( "VISUAL_LINE" ); break; - case 112: // F1, help } }; diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index b57650f..f4b6d9b 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -9,26 +9,6 @@ var Actions = __import( "Components.Vim.Actions.*" ); - var GetLine = function( buffs, l ) - { - /** @type {Components.Vim.LineBuffer} */ - var LineHead = buffs[0]; - l ++; - - for( var i = 0, line = LineHead; - line && i < l; i ++ ) - { - LineHead = line; - while( line ) - { - line = line.next; - if( line.br ) break; - } - } - - return LineHead; - }; - var LineOffset = function( buffs, l ) { /** @type {Components.Vim.LineBuffer} */ @@ -123,7 +103,7 @@ } /** @type {Components.Vim.LineBuffer} */ - var line = GetLine( buffs, this.Y ); + var line = this.getLine(); var content = line.visualLines.join( "\n" ); var cLen = content.length; @@ -211,35 +191,40 @@ { var lastLine = feeder.lastBuffer.lineNum; var lineShift = Y - feeder.moreAt; + var thisLine = this.getLine().lineNum; - i = lineShift; - while( !feeder.EOF ) + if( !feeder.EOF ) + feeder.pan( undefined, lineShift ); + + // if it turns out to be the same line + // before after panning + // we keep scrolling it ( panning ) + // until the entire line cosumes the screen + while( !feeder.EOF && feeder.lastBuffer.lineNum == lastLine ) { - feeder.pan( undefined, i ); - - // if it turns out to be the same line - // before after panning - // we keep scrolling it ( panning ) - // until the entire line cosumes the screen - if( feeder.lastBuffer.lineNum == lastLine ) - { - i ++; - } - else break; + feeder.pan( undefined, 1 ); } // The line number cursor need to be in - Y = lastLine + lineShift; + Y = thisLine + d; + i = this.Y; + this.Y = 0; // Calculate the visual line position "i" - for( i = 0, line = feeder.firstBuffer; - line != feeder.lastBuffer; - line = line.next ) + for( var line = this.getLine(); + line && line.lineNum != Y && !line.placeholder; + this.Y ++, line = this.getLine() ) { - if( line.br ) i ++; - if( line.lineNum == Y || line.next.placeholder ) break; } + i = this.Y; + + // Check if this line is collapsed + if( !feeder.EOF && feeder.lastBuffer.next.lineNum == line.lineNum ) + { + // If yes, step back to last visible line + i --; + } } this.Y = i; @@ -301,15 +286,16 @@ { var feeder = this.feeder; var line = feeder.firstBuffer; + var eBuffer = feeder.lastBuffer.next; for( var i = 0; - line != feeder.lastBuffer; + line != eBuffer; line = line.next ) { if( line.br ) i ++; - if( this.Y == i ) break; + if( this.Y == i ) return line; } - return line; + return null; }; // The absX for current Line diff --git a/botanjs/src/Components/Vim/LineFeeder.js b/botanjs/src/Components/Vim/LineFeeder.js index 7bedae0..e50f4ac 100644 --- a/botanjs/src/Components/Vim/LineFeeder.js +++ b/botanjs/src/Components/Vim/LineFeeder.js @@ -94,7 +94,7 @@ this.__softRender = function() { var line = _self.lineBuffers[ _self.__rStart ]; - var steps = _self.__rLength; + var steps = _self.__rLength + 1; for( var i = 0; line && i < steps && ( line = line.next ) && placeholdCond( line ); diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index b9a76e4..8452fab 100644 --- a/botanjs/src/Components/Vim/VimArea.js +++ b/botanjs/src/Components/Vim/VimArea.js @@ -92,7 +92,9 @@ // Content feeder var cfeeder = new LineFeeder( cRange, c ); - cfeeder.init( content ); + // This "\n" fixes the last line "\n" not displaying + // it will be trimmed after saving + cfeeder.init( content + "\n" ); // Status can consumes up to full screen, I think sfeeder = new LineFeeder( r, c ); @@ -147,7 +149,7 @@ }; __readOnly( VimArea.prototype, "content", function() { - return this.contentFeeder.content; + return this.contentFeeder.content.slice( 0, -1 ); } ); ns[ NS_EXPORT ]( EX_CLASS, "VimArea", VimArea ); diff --git a/botanjs/src/Components/Vim/_this.js b/botanjs/src/Components/Vim/_this.js index 5b263b9..04060da 100644 --- a/botanjs/src/Components/Vim/_this.js +++ b/botanjs/src/Components/Vim/_this.js @@ -5,6 +5,7 @@ "INSERT": "-- INSERT --" , "REPLACE": "-- REPLACE --" , "MORE": "-- MORE --" + , "VISUAL": "-- VISUAL --" , "VISLINE": "-- VISUAL LINE --" , "WRITE": "\"%1\" %2L, %3C written" , "CONTINUE": "Press ENTER or type command to continue" From e52c312af5f72ab345105ac17d636a87c5a5bbb7 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: Tue, 22 Mar 2016 02:08:07 +0800 Subject: [PATCH 05/11] added g8, v ( hightlight only ) Some structural changes on VimControls --- botanjs/src/Components/Vim/Actions/INSERT.js | 5 +- .../src/Components/Vim/Actions/PRINT_HEX.js | 52 ++++ botanjs/src/Components/Vim/Actions/VISUAL.js | 67 +++++ botanjs/src/Components/Vim/Controls.js | 283 ++++++++++-------- botanjs/src/Components/Vim/Cursor.js | 15 +- botanjs/src/Components/Vim/VimArea.js | 14 +- botanjs/src/externs/Components.Vim.Cursor.js | 6 +- botanjs/src/externs/Components.Vim.IAction.js | 3 + 8 files changed, 307 insertions(+), 138 deletions(-) create mode 100644 botanjs/src/Components/Vim/Actions/PRINT_HEX.js create mode 100644 botanjs/src/Components/Vim/Actions/VISUAL.js diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js index e6d4615..a199a5f 100644 --- a/botanjs/src/Components/Vim/Actions/INSERT.js +++ b/botanjs/src/Components/Vim/Actions/INSERT.js @@ -33,6 +33,8 @@ this.__rec( "", true ); }; + INSERT.prototype.allowMovement = false; + INSERT.prototype.__saveCur = function() { var c = this.__cursor; @@ -80,7 +82,8 @@ insertLength = contentUndo.length; contentUndo = contentRedo; - cur.P = st.p; + cur.PStart = st.p; + cur.PEnd = st.p + 1; cur.X = st.x; cur.Y = st.y; feeder.panX = st.px; diff --git a/botanjs/src/Components/Vim/Actions/PRINT_HEX.js b/botanjs/src/Components/Vim/Actions/PRINT_HEX.js new file mode 100644 index 0000000..c3efbbb --- /dev/null +++ b/botanjs/src/Components/Vim/Actions/PRINT_HEX.js @@ -0,0 +1,52 @@ +(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" ); + + /** @type {Components.Vim.Cursor.IAction} */ + var PRINT_HEX = function( Cursor ) + { + /** @type {Components.Vim.Cursor} */ + this.__cursor = Cursor; + }; + + PRINT_HEX.prototype.dispose = function() + { + }; + + PRINT_HEX.prototype.handler = function( e ) + { + e.preventDefault(); + var str = unescape( encodeURIComponent( this.__cursor.feeder.content[ this.__cursor.aPos ] ) ); + var l = str.length; + var msg = []; + for( var i = 0; i < l; i ++ ) + { + msg[i] = str[i] == "\n" + ? "a" + : str.charCodeAt( i ).toString( 16 ) + ; + + if( msg[i].length == 1 ) + { + msg[i] = "0" + msg[i]; + } + else if( msg[i].length == 0 ) + { + msg[i] = "00"; + } + } + + this.__msg = msg.join( " " ); + }; + + PRINT_HEX.prototype.getMessage = function() + { + return this.__msg; + }; + + ns[ NS_EXPORT ]( EX_CLASS, "PRINT_HEX", PRINT_HEX ); +})(); diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js new file mode 100644 index 0000000..752eaec --- /dev/null +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -0,0 +1,67 @@ +(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" ); + + var Mesg = __import( "Components.Vim.Message" ); + + /** @type {Components.Vim.Cursor.IAction} */ + var VISUAL = function( Cursor ) + { + /** @type {Components.Vim.Cursor} */ + this.__cursor = Cursor; + this.__startaP = Cursor.aPos; + this.__start = Cursor.PStart; + this.__selStart = Cursor.PStart; + + Cursor.blink = false; + }; + + VISUAL.prototype.allowMovement = true; + + VISUAL.prototype.dispose = function() + { + this.__cursor.blink = true; + this.__cursor.PStart = this.__selStart; + this.__cursor.PEnd = this.__selStart + 1; + }; + + + VISUAL.prototype.handler = function( e ) + { + e.preventDefault(); + + if( [ 16, 17, 18 ].indexOf( e.keyCode ) != -1 ) return; + var cur = this.__cursor; + var prevPos = this.__start; + var newPos = cur.PStart; + + var posDiff = newPos - prevPos; + if( 0 <= posDiff ) + { + this.__selStart = newPos; + newPos = newPos + 1; + } + else if( posDiff < 0 ) + { + prevPos += posDiff; + newPos = this.__start + 1; + this.__selStart = prevPos; + } + + cur.PStart = prevPos; + cur.PEnd = newPos; + }; + + VISUAL.prototype.getMessage = function() + { + var msg = Mesg( "VISUAL" ); + + return msg; + }; + + ns[ NS_EXPORT ]( EX_CLASS, "VISUAL", VISUAL ); +})(); diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 7f37de7..f55181b 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -7,6 +7,10 @@ var SHIFT = 1 << 9; var CTRL = 1 << 10; + var KEY_SHIFT = 16; + var KEY_CTRL = 17; + var KEY_ALT = 18; + var BACKSPACE = 8; var _0 = 48; var _1 = 49; var _2 = 50; var _3 = 51; var _4 = 52; @@ -29,132 +33,62 @@ { /** @type {Components.Vim.VimArea} */ this.__vimArea = vimArea - this.__keyChains = []; + + this.__cfeeder = vimArea.contentFeeder; + this.__sfeeder = vimArea.statusFeeder; + + this.__ccur = this.__cfeeder.cursor; }; - Controls.prototype.__comboG = function( keyCode ) + Controls.prototype.__comp = function( kCode, handler ) { - var keyON = this.__keyChains[ 0 ] == G; - if( keyON ) + if( handler ) { - var cursor = this.__vimArea.contentFeeder.cursor; - switch( keyCode ) - { - case G: - cursor.moveY( -Number.MAX_VALUE ); - cursor.moveX( -Number.MAX_VALUE, true ); - this.__keyChains = []; - return true; - default: - this.__keyChains = []; - beep(); - return true; - } - } - else if( keyCode == G ) - { - this.__keyChains[ 0 ] = G; + if( !this.__compReg ) this.__compReg = []; + this.__compReg.push({ + keys: Array.prototype.slice.call( arguments, 2 ) + , handler: handler + , i: 0 + }); return true; } + for( var i = 0; i < this.__compReg.length; i ++ ) + { + var compReg = this.__compReg[i]; + var keys = compReg.keys; + + if( keys[ compReg.i ++ ] == kCode ) + { + if( compReg.i == keys.length ) + { + compReg.handler(); + compReg = null; + this.__cMovement = false; + } + + return true; + } + } + + if( this.__compReg ) beep(); + this.__compReg = null; + this.__cMovement = false; return false; }; - Controls.prototype.__comboT = function( e ) { return false; }; - Controls.prototype.__comboD = function( e ) { return false; }; - - // < - Controls.prototype.__comboLeftShift = function( e ) { return false; }; - - // > - Controls.prototype.__comboRightShift = function( e ) { return false; }; - - Controls.prototype.__comboKey = function( e ) + Controls.prototype.__actionCommand = function( e, kCode ) { - return this.__comboG( e ) - || this.__comboD( e ) - || this.__comboT( e ) - || this.__comboLeftShift( e ) - || this.__comboRightShift( e ); - }; - - Controls.prototype.handler = function( sender, e ) - { - // Neve capture these keys - if( e.altKey - // F2 - F12 - || ( F1 < e.keyCode && e.keyCode < 124 ) - ) return; - - var vArea = this.__vimArea; - // Action Mode handled by the actions themselves - var cfeeder = vArea.contentFeeder; - - // Esc OR Ctrl + c - var Escape = e.keyCode == ESC || ( e.ctrlKey && e.keyCode == C ); - - // Clear the keychains in combo commands - if( Escape && this.__keyChains.length ) - { - this.__keyChains = []; - beep(); - return; - } - - if( cfeeder.cursor.action ) - { - if( Escape ) - { - e.preventDefault(); - cfeeder.cursor.closeAction(); - } - else - { - cfeeder.cursor.action.handler( e ); - } - return; - } - - e.preventDefault(); - var kCode = e.keyCode - + ( e.shiftKey || e.getModifierState( "CapsLock" ) ? SHIFT : 0 ) - + ( e.ctrlKey ? CTRL : 0 ); - - // Handles long commands - - if( this.__comboKey( kCode ) ) return; - - var cfeeder = vArea.contentFeeder; - var sfeeder = vArea.statusFeeder; - - var ccur = cfeeder.cursor; - - var cMoveX = function( a, b, c ) - { - var x = ccur.X; - ccur.moveX( a, b, c ); - if( ccur.X == x ) beep(); - }; - - var cMoveY = function( a ) - { - var y = ccur.Y + cfeeder.panY; - ccur.moveY( a ); - if( y == ( ccur.Y + cfeeder.panY ) ) - { - if( 0 < a && !cfeeder.EOF ) return; - beep(); - } - }; - var ActionHandled = true; - // Action Commands + var ccur = this.__ccur; + + // Action Command switch( kCode ) { case SHIFT + A: // Append at the line end ccur.lineEnd(); case A: // Append - cMoveX( 1, true, true ); + this.__cMoveX( 1, true, true ); case I: // Insert ccur.openAction( "INSERT" ); break; @@ -177,22 +111,67 @@ case SHIFT + K: // Find the manual entry break; + case V: // Visual + ccur.openAction( "VISUAL" ); + break; + case SHIFT + V: // Visual line + ccur.openAction( "VISUAL_LINE" ); + break; + case F1: // F1, help break; default: ActionHandled = false; } - if( ActionHandled ) return; + return ActionHandled; + }; - // Cursor Commands + Controls.prototype.__cMoveX = function( a, b, c ) + { + var ccur = this.__ccur; + + var x = ccur.X; + ccur.moveX( a, b, c ); + if( ccur.X == x ) beep(); + }; + + Controls.prototype.__cMoveY = function( a ) + { + var ccur = this.__ccur; + var cfeeder = this.__cfeeder; + + var y = ccur.Y + cfeeder.panY; + ccur.moveY( a ); + if( y == ( ccur.Y + cfeeder.panY ) ) + { + if( 0 < a && !cfeeder.EOF ) return; + beep(); + } + }; + + Controls.prototype.__cursorCommand = function( e, kCode ) + { + if( this.__cMovement && this.__comp ) + { + var k = e.keyCode; + if(!( k == KEY_SHIFT || k == KEY_CTRL || k == KEY_ALT )) + { + this.__comp( kCode ); + return true; + } + } + + var ccur = this.__ccur; + + var cursorHandled = true; switch( kCode ) { - case BACKSPACE: cMoveX( -1, true ); break; // Backspace, go back 1 char, regardless of line - case H: cMoveX( -1 ); break; // Left - case L: cMoveX( 1 ); break; // Right - case K: cMoveY( -1 ); break; // Up - case J: cMoveY( 1 ); break; // Down + case BACKSPACE: this.__cMoveX( -1, true ); break; // Backspace, go back 1 char, regardless of line + case H: this.__cMoveX( -1 ); break; // Left + case L: this.__cMoveX( 1 ); break; // Right + case K: this.__cMoveY( -1 ); break; // Up + case J: this.__cMoveY( 1 ); break; // Down case SHIFT + H: // First line buffer break; @@ -207,24 +186,78 @@ case SHIFT + G: // Goto last line ccur.moveY( Number.MAX_VALUE ); ccur.moveX( Number.MAX_VALUE, true ); - break; + break case SHIFT + _5: // %, Find next item break; + + case G: // Go to top + this.__cMovement = true; + this.__comp( kCode, function(){ + ccur.moveY( -Number.MAX_VALUE ); + ccur.moveX( -Number.MAX_VALUE, true ); + }, G ); + this.__comp( kCode, function(){ + ccur.openRunAction( "PRINT_HEX", e ); + }, _8 ); + break; + + default: + cursorHandled = false; } + return cursorHandled; + }; - // Integrated commands - switch( kCode ) + Controls.prototype.handler = function( sender, e ) + { + // Neve capture these keys + if( e.altKey + // F2 - F12 + || ( F1 < e.keyCode && e.keyCode < 124 ) + ) return; + + // Esc OR Ctrl + c + var Escape = e.keyCode == ESC || ( e.ctrlKey && e.keyCode == C ); + + // Clear composite command + if( Escape && this.__compReg ) { - case V: // Visual - ccur.openAction( "VISUAL" ); - break; - case SHIFT + V: // Visual line - ccur.openAction( "VISUAL_LINE" ); - break; + this.__compReg = null; + this.__cMovement = false; + beep(); + return; } + var cfeeder = this.__cfeeder; + var ccur = this.__ccur; + + var kCode = e.keyCode + + ( e.shiftKey || e.getModifierState( "CapsLock" ) ? SHIFT : 0 ) + + ( e.ctrlKey ? CTRL : 0 ); + + // Action commands are handled by the actions themselves + if( ccur.action ) + { + if( Escape ) + { + e.preventDefault(); + ccur.closeAction(); + } + else + { + if( ccur.action.allowMovement ) + this.__cursorCommand( e, kCode ); + + ccur.action.handler( e ); + } + return; + } + + e.preventDefault(); + + if( this.__cursorCommand( e, kCode ) ) return; + if( this.__actionCommand( e, kCode ) ) return; }; ns[ NS_EXPORT ]( EX_CLASS, "Controls", Controls ); diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index f4b6d9b..51377a5 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -71,12 +71,15 @@ this.Y = 0; // The resulting position - this.P = 0; + this.PStart = 0; + this.PEnd = 1; // State recorder this.rec = new Recorder(); this.action = null; + + this.blink = true; }; // Can only be 1, -1 @@ -147,7 +150,11 @@ Cursor.prototype.updatePosition = function() { - this.P = this.X + LineOffset( this.feeder.lineBuffers, this.Y ); + var P = this.X + LineOffset( this.feeder.lineBuffers, this.Y ); + this.PStart = P; + this.PEnd = P + 1; + this.__p = P; + this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); }; @@ -383,8 +390,8 @@ __readOnly( Cursor.prototype, "position", function() { return { - start: this.P - , end: this.P + 1 + start: this.PStart + , end: this.PEnd }; } ); diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index 8452fab..8ecb7a9 100644 --- a/botanjs/src/Components/Vim/VimArea.js +++ b/botanjs/src/Components/Vim/VimArea.js @@ -53,12 +53,6 @@ var _self = this; - var controls = new VimControls( this ); - stage.addEventListener( - "KeyDown" - , KeyHandler( this, controls.handler.bind( controls ) ) - ); - stage.addEventListener( "Focus", function() { _self.__active = true; } ); stage.addEventListener( "Blur", function() { _self.__active = false; } ); @@ -141,11 +135,17 @@ Cycle.perma( "VimCursorBlinkCycle" + element.id, function() { _self.select( - ( _self.__blink = !_self.__blink ) + !_self.__cursor.blink || ( _self.__blink = !_self.__blink ) ? _self.__cursor.position : { start: 0, end: 0 } ); }, 600 ); + + var controls = new VimControls( this ); + this.stage.addEventListener( + "KeyDown" + , KeyHandler( this, controls.handler.bind( controls ) ) + ); }; __readOnly( VimArea.prototype, "content", function() { diff --git a/botanjs/src/externs/Components.Vim.Cursor.js b/botanjs/src/externs/Components.Vim.Cursor.js index ec39afc..0873383 100644 --- a/botanjs/src/externs/Components.Vim.Cursor.js +++ b/botanjs/src/externs/Components.Vim.Cursor.js @@ -25,12 +25,16 @@ Components.Vim.Cursor.openRunAction; /** @type Function */ Components.Vim.Cursor.closeAction; +/** @type {Boolean} */ +Components.Vim.Cursor.blink; /** @type {Array} */ Components.Vim.Cursor.lineBuffers; /** @type Number */ Components.Vim.Cursor.pX; /** @type Number */ -Components.Vim.Cursor.P; +Components.Vim.Cursor.PStart; +/** @type Number */ +Components.Vim.Cursor.PEnd; /** @type Number */ Components.Vim.Cursor.aX; /** @type Number */ diff --git a/botanjs/src/externs/Components.Vim.IAction.js b/botanjs/src/externs/Components.Vim.IAction.js index db82c97..22571a7 100644 --- a/botanjs/src/externs/Components.Vim.IAction.js +++ b/botanjs/src/externs/Components.Vim.IAction.js @@ -7,3 +7,6 @@ Components.Vim.IAction.dispose; Components.Vim.IAction.handler; /** @type Function */ Components.Vim.IAction.getMessage; + +/** @type Boolean */ +Components.Vim.IAction.allowMovement; From 605ac1e95a48a24478c0b64eab1daa6eb5784885 Mon Sep 17 00:00:00 2001 From: tgckpg Date: Tue, 22 Mar 2016 02:55:29 +0800 Subject: [PATCH 06/11] Update README.md --- README.md | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a8ca0c..649663e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,52 @@ # BotanJS-vim -A textarea powered by vim controls in BotanJS +This is a functional vim using the *screen buffer* approach. What I am trying to do is to achieve that smooth-vim-feeling as close as possible to the original Vim in a general terminal. Visit the demo over [here](https://tgckpg.github.io/BotanJS-vim) + +## This is still working in progress +The below commands are currently working. ( will be updated if I remembered, might not be synced with the demo ) +``` +Cursor movements +hjkl +G + +INSERT: +aA i + +commands that starts with g: +gg +g8 + +VISUAL: +v ( hightlight only ) + +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 + +``` + +### How it works +By *screen buffer*, it means that the textarea is treated as a screen buffer. You are not directly interacting with the textarea. Instead you type into the script, then the result is *rendered* through the textarea. + +### Why use screen buffer? +By treating the textarea as a *screen*. I could archive almost everything except for coloring. And it is easier to precisely track the cursor this way. + +Visit this [blog](https://blog.astropenguin.net/article/view/vimarea-day-1-hjkl/) entries for more information. + +### How the source code works +This is based on a framework I wrote called BotanJS. Which is a frontend framework & Service API that is so big that I am too busy ( lazy ) to explain. If you are only interested in the Vim itself only. It is recommended to download the compiled source code provided in the demo site above. + +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? + +### Use wasavi if you can +#### [wasavi! wasavi! wasavi!](https://github.com/akahuku/wasavi) +[wasavi](https://github.com/akahuku/wasavi) is so good! This is a must-have plugin in Google Chrome. If you haven't heard of that yet seriously go use it now! + +I tried porting it into the browser tho. But I am too stupid to do that. But still I highly recommend using it. + +### How can I contribute? +First, you need to understand the framework behind it. Then file a merge request. Sounds easy right?... prepare to DIEEE! From 75662e6d0426339b1eff5d1499ca163761840b40 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: Tue, 22 Mar 2016 17:35:49 +0800 Subject: [PATCH 07/11] Some placeholder commands in VISUAL --- botanjs/src/Components/Vim/Actions/DELETE.js | 32 +++++++ botanjs/src/Components/Vim/Actions/VISUAL.js | 44 +++++++-- botanjs/src/Components/Vim/Actions/YANK.js | 32 +++++++ botanjs/src/Components/Vim/Controls.js | 96 ++++++++++++++++++- botanjs/src/Components/Vim/Cursor.js | 8 +- botanjs/src/Components/Vim/State/Registers.js | 50 ++++++++++ botanjs/src/Components/Vim/VimArea.js | 34 ++++--- .../src/externs/Components.Vim.Controls.js | 7 ++ botanjs/src/externs/Components.Vim.Cursor.js | 2 + .../externs/Components.Vim.State.Recorder.js | 6 +- .../externs/Components.Vim.State.Registers.js | 9 ++ botanjs/src/externs/Components.Vim.VimArea.js | 2 + 12 files changed, 296 insertions(+), 26 deletions(-) create mode 100644 botanjs/src/Components/Vim/Actions/DELETE.js create mode 100644 botanjs/src/Components/Vim/Actions/YANK.js create mode 100644 botanjs/src/Components/Vim/State/Registers.js create mode 100644 botanjs/src/externs/Components.Vim.Controls.js create mode 100644 botanjs/src/externs/Components.Vim.State.Registers.js diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js new file mode 100644 index 0000000..113a417 --- /dev/null +++ b/botanjs/src/Components/Vim/Actions/DELETE.js @@ -0,0 +1,32 @@ +(function(){ + var ns = __namespace( "Components.Vim.Actions" ); + + var Mesg = __import( "Components.Vim.Message" ); + + /** @type {Components.Vim.Cursor.IAction} */ + var DELETE = function( Cursor ) + { + /** @type {Components.Vim.Cursor} */ + this.__cursor = Cursor; + }; + + DELETE.prototype.allowMovement = true; + + DELETE.prototype.dispose = function() + { + + }; + + DELETE.prototype.handler = function( e ) + { + e.preventDefault(); + + }; + + DELETE.prototype.getMessage = function() + { + return " DELETE COMMAND"; + }; + + ns[ NS_EXPORT ]( EX_CLASS, "DELETE", DELETE ); +})(); diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js index 752eaec..dad2fbb 100644 --- a/botanjs/src/Components/Vim/Actions/VISUAL.js +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -1,12 +1,17 @@ (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" ); + var debug = __import( "System.Debug" ); var Mesg = __import( "Components.Vim.Message" ); + /** @type {Components.Vim.Controls} */ + var Controls = __import( "Components.Vim.Controls" ); + + /** @type {Components.Vim.Cursor.IAction} */ + var YANK = ns[ NS_INVOKE ]( "YANK" ); + /** @type {Components.Vim.Cursor.IAction} */ + var DELETE = ns[ NS_INVOKE ]( "DELETE" ); /** @type {Components.Vim.Cursor.IAction} */ var VISUAL = function( Cursor ) @@ -16,6 +21,8 @@ this.__startaP = Cursor.aPos; this.__start = Cursor.PStart; this.__selStart = Cursor.PStart; + this.__msg = Mesg( "VISUAL" ); + this.__leaveMesg = ""; Cursor.blink = false; }; @@ -24,17 +31,38 @@ VISUAL.prototype.dispose = function() { + this.__msg = this.__leaveMesg; this.__cursor.blink = true; this.__cursor.PStart = this.__selStart; this.__cursor.PEnd = this.__selStart + 1; }; - - VISUAL.prototype.handler = function( e ) + VISUAL.prototype.handler = function( e, done ) { e.preventDefault(); - if( [ 16, 17, 18 ].indexOf( e.keyCode ) != -1 ) return; + if( Controls.ModKeys( e ) ) return; + + var Action = null; + switch( true ) + { + case Controls.KMap( e, "y" ): + Action = new YANK( this.__cursor ); + break; + case Controls.KMap( e, "d" ): + Action = new DELETE( this.__cursor ); + break; + } + + if( Action ) + { + Action.handler( e ); + this.__leaveMesg = Action.getMessage(); + Action.dispose(); + + return true; + } + var cur = this.__cursor; var prevPos = this.__start; var newPos = cur.PStart; @@ -58,9 +86,7 @@ VISUAL.prototype.getMessage = function() { - var msg = Mesg( "VISUAL" ); - - return msg; + return this.__msg; }; ns[ NS_EXPORT ]( EX_CLASS, "VISUAL", VISUAL ); diff --git a/botanjs/src/Components/Vim/Actions/YANK.js b/botanjs/src/Components/Vim/Actions/YANK.js new file mode 100644 index 0000000..ba987d7 --- /dev/null +++ b/botanjs/src/Components/Vim/Actions/YANK.js @@ -0,0 +1,32 @@ +(function(){ + var ns = __namespace( "Components.Vim.Actions" ); + + var Mesg = __import( "Components.Vim.Message" ); + + /** @type {Components.Vim.Cursor.IAction} */ + var YANK = function( Cursor ) + { + /** @type {Components.Vim.Cursor} */ + this.__cursor = Cursor; + }; + + YANK.prototype.allowMovement = true; + + YANK.prototype.dispose = function() + { + + }; + + YANK.prototype.handler = function( e ) + { + e.preventDefault(); + + }; + + YANK.prototype.getMessage = function() + { + return " YANK COMMAND"; + }; + + ns[ NS_EXPORT ]( EX_CLASS, "YANK", YANK ); +})(); diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index f55181b..918cc41 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -1,6 +1,7 @@ (function(){ var ns = __namespace( "Components.Vim" ); + /** @type {System.Debug} */ var debug = __import( "System.Debug" ); var beep = ns[ NS_INVOKE ]( "Beep" ); @@ -29,6 +30,81 @@ var F6 = 117; var F7 = 118; var F8 = 119; var F9 = 120; var F10 = 121; var F11 = 122; var F12 = 123; + var __maps = {}; + var Map = function( str ) + { + if( __maps[ str ] ) return __maps[ str ]; + + // C-Left, A-Up ... + var Code = str.split( "-" ); + var sCode = Code[0]; + + var Mod = 0; + if( Code.length == 2 ) + { + var m = true; + switch( Code[0] ) + { + case "C": Mod = CTRL; break; + case "A": Mod = ALT; break; + case "S": Mod = SHIFT; break; + default: + m = false; + } + + if( m ) + { + sCode = Code[1]; + } + } + + var kCode; + switch( sCode ) + { + case "A": Mod = SHIFT; case "a": kCode = Mod + A; break; + case "B": Mod = SHIFT; case "b": kCode = Mod + B; break; + case "C": Mod = SHIFT; case "c": kCode = Mod + C; break; + case "D": Mod = SHIFT; case "d": kCode = Mod + D; break; + case "E": Mod = SHIFT; case "e": kCode = Mod + E; break; + case "F": Mod = SHIFT; case "f": kCode = Mod + F; break; + case "G": Mod = SHIFT; case "g": kCode = Mod + G; break; + case "H": Mod = SHIFT; case "h": kCode = Mod + H; break; + case "I": Mod = SHIFT; case "i": kCode = Mod + I; break; + case "J": Mod = SHIFT; case "j": kCode = Mod + J; break; + case "K": Mod = SHIFT; case "k": kCode = Mod + K; break; + case "L": Mod = SHIFT; case "l": kCode = Mod + L; break; + case "M": Mod = SHIFT; case "m": kCode = Mod + M; break; + case "N": Mod = SHIFT; case "n": kCode = Mod + N; break; + case "O": Mod = SHIFT; case "o": kCode = Mod + O; break; + case "P": Mod = SHIFT; case "p": kCode = Mod + P; break; + case "Q": Mod = SHIFT; case "q": kCode = Mod + Q; break; + case "R": Mod = SHIFT; case "r": kCode = Mod + R; break; + case "S": Mod = SHIFT; case "s": kCode = Mod + S; break; + case "T": Mod = SHIFT; case "t": kCode = Mod + T; break; + case "U": Mod = SHIFT; case "u": kCode = Mod + U; break; + case "V": Mod = SHIFT; case "v": kCode = Mod + V; break; + case "W": Mod = SHIFT; case "w": kCode = Mod + W; break; + case "X": Mod = SHIFT; case "x": kCode = Mod + X; break; + case "Y": Mod = SHIFT; case "y": kCode = Mod + Y; break; + case "Z": Mod = SHIFT; case "z": kCode = Mod + Z; break; + + case "!": Mod = SHIFT; case "1": kCode = Mod + _1; break; + case "@": Mod = SHIFT; case "2": kCode = Mod + _2; break; + case "#": Mod = SHIFT; case "3": kCode = Mod + _3; break; + case "$": Mod = SHIFT; case "4": kCode = Mod + _4; break; + case "%": Mod = SHIFT; case "5": kCode = Mod + _5; break; + case "^": Mod = SHIFT; case "6": kCode = Mod + _6; break; + case "&": Mod = SHIFT; case "7": kCode = Mod + _7; break; + case "*": Mod = SHIFT; case "8": kCode = Mod + _8; break; + case "(": Mod = SHIFT; case "9": kCode = Mod + _9; break; + case ")": Mod = SHIFT; case "0": kCode = Mod + _0; break; + default: + throw new Error( "No such keys: " + str ); + } + + return __maps[ str ] = kCode; + }; + var Controls = function( vimArea ) { /** @type {Components.Vim.VimArea} */ @@ -249,7 +325,10 @@ if( ccur.action.allowMovement ) this.__cursorCommand( e, kCode ); - ccur.action.handler( e ); + if( ccur.action.handler( e ) ) + { + ccur.closeAction(); + } } return; } @@ -260,5 +339,20 @@ if( this.__actionCommand( e, kCode ) ) return; }; + __static_method( Controls, "ModKeys", function( e ) + { + var c = e.keyCode; + return c == KEY_SHIFT || c == KEY_CTRL || c == KEY_ALT; + } ); + + __static_method( Controls, "KMap", function( e, map ) + { + var kCode = e.keyCode + + ( e.shiftKey || e.getModifierState( "CapsLock" ) ? SHIFT : 0 ) + + ( e.ctrlKey ? CTRL : 0 ); + + return kCode == Map( map ); + } ); + ns[ NS_EXPORT ]( EX_CLASS, "Controls", Controls ); })(); diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index 51377a5..0481f30 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -82,6 +82,9 @@ this.blink = true; }; + // Set by VimArea + Cursor.prototype.Vim; + // Can only be 1, -1 // 0 will be treated as undefined Cursor.prototype.moveX = function( d, penentrate, phantomSpace ) @@ -259,6 +262,8 @@ this.updatePosition(); }; + // Open an action handler + // i.e. YANK, VISUAL, INSERT, UNDO, etc. Cursor.prototype.openAction = function( name ) { if( this.action ) this.action.dispose(); @@ -272,12 +277,13 @@ { if( !this.action ) return; this.action.dispose(); + this.__pulseMsg = this.action.getMessage(); this.action = null; - this.__pulseMsg = null; this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); }; + // Open, Run, then close an action Cursor.prototype.openRunAction = function( name, e ) { /** @type {Components.Vim.IAction} */ diff --git a/botanjs/src/Components/Vim/State/Registers.js b/botanjs/src/Components/Vim/State/Registers.js new file mode 100644 index 0000000..220fe70 --- /dev/null +++ b/botanjs/src/Components/Vim/State/Registers.js @@ -0,0 +1,50 @@ +(function(){ +/* From Vim, :help registers + There are ten types of registers: + 1. The unnamed register "" + 2. 10 numbered registers "0 to "9 + 3. The small delete register "- + 4. 26 named registers "a to "z or "A to "Z + 5. three read-only registers ":, "., "% + 6. alternate buffer register "# + 7. the expression register "= + 8. The selection and drop registers "*, "+ and "~ + 9. The black hole register "_ + 10. Last search pattern register "/ + i.e. 0123456789-abcdefghijklmnopqrstuvwxyz:.%$=*+~_/ +*/ + var ns = __namespace( "Components.Vim.State" ); + + var Registers = function() + { + this.__registers = {}; + }; + + Registers.prototype.unnamed = function( str ) + { + this.__registers[ "\"" ] = str; + }; + + Registers.prototype.yank = function( str ) + { + this.unnamed( str ); + this.__registers[ 0 ] = str; + }; + + Registers.prototype.change = function( str ) + { + this.unnamed( str ); + var r = this.__registers; + for( var i = 9; 1 < i; i -- ) + { + if( r[ i - 1 ] != undefined ) + { + r[ i ] = r[ i - 1 ]; + } + } + + r[ 1 ] = str; + }; + + ns[ NS_EXPORT ]( EX_CLASS, "Registers", Registers ); +})(); diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index 8ecb7a9..d02bdf4 100644 --- a/botanjs/src/Components/Vim/VimArea.js +++ b/botanjs/src/Components/Vim/VimArea.js @@ -10,6 +10,9 @@ /** @type {System.Debug} */ var debug = __import( "System.Debug" ); + /** @type {Components.Vim.State.Registers} */ + var Registers = __import( "Components.Vim.State.Registers" ); + /** @type {Components.Vim.LineFeeder} */ var LineFeeder = ns[ NS_INVOKE ]( "LineFeeder" ); /** @type {Components.Vim.StatusBar} */ @@ -18,6 +21,8 @@ var VimControls = ns[ NS_INVOKE ]( "Controls" ); var mesg = ns[ NS_INVOKE ]( "Message" ); + var Insts = []; + var KeyHandler = function( sender, handler ) { return function( e ) @@ -58,6 +63,9 @@ // Init this.VisualizeVimFrame( element.value ); + + // Push this instance + Insts.push( this ); }; VimArea.prototype.select = function( sel ) @@ -86,6 +94,7 @@ // Content feeder var cfeeder = new LineFeeder( cRange, c ); + // Feed the contents to content feeder // This "\n" fixes the last line "\n" not displaying // it will be trimmed after saving cfeeder.init( content + "\n" ); @@ -94,19 +103,15 @@ sfeeder = new LineFeeder( r, c ); sfeeder.setRender( false ); - // XXX: Placeholder + // Set the Vim instance + cfeeder.cursor.Vim = this; + sfeeder.cursor.Vim = this; + + // Set the stamps var statusBar = new StatusBar( c ); - statusBar.stamp( -18, function(){ - return cfeeder.lineStat; - }); - - statusBar.stamp( -3, function(){ - return mesg( cfeeder.docPos ); - } ); - - statusBar.stamp( 0, function(){ - return cfeeder.cursor.message; - } ); + statusBar.stamp( -18, function(){ return cfeeder.lineStat; } ); + statusBar.stamp( -3, function(){ return mesg( cfeeder.docPos ); } ); + statusBar.stamp( 0, function(){ return cfeeder.cursor.message; } ); sfeeder.init( statusBar.statusText ); @@ -128,6 +133,7 @@ this.contentFeeder = cfeeder; this.statusFeeder = sfeeder; this.statusBar = statusBar; + this.registers = new Registers(); this.__cursor = cfeeder.cursor; @@ -148,6 +154,10 @@ ); }; + __readOnly( VimArea, "Instances", function() { + return Insts.slice(); + } ); + __readOnly( VimArea.prototype, "content", function() { return this.contentFeeder.content.slice( 0, -1 ); } ); diff --git a/botanjs/src/externs/Components.Vim.Controls.js b/botanjs/src/externs/Components.Vim.Controls.js new file mode 100644 index 0000000..6fec2e7 --- /dev/null +++ b/botanjs/src/externs/Components.Vim.Controls.js @@ -0,0 +1,7 @@ +/** @constructor */ +Components.Vim.Controls = function(){}; + +/** @type Function */ +Components.Vim.Controls.ModKeys; +/** @type Function */ +Components.Vim.Controls.KMap; diff --git a/botanjs/src/externs/Components.Vim.Cursor.js b/botanjs/src/externs/Components.Vim.Cursor.js index 0873383..76fef9d 100644 --- a/botanjs/src/externs/Components.Vim.Cursor.js +++ b/botanjs/src/externs/Components.Vim.Cursor.js @@ -1,6 +1,8 @@ /** @constructor */ Components.Vim.Cursor = function(){}; +/** @type {Components.Vim.VimArea} */ +Components.Vim.Cursor.Vim; /** @type {Components.Vim.LineFeeder} */ Components.Vim.Cursor.feeder; /** @type {Components.Vim.IAction} */ diff --git a/botanjs/src/externs/Components.Vim.State.Recorder.js b/botanjs/src/externs/Components.Vim.State.Recorder.js index 0303938..b39cbf9 100644 --- a/botanjs/src/externs/Components.Vim.State.Recorder.js +++ b/botanjs/src/externs/Components.Vim.State.Recorder.js @@ -2,8 +2,8 @@ Components.Vim.State.Recorder = function(){}; /** @type Function */ -Components.Vim.State.undo; +Components.Vim.State.Recorder.undo; /** @type Function */ -Components.Vim.State.redo; +Components.Vim.State.Recorder.redo; /** @type Function */ -Components.Vim.State.record; +Components.Vim.State.Recorder.record; diff --git a/botanjs/src/externs/Components.Vim.State.Registers.js b/botanjs/src/externs/Components.Vim.State.Registers.js new file mode 100644 index 0000000..75b9c13 --- /dev/null +++ b/botanjs/src/externs/Components.Vim.State.Registers.js @@ -0,0 +1,9 @@ +/** @constructor */ +Components.Vim.State.Registers = function(){}; + +/** @type Function */ +Components.Vim.State.Registers.change; +/** @type Function */ +Components.Vim.State.Registers.yank; +/** @type Function */ +Components.Vim.State.Registers.unnamed; diff --git a/botanjs/src/externs/Components.Vim.VimArea.js b/botanjs/src/externs/Components.Vim.VimArea.js index e71c020..982acda 100644 --- a/botanjs/src/externs/Components.Vim.VimArea.js +++ b/botanjs/src/externs/Components.Vim.VimArea.js @@ -12,3 +12,5 @@ Components.Vim.VimArea.statusBar; Components.Vim.VimArea.rows; /** @type {Number} */ Components.Vim.VimArea.cols; +/** @type {Array} */ +Components.Vim.VimArea.Instances; From 5f5e0604fa8d7ddba1ef5a0bcf27941819398e35 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: Tue, 22 Mar 2016 19:31:19 +0800 Subject: [PATCH 08/11] Wrapping keyboardevent with InputEvent --- botanjs/src/Components/Vim/Actions/VISUAL.js | 8 +- botanjs/src/Components/Vim/Controls.js | 78 ++++++++++++------- botanjs/src/Components/Vim/VimArea.js | 3 +- .../Components.Vim.Controls.InputEvent.js | 13 ++++ .../src/externs/Components.Vim.Controls.js | 5 -- 5 files changed, 69 insertions(+), 38 deletions(-) create mode 100644 botanjs/src/externs/Components.Vim.Controls.InputEvent.js diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js index dad2fbb..ab9671b 100644 --- a/botanjs/src/Components/Vim/Actions/VISUAL.js +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -5,8 +5,6 @@ var debug = __import( "System.Debug" ); var Mesg = __import( "Components.Vim.Message" ); - /** @type {Components.Vim.Controls} */ - var Controls = __import( "Components.Vim.Controls" ); /** @type {Components.Vim.Cursor.IAction} */ var YANK = ns[ NS_INVOKE ]( "YANK" ); @@ -41,15 +39,15 @@ { e.preventDefault(); - if( Controls.ModKeys( e ) ) return; + if( e.ModKeys ) return; var Action = null; switch( true ) { - case Controls.KMap( e, "y" ): + case e.kMap( "y" ): Action = new YANK( this.__cursor ); break; - case Controls.KMap( e, "d" ): + case e.kMap( "d" ): Action = new DELETE( this.__cursor ); break; } diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 918cc41..ce1e947 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -7,6 +7,7 @@ var SHIFT = 1 << 9; var CTRL = 1 << 10; + var ALT = 1 << 11; var KEY_SHIFT = 16; var KEY_CTRL = 17; @@ -139,7 +140,7 @@ if( compReg.i == keys.length ) { compReg.handler(); - compReg = null; + this.__compReg = null; this.__cMovement = false; } @@ -153,13 +154,13 @@ return false; }; - Controls.prototype.__actionCommand = function( e, kCode ) + Controls.prototype.__actionCommand = function( e ) { var ActionHandled = true; var ccur = this.__ccur; // Action Command - switch( kCode ) + switch( e.keyCode ) { case SHIFT + A: // Append at the line end ccur.lineEnd(); @@ -226,12 +227,13 @@ } }; - Controls.prototype.__cursorCommand = function( e, kCode ) + Controls.prototype.__cursorCommand = function( e ) { + var kCode = e.keyCode; + if( this.__cMovement && this.__comp ) { - var k = e.keyCode; - if(!( k == KEY_SHIFT || k == KEY_CTRL || k == KEY_ALT )) + if( !e.ModKeys ) { this.__comp( kCode ); return true; @@ -285,19 +287,20 @@ return cursorHandled; }; + /** + * sender @param {Components.Vim.VimArea} + * e @param {Components.Vim.Controls.InputEvent} + * */ Controls.prototype.handler = function( sender, e ) { // Neve capture these keys - if( e.altKey + if( e.ModKeys // F2 - F12 || ( F1 < e.keyCode && e.keyCode < 124 ) ) return; - // Esc OR Ctrl + c - var Escape = e.keyCode == ESC || ( e.ctrlKey && e.keyCode == C ); - // Clear composite command - if( Escape && this.__compReg ) + if( e.Escape && this.__compReg ) { this.__compReg = null; this.__cMovement = false; @@ -308,14 +311,12 @@ var cfeeder = this.__cfeeder; var ccur = this.__ccur; - var kCode = e.keyCode - + ( e.shiftKey || e.getModifierState( "CapsLock" ) ? SHIFT : 0 ) - + ( e.ctrlKey ? CTRL : 0 ); + var kCode = e.keyCode; // Action commands are handled by the actions themselves if( ccur.action ) { - if( Escape ) + if( e.Escape ) { e.preventDefault(); ccur.closeAction(); @@ -333,26 +334,49 @@ return; } - e.preventDefault(); + if( this.__cursorCommand( e ) ) + { + e.preventDefault(); + return; + } - if( this.__cursorCommand( e, kCode ) ) return; - if( this.__actionCommand( e, kCode ) ) return; + if( this.__actionCommand( e ) ) + { + e.preventDefault(); + return; + } }; - __static_method( Controls, "ModKeys", function( e ) + var InputEvent = function( e ) { - var c = e.keyCode; - return c == KEY_SHIFT || c == KEY_CTRL || c == KEY_ALT; - } ); + this.__e = e; - __static_method( Controls, "KMap", function( e, map ) - { - var kCode = e.keyCode + var c = this.__e.keyCode; + + this.__escape = c == ESC || ( e.ctrlKey && c == C ); + this.__kCode = c + ( e.shiftKey || e.getModifierState( "CapsLock" ) ? SHIFT : 0 ) + ( e.ctrlKey ? CTRL : 0 ); - return kCode == Map( map ); - } ); + this.__modKeys = c == KEY_SHIFT || c == KEY_CTRL || c == KEY_ALT; + this.__key = e.key; + }; + + __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; } ); + + InputEvent.prototype.kMap = function( map ) + { + return this.__kCode == Map( map ); + }; + + InputEvent.prototype.preventDefault = function() + { + if( this.__e ) this.__e.preventDefault(); + }; ns[ NS_EXPORT ]( EX_CLASS, "Controls", Controls ); + ns[ NS_EXPORT ]( EX_CLASS, "InputEvent", InputEvent ); })(); diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index d02bdf4..368cecb 100644 --- a/botanjs/src/Components/Vim/VimArea.js +++ b/botanjs/src/Components/Vim/VimArea.js @@ -19,6 +19,7 @@ var StatusBar = ns[ NS_INVOKE ]( "StatusBar" ); var VimControls = ns[ NS_INVOKE ]( "Controls" ); + var InputEvent = ns[ NS_INVOKE ]( "InputEvent" ); var mesg = ns[ NS_INVOKE ]( "Message" ); var Insts = []; @@ -31,7 +32,7 @@ if ( e.keyCode ) code = e.keyCode; else if ( e.which ) code = e.which; - handler( sender, e ); + handler( sender, new InputEvent( e ) ); }; }; diff --git a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js new file mode 100644 index 0000000..ffd085e --- /dev/null +++ b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js @@ -0,0 +1,13 @@ +/** @constructor */ +Components.Vim.Controls.InputEvent = function(){}; + +/** @type String */ +Components.Vim.Controls.InputEvent.key; +/** @type Boolean */ +Components.Vim.Controls.InputEvent.ModKeys; +/** @type Boolean */ +Components.Vim.Controls.InputEvent.Escape; +/** @type Number */ +Components.Vim.Controls.InputEvent.keyCode; +/** @type Function */ +Components.Vim.Controls.InputEvent.kMap; diff --git a/botanjs/src/externs/Components.Vim.Controls.js b/botanjs/src/externs/Components.Vim.Controls.js index 6fec2e7..d87960f 100644 --- a/botanjs/src/externs/Components.Vim.Controls.js +++ b/botanjs/src/externs/Components.Vim.Controls.js @@ -1,7 +1,2 @@ /** @constructor */ Components.Vim.Controls = function(){}; - -/** @type Function */ -Components.Vim.Controls.ModKeys; -/** @type Function */ -Components.Vim.Controls.KMap; From 78f8a20a5d77436720a8931480fda21b020a9218 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: Wed, 23 Mar 2016 03:15:02 +0800 Subject: [PATCH 09/11] DELETE command in VISUAL, generalized Stator Obj --- botanjs/src/Components/Vim/Actions/DELETE.js | 65 ++++++++- botanjs/src/Components/Vim/Actions/INSERT.js | 123 +++++------------- botanjs/src/Components/Vim/Actions/VISUAL.js | 29 +++-- botanjs/src/Components/Vim/Controls.js | 47 ++++--- botanjs/src/Components/Vim/Cursor.js | 28 +++- botanjs/src/Components/Vim/State/Stator.js | 71 ++++++++++ botanjs/src/Components/Vim/VimArea.js | 2 +- botanjs/src/Components/Vim/_this.js | 2 + .../Components.Vim.Controls.InputEvent.js | 2 + botanjs/src/externs/Components.Vim.Cursor.js | 6 + 10 files changed, 241 insertions(+), 134 deletions(-) create mode 100644 botanjs/src/Components/Vim/State/Stator.js diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js index 113a417..c42b2ef 100644 --- a/botanjs/src/Components/Vim/Actions/DELETE.js +++ b/botanjs/src/Components/Vim/Actions/DELETE.js @@ -1,13 +1,24 @@ (function(){ var ns = __namespace( "Components.Vim.Actions" ); + /** @type {System.Debug} */ + var debug = __import( "System.Debug" ); + + /** @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 DELETE = function( Cursor ) { /** @type {Components.Vim.Cursor} */ this.__cursor = Cursor; + this.__nline = 0; }; DELETE.prototype.allowMovement = true; @@ -17,15 +28,65 @@ }; - DELETE.prototype.handler = function( e ) + DELETE.prototype.handler = function( e, sp ) { e.preventDefault(); + /** @type {Components.Vim.State.Registers} */ + var reg = e.target.registers; + + var cur = this.__cursor; + var feeder = cur.feeder; + + var c = feeder.content; + + var s = sp; + var e = cur.aPos; + + if( e < s ) + { + s = cur.aPos; + e = sp; + } + + var removed = c.substring( s, e + 1 ); + reg.change( removed ); + + this.__nline = occurence( removed, "\n" ); + + feeder.content = c.substring( 0, s ) + c.substring( e + 1 ); + + var stator = new Stator( cur, s ); + var stack = new Stack(); + + c = c[ e + 1 ]; + if( c == "\n" || c == undefined ) + { + cur.suppressEvent(); + cur.moveX( -1 ); + cur.unsuppressEvent(); + } + + var f = stator.save( 0, removed ); + stack.store( function() { + f(); + // Offset correction after REDO / UNDO + cur.moveX( 1 ); + } ); + + cur.rec.record( stack ); + + feeder.pan(); }; DELETE.prototype.getMessage = function() { - return " DELETE COMMAND"; + if( this.__nline ) + { + return Mesg( "LINE_FEWER", this.__nline ); + } + + return ""; }; ns[ NS_EXPORT ]( EX_CLASS, "DELETE", DELETE ); diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js index a199a5f..e67f083 100644 --- a/botanjs/src/Components/Vim/Actions/INSERT.js +++ b/botanjs/src/Components/Vim/Actions/INSERT.js @@ -2,9 +2,11 @@ var ns = __namespace( "Components.Vim.Actions" ); /** @type {Components.Vim.State.Stack} */ - var Stack = __import( "Components.Vim.State.Stack" ); + var Stack = __import( "Components.Vim.State.Stack" ); + /** @type {Components.Vim.State.Stator} */ + var Stator = __import( "Components.Vim.State.Stator" ); /** @type {System.Debug} */ - var debug = __import( "System.Debug" ); + var debug = __import( "System.Debug" ); var Mesg = __import( "Components.Vim.Message" ); @@ -27,7 +29,7 @@ /** @type {Components.Vim.Cursor} */ this.__cursor = Cursor; - this.__startState = this.__saveCur(); + this.__Stator = new Stator( Cursor ); // Initialize this stack this.__rec( "", true ); @@ -35,66 +37,12 @@ INSERT.prototype.allowMovement = false; - INSERT.prototype.__saveCur = function() - { - var c = this.__cursor; - var obj = { - p: c.P - , x: c.X - , y: c.Y - , px: c.feeder.panX - , py: c.feeder.panY - }; - - if( 0 < obj.x ) - { - obj.p -= 1; - obj.x -= 1; - } - - return obj; - } - INSERT.prototype.dispose = function() { this.__cursor.moveX( -1 ); this.__rec( "", true ); }; - INSERT.prototype.__storeState = function() - { - var cur = this.__cursor; - var feeder = cur.feeder; - var insertLength = this.__insertLength; - var contentUndo = this.__contentUndo; - var startPos = this.__startPosition; - var sSt = this.__startState; - var eSt = this.__saveCur(); - - var st = sSt; - // Calling this repeatedly will swap between UNDO / REDO state - return function() { - var contentRedo = feeder.content.substr( startPos, insertLength ); - feeder.content = - feeder.content.substring( 0, startPos ) - + contentUndo - + feeder.content.substring( startPos + insertLength ); - insertLength = contentUndo.length; - contentUndo = contentRedo; - - cur.PStart = st.p; - cur.PEnd = st.p + 1; - cur.X = st.x; - cur.Y = st.y; - feeder.panX = st.px; - feeder.panY = st.py; - - feeder.pan(); - - st = ( st == sSt ) ? eSt : sSt; - }; - }; - INSERT.prototype.__rec = function( c, newRec ) { if( newRec || !this.__stack ) @@ -107,7 +55,7 @@ ) return; this.__stack.store( - this.__storeState() + this.__Stator.save( this.__insertLength, this.__contentUndo ) ); this.__cursor.rec.record( this.__stack ); @@ -116,7 +64,6 @@ this.__insertLength = 0; this.__contentUndo = ""; this.__stack = new Stack(); - this.__startPosition = this.__cursor.aPos; } if( c == "\n" ) @@ -132,45 +79,37 @@ var cur = this.__cursor; var feeder = cur.feeder; - switch( e.keyCode ) + // Backspace + if( e.kMap( "BS" ) ) { - case 8: // Backspace - var oY = feeder.panY + cur.Y; - if( cur.X == 0 && feeder.panY == 0 && cur.Y == 0 ) return; + var oY = feeder.panY + cur.Y; + if( cur.X == 0 && feeder.panY == 0 && cur.Y == 0 ) return; - cur.moveX( -1, true, true ); + cur.moveX( -1, true, true ); - var f = cur.aPos; + var f = cur.aPos; - if( this.__insertLength <= 0 ) - { - this.__contentUndo = feeder.content.substr( f, 1 ) + this.__contentUndo; - this.__startPosition --; - } - else - { - this.__insertLength --; - } + if( this.__insertLength <= 0 ) + { + this.__contentUndo = feeder.content.substr( f, 1 ) + this.__contentUndo; + this.__insertLength --; + } - feeder.content = - feeder.content.substring( 0, f ) - + feeder.content.substring( f + 1 ); - - break; - case 46: // Delete - var f = cur.aPos; - - this.__contentUndo += feeder.content.substr( f, 1 ); - - feeder.content = - feeder.content.substring( 0, f ) - + feeder.content.substring( f + 1 ); - - break; - default: - // Do nothing - return; + feeder.content = + feeder.content.substring( 0, f ) + + feeder.content.substring( f + 1 ); } + else if( e.kMap( "Del" ) ) + { + var f = cur.aPos; + + this.__contentUndo += feeder.content.substr( f, 1 ); + + feeder.content = + feeder.content.substring( 0, f ) + + feeder.content.substring( f + 1 ); + } + else return; feeder.pan(); feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js index ab9671b..659b058 100644 --- a/botanjs/src/Components/Vim/Actions/VISUAL.js +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -23,6 +23,7 @@ this.__leaveMesg = ""; Cursor.blink = false; + Cursor.pSpace = true; }; VISUAL.prototype.allowMovement = true; @@ -31,6 +32,7 @@ { this.__msg = this.__leaveMesg; this.__cursor.blink = true; + this.__cursor.pSpace = false; this.__cursor.PStart = this.__selStart; this.__cursor.PEnd = this.__selStart + 1; }; @@ -41,27 +43,18 @@ if( e.ModKeys ) return; + var cur = this.__cursor; var Action = null; switch( true ) { case e.kMap( "y" ): - Action = new YANK( this.__cursor ); + Action = new YANK( cur ); break; case e.kMap( "d" ): - Action = new DELETE( this.__cursor ); + Action = new DELETE( cur ); break; } - if( Action ) - { - Action.handler( e ); - this.__leaveMesg = Action.getMessage(); - Action.dispose(); - - return true; - } - - var cur = this.__cursor; var prevPos = this.__start; var newPos = cur.PStart; @@ -78,6 +71,18 @@ this.__selStart = prevPos; } + if( Action ) + { + cur.suppressEvent(); + Action.handler( e, this.__startaP ); + this.__leaveMesg = Action.getMessage(); + Action.dispose(); + cur.unsuppressEvent(); + + this.__selStart = cur.PStart; + return true; + } + cur.PStart = prevPos; cur.PEnd = newPos; }; diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index ce1e947..7bcc195 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -14,6 +14,7 @@ var KEY_ALT = 18; var BACKSPACE = 8; + var DELETE = 46; 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; @@ -31,6 +32,8 @@ var F6 = 117; var F7 = 118; var F8 = 119; var F9 = 120; var F10 = 121; var F11 = 122; var F12 = 123; + var COMMA = 188; var FULLSTOP = 190; + var __maps = {}; var Map = function( str ) { @@ -62,6 +65,9 @@ var kCode; switch( sCode ) { + case "BS": kCode = Mod + BACKSPACE; break; + case "Del": kCode = Mod + DELETE; break; + case "A": Mod = SHIFT; case "a": kCode = Mod + A; break; case "B": Mod = SHIFT; case "b": kCode = Mod + B; break; case "C": Mod = SHIFT; case "c": kCode = Mod + C; break; @@ -99,8 +105,11 @@ case "*": Mod = SHIFT; case "8": kCode = Mod + _8; break; case "(": Mod = SHIFT; case "9": kCode = Mod + _9; break; case ")": Mod = SHIFT; case "0": kCode = Mod + _0; break; + case "<": Mod = SHIFT; case ",": kCode = Mod + COMMA; break; + case ">": Mod = SHIFT; case ".": kCode = Mod + FULLSTOP; break; + default: - throw new Error( "No such keys: " + str ); + throw new Error( "Unsupport keys: " + str ); } return __maps[ str ] = kCode; @@ -209,7 +218,7 @@ var ccur = this.__ccur; var x = ccur.X; - ccur.moveX( a, b, c ); + ccur.moveX( a, b, c || ccur.pSpace ); if( ccur.X == x ) beep(); }; @@ -245,7 +254,7 @@ var cursorHandled = true; switch( kCode ) { - case BACKSPACE: this.__cMoveX( -1, true ); break; // Backspace, go back 1 char, regardless of line + case BACKSPACE: this.__cMoveX( -1, true ); break; // Backspace, go back 1 char case H: this.__cMoveX( -1 ); break; // Left case L: this.__cMoveX( 1 ); break; // Right case K: this.__cMoveY( -1 ); break; // Up @@ -259,7 +268,7 @@ ccur.lineStart(); break; case SHIFT + _4: // $, End - ccur.lineEnd(); + ccur.lineEnd( ccur.pSpace ); break; case SHIFT + G: // Goto last line ccur.moveY( Number.MAX_VALUE ); @@ -293,10 +302,10 @@ * */ Controls.prototype.handler = function( sender, e ) { - // Neve capture these keys - if( e.ModKeys + // Never capture these keys + if( e.keyCode == ( ALT + D ) // F2 - F12 - || ( F1 < e.keyCode && e.keyCode < 124 ) + || ( F1 < e.keyCode && e.keyCode <= F12 ) ) return; // Clear composite command @@ -334,36 +343,32 @@ return; } - if( this.__cursorCommand( e ) ) - { - e.preventDefault(); - return; - } + e.preventDefault(); - if( this.__actionCommand( e ) ) - { - e.preventDefault(); - return; - } + if( this.__cursorCommand( e ) ) return; + if( this.__actionCommand( e ) ) return; }; - var InputEvent = function( e ) + var InputEvent = function( sender, e ) { this.__e = e; + this.__target = sender; var c = this.__e.keyCode; this.__escape = c == ESC || ( e.ctrlKey && c == C ); this.__kCode = c + ( e.shiftKey || e.getModifierState( "CapsLock" ) ? SHIFT : 0 ) - + ( e.ctrlKey ? CTRL : 0 ); + + ( e.ctrlKey ? CTRL : 0 ) + + ( e.altKey ? ALT : 0 ); this.__modKeys = c == KEY_SHIFT || c == KEY_CTRL || c == KEY_ALT; this.__key = e.key; }; - __readOnly( InputEvent.prototype, "key", function() { return this.__key; } ); - __readOnly( InputEvent.prototype, "keyCode", function() { return this.__kCode; } ); + __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; } ); diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index 0481f30..5f99f5d 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -80,6 +80,9 @@ this.action = null; this.blink = true; + this.pSpace = false; + + this.__suppEvt = 0; }; // Set by VimArea @@ -118,12 +121,12 @@ // Include empty lines befor cursor end if( ( phantomSpace && cLen - 1 <= x ) || ( cLen == 1 && c == undefined ) ) { - x = d > 0 ? cLen - 1 : 0; + x = 0 < d ? cLen - 1 : 0; } // ( 2 < cLen ) Exclude empty lines at cursor end - else if( ( 2 < cLen && x == cLen - 1 && c == " " ) || c == undefined ) + else if( ( 2 <= cLen && x == cLen - 1 && c == " " ) || c == undefined ) { - x = d > 0 ? cLen - 2 : 0; + x = 0 < d ? cLen - 2 : 0; } else if( c == "\n" ) { @@ -158,6 +161,16 @@ this.PEnd = P + 1; this.__p = P; + this.__fireUpdate(); + }; + + Cursor.prototype.__fireUpdate = function() + { + if( 0 < this.__suppEvt ) + { + debug.Info( "Event suppressed, suppression level is: " + this.__suppEvt ); + return; + } this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); }; @@ -270,7 +283,7 @@ this.action = new (Actions[ name ])( this ); this.__pulseMsg = null; - this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); + this.__fireUpdate(); }; Cursor.prototype.closeAction = function() @@ -280,7 +293,7 @@ this.__pulseMsg = this.action.getMessage(); this.action = null; - this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); + this.__fireUpdate(); }; // Open, Run, then close an action @@ -292,9 +305,12 @@ this.__pulseMsg = action.getMessage(); action.dispose(); - this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); + this.__fireUpdate(); }; + Cursor.prototype.suppressEvent = function() { ++ this.__suppEvt; }; + Cursor.prototype.unsuppressEvent = function() { -- this.__suppEvt; }; + Cursor.prototype.getLine = function() { var feeder = this.feeder; diff --git a/botanjs/src/Components/Vim/State/Stator.js b/botanjs/src/Components/Vim/State/Stator.js new file mode 100644 index 0000000..a17687d --- /dev/null +++ b/botanjs/src/Components/Vim/State/Stator.js @@ -0,0 +1,71 @@ +(function(){ + var ns = __namespace( "Components.Vim.State" ); + + var Stator = function( cur, start ) + { + this.__cursor = cur; + this.__startPosition = start == undefined ? cur.aPos : start; + this.__startState = this.__saveCur(); + }; + + Stator.prototype.save = function( insertLength, contentUndo ) + { + var cur = this.__cursor; + var feeder = cur.feeder; + var startPos = this.__startPosition; + + if( insertLength < 0 ) + { + startPos += insertLength; + insertLength = 0; + } + + var sSt = this.__startState; + var eSt = this.__saveCur(); + + var st = sSt; + // Calling this repeatedly will swap between UNDO / REDO state + return function() { + var contentRedo = feeder.content.substr( startPos, insertLength ); + feeder.content = + feeder.content.substring( 0, startPos ) + + contentUndo + + feeder.content.substring( startPos + insertLength ); + insertLength = contentUndo.length; + contentUndo = contentRedo; + + cur.PStart = st.p; + cur.PEnd = st.p + 1; + cur.X = st.x; + cur.Y = st.y; + feeder.panX = st.px; + feeder.panY = st.py; + + feeder.pan(); + + st = ( st == sSt ) ? eSt : sSt; + }; + }; + + Stator.prototype.__saveCur = function() + { + var c = this.__cursor; + var obj = { + p: c.PStart + , x: c.X + , y: c.Y + , px: c.feeder.panX + , py: c.feeder.panY + }; + + if( 0 < obj.x ) + { + obj.p -= 1; + obj.x -= 1; + } + + return obj; + }; + + ns[ NS_EXPORT ]( EX_CLASS, "Stator", Stator ); +})(); diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index 368cecb..54c7864 100644 --- a/botanjs/src/Components/Vim/VimArea.js +++ b/botanjs/src/Components/Vim/VimArea.js @@ -32,7 +32,7 @@ if ( e.keyCode ) code = e.keyCode; else if ( e.which ) code = e.which; - handler( sender, new InputEvent( e ) ); + handler( sender, new InputEvent( sender, e ) ); }; }; diff --git a/botanjs/src/Components/Vim/_this.js b/botanjs/src/Components/Vim/_this.js index 04060da..f9e572e 100644 --- a/botanjs/src/Components/Vim/_this.js +++ b/botanjs/src/Components/Vim/_this.js @@ -17,6 +17,8 @@ , "UNDO_LIMIT": "Already at oldest change" , "REDO_LIMIT": "Already at newest change" + + , "LINE_FEWER": "%1 fewer lines" }; var errors = { diff --git a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js index ffd085e..a8e1bdb 100644 --- a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js +++ b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js @@ -1,6 +1,8 @@ /** @constructor */ Components.Vim.Controls.InputEvent = function(){}; +/** @type {Components.Vim.VimArea} */ +Components.Vim.Controls.InputEvent.target; /** @type String */ Components.Vim.Controls.InputEvent.key; /** @type Boolean */ diff --git a/botanjs/src/externs/Components.Vim.Cursor.js b/botanjs/src/externs/Components.Vim.Cursor.js index 76fef9d..2f42cdb 100644 --- a/botanjs/src/externs/Components.Vim.Cursor.js +++ b/botanjs/src/externs/Components.Vim.Cursor.js @@ -26,9 +26,15 @@ Components.Vim.Cursor.openAction; Components.Vim.Cursor.openRunAction; /** @type Function */ Components.Vim.Cursor.closeAction; +/** @type Function */ +Components.Vim.Cursor.suppressEvent; +/** @type Function */ +Components.Vim.Cursor.unsuppressEvent; /** @type {Boolean} */ Components.Vim.Cursor.blink; +/** @type {Boolean} */ +Components.Vim.Cursor.pSpace; /** @type {Array} */ Components.Vim.Cursor.lineBuffers; /** @type Number */ From 54475422a0d42370ad69419445272d49ad442c12 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: Tue, 29 Mar 2016 03:48:44 +0800 Subject: [PATCH 10/11] using rest args --- botanjs/src/System/Debug.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/botanjs/src/System/Debug.js b/botanjs/src/System/Debug.js index 018da5c..4820769 100644 --- a/botanjs/src/System/Debug.js +++ b/botanjs/src/System/Debug.js @@ -14,10 +14,10 @@ Log.writeLine( e.name + "\n\t" + e.message + "\n\t" + e.stack, Log.ERROR ); }; - var Info = function(e) + var Info = function() { if( st_info ) - Log.writeLine( e, Log.INFO ); + Log.writeLine( Array.prototype.join.call( arguments, " " ), Log.INFO ); }; var turnOff = function( what ) From 3f687cb704b832759532e769df13a127c50a96be 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: Tue, 29 Mar 2016 03:51:55 +0800 Subject: [PATCH 11/11] Some placeholders --- botanjs/src/Components/Vim/Actions/DELETE.js | 19 ++++++ botanjs/src/Components/Vim/Actions/VISUAL.js | 62 +++++++++++++------- botanjs/src/Components/Vim/Controls.js | 32 ++++++++++ 3 files changed, 91 insertions(+), 22 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js index c42b2ef..922f703 100644 --- a/botanjs/src/Components/Vim/Actions/DELETE.js +++ b/botanjs/src/Components/Vim/Actions/DELETE.js @@ -19,6 +19,7 @@ /** @type {Components.Vim.Cursor} */ this.__cursor = Cursor; this.__nline = 0; + this.__startX = Cursor.aPos; }; DELETE.prototype.allowMovement = true; @@ -32,12 +33,28 @@ { e.preventDefault(); + /** @type {Components.Vim.State.Registers} */ var reg = e.target.registers; var cur = this.__cursor; var feeder = cur.feeder; + var Triggered = false; + if( sp == undefined && this.__startX != cur.aPos ) + { + Triggered = true; + + if( e.kMap( "l" ) ) + { + cur.moveX( -1 ); + } + + sp = this.__startX; + } + else if( sp == undefined ) return; + + var c = feeder.content; var s = sp; @@ -77,6 +94,8 @@ cur.rec.record( stack ); feeder.pan(); + + return Triggered; }; DELETE.prototype.getMessage = function() diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js index 659b058..8072e6b 100644 --- a/botanjs/src/Components/Vim/Actions/VISUAL.js +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -17,6 +17,7 @@ /** @type {Components.Vim.Cursor} */ this.__cursor = Cursor; this.__startaP = Cursor.aPos; + this.__startP = { x: Cursor.X, y: Cursor.Y, p: Cursor.P }; this.__start = Cursor.PStart; this.__selStart = Cursor.PStart; this.__msg = Mesg( "VISUAL" ); @@ -45,46 +46,63 @@ var cur = this.__cursor; var Action = null; - switch( true ) - { - case e.kMap( "y" ): - Action = new YANK( cur ); - break; - case e.kMap( "d" ): - Action = new DELETE( cur ); - break; - } - var prevPos = this.__start; - var newPos = cur.PStart; - - var posDiff = newPos - prevPos; - if( 0 <= posDiff ) + if( e.kMap( "y" ) ) { - this.__selStart = newPos; - newPos = newPos + 1; + Action = new YANK( cur ); } - else if( posDiff < 0 ) + else if( e.kMap( "d" ) ) { - prevPos += posDiff; - newPos = this.__start + 1; - this.__selStart = prevPos; + Action = new DELETE( cur ); } if( Action ) { cur.suppressEvent(); + + // Low-level cursor position adjustment + // this swap the cursor direction from LTR to RTL + // i.e. treat all delete as "e<----s" flow + // to keep the cursor position as the top on UNDO / REDO + if( Action.constructor == DELETE && this.__startaP < cur.aPos ) + { + this.__startaP = cur.aPos; + cur.X = this.__startP.x; + cur.Y = this.__startP.y; + cur.P = this.__startP.p; + } + Action.handler( e, this.__startaP ); this.__leaveMesg = Action.getMessage(); + Action.dispose(); cur.unsuppressEvent(); this.__selStart = cur.PStart; + return true; } + else + { + var prevPos = this.__start; + var newPos = cur.PStart; - cur.PStart = prevPos; - cur.PEnd = newPos; + var posDiff = newPos - prevPos; + if( 0 <= posDiff ) + { + this.__selStart = newPos; + newPos = newPos + 1; + } + else if( posDiff < 0 ) + { + prevPos += posDiff; + newPos = this.__start + 1; + this.__selStart = prevPos; + } + + cur.PStart = prevPos; + cur.PEnd = newPos; + } }; VISUAL.prototype.getMessage = function() diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 7bcc195..8dba252 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -26,6 +26,8 @@ var U = 85; var V = 86; var W = 87; var X = 88; var Y = 89; var Z = 90; + var S_BRACKET_L = 219; var S_BRACKET_R = 221; + var ESC = 27; var F1 = 112; var F2 = 113; var F3 = 114; var F4 = 115; var F5 = 116; @@ -184,6 +186,9 @@ case CTRL + R: // Redo ccur.openRunAction( "REDO", e ); break; + case D: // Del with motion + ccur.openAction( "DELETE" ); + break; case X: // Del break; case SHIFT + X: // Delete before @@ -277,6 +282,33 @@ case SHIFT + _5: // %, Find next item break; + case T: // To + break; + case I: // In between boundary + if( !ccur.action ) + { + cursorHandled = false; + break; + } + + this.__cMovement = true; + // Word boundary + this.__comp( kCode, function(){ + debug.Info( "Word boundary" ); + }, W ); + this.__comp( kCode, function(){ + debug.Info( "Bracket boundary [" ); + }, S_BRACKET_L ); + this.__comp( kCode, function(){ + debug.Info( "Bracket boundary ]" ); + }, S_BRACKET_R ); + this.__comp( kCode, function(){ + debug.Info( "Bracket boundary {" ); + }, SHIFT + S_BRACKET_L ); + this.__comp( kCode, function(){ + debug.Info( "Bracket boundary }" ); + }, SHIFT + S_BRACKET_R ); + break; case G: // Go to top this.__cMovement = true;