From fcfe5d9b6028dfb04d09a031510dfd18ea9726cc 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, 5 Apr 2016 02:57:40 +0800 Subject: [PATCH] Added VISUAL LINE --- botanjs/src/Components/Vim/Actions/DELETE.js | 3 +- botanjs/src/Components/Vim/Actions/TO.js | 8 +- botanjs/src/Components/Vim/Actions/VISUAL.js | 227 +++++++++++++++++-- botanjs/src/Components/Vim/Actions/YANK.js | 3 +- botanjs/src/Components/Vim/Controls.js | 5 +- botanjs/src/Components/Vim/Cursor.js | 3 +- botanjs/src/Components/Vim/VimArea.js | 5 + 7 files changed, 220 insertions(+), 34 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js index 30cb180..10a09c9 100644 --- a/botanjs/src/Components/Vim/Actions/DELETE.js +++ b/botanjs/src/Components/Vim/Actions/DELETE.js @@ -33,7 +33,7 @@ this.__cursor.unsuppressEvent(); }; - DELETE.prototype.handler = function( e, sp ) + DELETE.prototype.handler = function( e, sp, newLine ) { e.preventDefault(); @@ -46,7 +46,6 @@ var feeder = cur.feeder; var Triggered = false; - var newLine = false; if( sp == undefined ) { diff --git a/botanjs/src/Components/Vim/Actions/TO.js b/botanjs/src/Components/Vim/Actions/TO.js index 1ff0bcd..eb1e00e 100644 --- a/botanjs/src/Components/Vim/Actions/TO.js +++ b/botanjs/src/Components/Vim/Actions/TO.js @@ -40,9 +40,6 @@ var lowerLimmit = p; - var cX = cur.X; - var tX = cX; - var Char = et.key; if( et.kMap( "Tab" ) ) { @@ -55,15 +52,16 @@ return; } + var tX = -1; // Forward if( em.kMap( "t" ) || em.kMap( "f" ) ) { - tX = f.content.indexOf( Char, p + cX + 1 ); + tX = f.content.indexOf( Char, cur.aPos + 1 ); } // backward else { - tX = f.content.lastIndexOf( Char, p + cX - 1 ); + tX = f.content.lastIndexOf( Char, cur.aPos - 1 ); } if( lowerLimmit <= tX && tX < upperLimit ) diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js index bd9085d..b53b47d 100644 --- a/botanjs/src/Components/Vim/Actions/VISUAL.js +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -11,12 +11,44 @@ /** @type {Components.Vim.IAction} */ var DELETE = ns[ NS_INVOKE ]( "DELETE" ); + var MODE_NULL = -1; + var MODE_VISUAL = 0; + var MODE_LINE = 1; + + // The offset of given line relative to content + var offsetY = function( cur, l ) + { + if( l == 0 ) return 0; + + var f = cur.feeder; + + var j = 0; + + var last = -1; + for( var i = f.content.indexOf( "\n" ); 0 <= i; i = f.content.indexOf( "\n", i + 1 ) ) + { + last = i; + j ++; + if( l <= j ) break; + } + + if( f.EOF ) i = last; + + // "\n" compensation + var c = f.content[ i + 1 ]; + if(!( c == undefined || c == "\n" )) + { + i ++; + } + + return i; + }; + /** @type {Components.Vim.IAction} */ var VISUAL = function( Cursor ) { this.__reset( Cursor ); - this.__msg = Mesg( "VISUAL" ); - + this.__msg = ""; Cursor.blink = false; Cursor.pSpace = true; }; @@ -25,9 +57,25 @@ { /** @type {Components.Vim.Cursor} */ this.__cursor = Cursor; - this.__startaP = Cursor.aPos; - this.__start = Cursor.PStart; - this.__selStart = Cursor.PStart; + + var s = { + lineNum: Cursor.getLine().lineNum + , X: Cursor.X + , aPos: Cursor.aPos + , pstart: Cursor.PStart + }; + + s.aStart = s.aPos - Cursor.aX; + + Cursor.suppressEvent(); + Cursor.lineEnd( true ); + + s.aEnd = Cursor.aPos; + + Cursor.moveTo( s.aPos ); + Cursor.unsuppressEvent(); + + this.__startLine = s; }; VISUAL.prototype.allowMovement = true; @@ -38,8 +86,7 @@ c.blink = true; c.pSpace = false; - c.PStart = this.__selStart; - c.PEnd = this.__selStart + 1; + c.updatePosition(); // This fix the highlighting position of missing phantomSpace // for maximum filled line @@ -59,8 +106,11 @@ if( e.ModKeys ) return; var cur = this.__cursor; + var feeder = cur.feeder; var Action = null; + var dispatchUpdate = false; + if( e.kMap( "y" ) ) { Action = new YANK( cur ); @@ -69,27 +119,73 @@ { Action = new DELETE( cur ); } + else if( e.kMap( "V" ) ) + { + if( this.__mode == MODE_LINE ) return true; + else + { + dispatchUpdate = true; + this.__mode = MODE_LINE; + this.__msg = Mesg( "VISLINE" ); + } + } + else if( e.kMap( "v" ) ) + { + if( this.__mode == MODE_VISUAL ) return true; + else + { + dispatchUpdate = true; + this.__mode = MODE_VISUAL; + this.__msg = Mesg( "VISUAL" ); + cur.updatePosition(); + } + } + + if( dispatchUpdate ) + feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); + + if( this.__mode == MODE_NULL ) + { + debug.Error( new Error( "Mode is undefined" ) ); + return true; + } + + var startLine = this.__startLine; if( Action ) { cur.suppressEvent(); - // Low-level cursor position adjustment + var lineMode = this.__mode == MODE_LINE; + if( lineMode ) + { + if( startLine.aPos < cur.aPos ) + { + cur.lineEnd( true ); + startLine.aPos = startLine.aStart; + } + else + { + cur.lineStart(); + startLine.aPos = startLine.aEnd; + } + } + // 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 ) + if( Action.constructor == DELETE && startLine.aPos < cur.aPos ) { var o = cur.aPos; - cur.moveTo( this.__startaP, true ); - this.__startaP = o; + cur.moveTo( startLine.aPos, true ); + startLine.aPos = o; } - Action.handler( e, this.__startaP ); + Action.handler( e, startLine.aPos, lineMode ); if( Action.constructor != DELETE ) { - cur.moveTo( this.__startaP ); + cur.moveTo( startLine.aPos ); } this.__msg = Action.getMessage(); @@ -97,7 +193,7 @@ Action.dispose(); cur.unsuppressEvent(); - this.__selStart = cur.PStart; + startLine.pstart = cur.PStart; return true; } @@ -109,30 +205,119 @@ var r = e.range; - if( cur.aPos == this.__startaP ) + if( cur.aPos == startLine.aPos ) { cur.moveTo( r.open, true ); this.__reset( cur ); + startLine = this.__startLine; } cur.unsuppressEvent(); cur.moveTo( r.close, true ); } - var prevPos = this.__start; - var newPos = cur.PStart; + var currAp = cur.aPos; + + // Calculate the visible max min aPos of the current screen + var line = feeder.firstBuffer; + var firstLine = line.lineNum; + var minAp = offsetY( cur, firstLine ); + var maxAp = offsetY( cur, firstLine + feeder.moreAt + 1 ) - 1; + + debug.Info( "Min aPos: " + minAp, "Max aPos: " + maxAp ); + + var pstart = startLine.X; + var nstart = cur.PStart; + + // highlight from the start + if( startLine.aPos < minAp ) + { + pstart = 0; + } + // highlight from the end + else if( maxAp < startLine.aPos ) + { + pstart = -2; + var i = 0; + do + { + if( line.placeholder ) break; + if( i <= feeder.moreAt ) + { + pstart += line.toString().length + 1; + } + i ++; + } + while( line = line.next ); + } + else + { + var l = startLine.lineNum; + if( this.__mode == MODE_LINE ) + { + cur.suppressEvent(); + pstart = 0; + + if( currAp < startLine.aPos ) + { + pstart = -1; + l ++; + + cur.lineStart(); + nstart = cur.PStart; + } + else if( startLine.aPos < currAp ) + { + cur.lineEnd( true ); + nstart = cur.PStart; + } + // aPos == currPos + else + { + cur.lineStart(); + nstart = cur.PStart; + cur.lineEnd( true ); + pstart = cur.PStart; + l = line.lineNum; + } + + cur.moveTo( currAp, true ); + + cur.unsuppressEvent(); + } + else if( this.__mode == MODE_VISUAL ) + { + if( currAp == startLine.aPos ) return; + } + + // Append the Y offset + var i = 0; + do + { + if( line.lineNum == l ) break; + pstart += line.toString().length + 1; + } + while( line = line.next ); + } + + var prevPos = pstart; + var newPos = nstart; var posDiff = newPos - prevPos; - if( 0 <= posDiff ) + + var currAp = cur.aPos; + + // Sets the visual position + // s-->e + if( 0 < posDiff ) { - this.__selStart = newPos; newPos = newPos + 1; } + // e<--s else if( posDiff < 0 ) { prevPos += posDiff; - newPos = this.__start + 1; - this.__selStart = prevPos; + newPos = pstart + 1; } cur.PStart = prevPos; diff --git a/botanjs/src/Components/Vim/Actions/YANK.js b/botanjs/src/Components/Vim/Actions/YANK.js index 86ba222..44e3c27 100644 --- a/botanjs/src/Components/Vim/Actions/YANK.js +++ b/botanjs/src/Components/Vim/Actions/YANK.js @@ -27,7 +27,7 @@ this.__cursor.unsuppressEvent(); }; - YANK.prototype.handler = function( e, sp ) + YANK.prototype.handler = function( e, sp, newLine ) { e.preventDefault(); @@ -41,7 +41,6 @@ var Triggered = false; - var newLine = false; if( sp == undefined ) { Triggered = true; diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index f38b847..aace42f 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -271,10 +271,9 @@ break; case V: // Visual - ccur.openAction( "VISUAL" ); - break; case SHIFT + V: // Visual line - ccur.openAction( "VISUAL_LINE" ); + ccur.openAction( "VISUAL" ); + ccur.action.handler( e ); break; case SHIFT + SEMI_COLON: // ":" Command line diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index 3eaaac0..876614e 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -435,7 +435,7 @@ return null; }; - // The absX for current Line + // The position offset relative to current line __readOnly( Cursor.prototype, "aX", function() { var X = this.X; @@ -473,6 +473,7 @@ } } } + else return this.X; return w; } ); diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index 1817deb..f0899af 100644 --- a/botanjs/src/Components/Vim/VimArea.js +++ b/botanjs/src/Components/Vim/VimArea.js @@ -222,6 +222,11 @@ _self.select( cfeeder.cursor.position ); }; + cfeeder.dispatcher.addEventListener( "SelectionChanged", function() + { + _self.select( cfeeder.cursor.position ); + } ); + cfeeder.dispatcher.addEventListener( "VisualUpdate", Update ); Update();