From 664b0f2ec1a06892e2fa6c8250c6613931413077 Mon Sep 17 00:00:00 2001 From: tgckpg Date: Thu, 28 Apr 2016 12:38:38 +0800 Subject: [PATCH 1/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f20e039..02e8606 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ 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. ## Demo -Visit the demo over [here](https://tgckpg.github.io/BotanJS-vim) +Visit the demo over [here](https://tgckpg.github.io/VimArea) ## This is going to be release soon Common commands are now supported. I am now going to list the commands that yet to be made. From a6ccf7e4dbd85b12e792f74ef7aa88564fc7d6ef 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, 6 Jan 2017 18:05:07 +0800 Subject: [PATCH 2/4] Quote in between & PUT impl --- README.md | 2 +- botanjs/src/Components/Vim/Actions/PUT.js | 28 +++++--- botanjs/src/Components/Vim/Actions/TO.js | 4 +- botanjs/src/Components/Vim/Actions/VISUAL.js | 11 ++- botanjs/src/Components/Vim/Controls.js | 16 ++++- botanjs/src/Components/Vim/Syntax/Analyzer.js | 69 +++++++++++++++++++ .../externs/Components.Vim.Syntax.Analyzer.js | 2 + 7 files changed, 117 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 02e8606..30265e8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Visit the demo over [here](https://tgckpg.github.io/VimArea) Common commands are now supported. I am now going to list the commands that yet to be made. ``` Commands that are going to implement soon: -auto indent ( new line from bracket ), [action]i[quote] +auto indent ( new line from bracket ) :'<,'> Commands that are planning to implement in near future: diff --git a/botanjs/src/Components/Vim/Actions/PUT.js b/botanjs/src/Components/Vim/Actions/PUT.js index 52a6891..6dacc15 100644 --- a/botanjs/src/Components/Vim/Actions/PUT.js +++ b/botanjs/src/Components/Vim/Actions/PUT.js @@ -25,7 +25,7 @@ this.__cursor.unsuppressEvent(); }; - PUT.prototype.handler = function( e ) + PUT.prototype.handler = function( e, sp, newLine ) { e.preventDefault(); @@ -63,20 +63,32 @@ var stator = new Stator( cur ); var aP = cur.aPos; + var contentUndo = ""; - feeder.content = feeder.content.substring( 0, aP ) - + cput - + feeder.content.substring( aP ); + if( sp == undefined ) + { + feeder.content = feeder.content.substring( 0, aP ) + + cput + feeder.content.substring( aP ); - feeder.pan(); + feeder.pan(); + cur.moveTo( 0 < nLines ? aP : aP + clen, true ); + } + else + { + sp ++; + contentUndo = feeder.content.substring( aP, sp ); + feeder.content = feeder.content.substring( 0, aP ) + + cput + feeder.content.substring( sp ); - cur.moveTo( 0 < nLines ? aP : aP + clen, true ); + feeder.pan(); + cur.moveTo( aP + clen, true ); + } var stack = new Stack(); if( newLine ) { - var f = stator.save( clen, "" ); + var f = stator.save( clen, contentUndo ); stack.store( function() { f(); @@ -85,7 +97,7 @@ } else { - stack.store( stator.save( clen, "" ) ); + stack.store( stator.save( clen, contentUndo ) ); } cur.rec.record( stack ); diff --git a/botanjs/src/Components/Vim/Actions/TO.js b/botanjs/src/Components/Vim/Actions/TO.js index eb1e00e..83daa03 100644 --- a/botanjs/src/Components/Vim/Actions/TO.js +++ b/botanjs/src/Components/Vim/Actions/TO.js @@ -38,7 +38,7 @@ if( 0 < n ) p ++; - var lowerLimmit = p; + var lowerLimit = p; var Char = et.key; if( et.kMap( "Tab" ) ) @@ -64,7 +64,7 @@ tX = f.content.lastIndexOf( Char, cur.aPos - 1 ); } - if( lowerLimmit <= tX && tX < upperLimit ) + if( lowerLimit <= tX && tX < upperLimit ) { cur.moveTo( tX ); } diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js index 5e0cbea..55aa192 100644 --- a/botanjs/src/Components/Vim/Actions/VISUAL.js +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -12,6 +12,8 @@ var DELETE = ns[ NS_INVOKE ]( "DELETE" ); /** @type {Components.Vim.IAction} */ var SHIFT_LINES = ns[ NS_INVOKE ]( "SHIFT_LINES" ); + /** @type {Components.Vim.IAction} */ + var PUT = ns[ NS_INVOKE ]( "PUT" ); var MODE_NULL = -1; var MODE_VISUAL = 0; @@ -121,6 +123,10 @@ { Action = new DELETE( cur ); } + else if( e.kMap( "p" ) ) + { + Action = new PUT( cur ); + } else if( e.kMap( "V" ) ) { if( this.__mode == MODE_LINE ) return true; @@ -180,7 +186,8 @@ // 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 && startLine.aPos < cur.aPos ) + var IsContMod = ~[ DELETE, PUT ].indexOf( Action.constructor ); + if( IsContMod && startLine.aPos < cur.aPos ) { var o = cur.aPos; cur.moveTo( startLine.aPos, true ); @@ -189,7 +196,7 @@ Action.handler( e, startLine.aPos, lineMode ); - if( Action.constructor != DELETE ) + if( !IsContMod ) { cur.moveTo( startLine.aPos ); } diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 36d0e04..cdb320c 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -616,14 +616,26 @@ var BracketMatch = analyzer.bracketIn( "[", ccur.aPos ); e2.__range = BracketMatch; }; + var singleQuote = function( e2 ) { + var BracketMatch = analyzer.quoteIn( "'", ccur.aPos ); + e2.__range = BracketMatch; + }; + var doubleQuote = function( e2 ) { + var BracketMatch = analyzer.quoteIn( "\"", ccur.aPos ); + e2.__range = BracketMatch; + }; // Bracket boundaries - this.__composite( e, bracket , SHIFT + _0 ); + this.__composite( e, bracket, SHIFT + _0 ); this.__composite( e, bracket, SHIFT + _9 ); this.__composite( e, squareBracket, S_BRACKET_L ); this.__composite( e, squareBracket, S_BRACKET_R ); this.__composite( e, curlyBracket, SHIFT + S_BRACKET_L ); this.__composite( e, curlyBracket, SHIFT + S_BRACKET_R ); + + // Quote boundaries + this.__composite( e, singleQuote, QUOTE ); + this.__composite( e, doubleQuote, SHIFT + QUOTE ); break; case G: @@ -725,7 +737,7 @@ var cfeeder = this.__cfeeder; var ccur = this.__ccur; - if( !ccur.action || ccur.action.allowMovement ) + if( !this.__cMovement && ( !ccur.action || ccur.action.allowMovement ) ) { this.__modCommand( e ); if( e.canceled ) return; diff --git a/botanjs/src/Components/Vim/Syntax/Analyzer.js b/botanjs/src/Components/Vim/Syntax/Analyzer.js index c10e441..de66e22 100644 --- a/botanjs/src/Components/Vim/Syntax/Analyzer.js +++ b/botanjs/src/Components/Vim/Syntax/Analyzer.js @@ -4,6 +4,8 @@ /** @type {System.Debug} */ var debug = __import( "System.Debug" ); + var beep = __import( "Components.Vim.Beep" ); + /** @type {Components.Vim.Syntax.Word} */ var Word = ns[ NS_INVOKE ]( "Word" ); @@ -265,6 +267,73 @@ return new TokenMatch(); }; + Analyzer.prototype.quoteIn = function( Char ) + { + var f = this.__feeder; + var cur = f.cursor; + var n = cur.getLine().lineNum; + + var p = 0; + for( var i = 0; p != -1 && i < n; i ++ ) p = f.content.indexOf( "\n", p + 1 ); + + var upperLimit = f.content.indexOf( "\n", p + 1 ); + + if( 0 < n ) p ++; + + var lowerLimit = p; + + // Cursor is at the quote character + // Move cursor inside the matching quote + if( f.content[ cur.aPos ] == Char ) + { + // Mark all quotes on current line + var quotePos = []; + var l = 0; + + for( var i = lowerLimit; i < upperLimit; i ++ ) + { + if( f.content[ i ] == Char ) + quotePos[ l ++ ] = i; + } + + var indexQuote = quotePos.indexOf( cur.aPos ); + var indexEnd = ( indexQuote == ( l - 1 ) ); + + // Length is even: Quotes are matched + // OR + // Length is odd and cursor is not at the last quote + if( l % 2 == 0 || !indexEnd ) + { + cur.moveX( indexQuote % 2 == 0 ? 1 : -1 ); + } + // Cursor is at the last quote + else + { + // Beep because last quote of odd length is an unmatch quote + beep(); + return new TokenMatch(); + } + + } + + // Forward + var fX = f.content.indexOf( Char, cur.aPos + 1 ); + // backward + var bX = f.content.lastIndexOf( Char, cur.aPos - 1 ); + + if( lowerLimit <= bX && fX < upperLimit ) + { + var tMatch = new TokenMatch(); + tMatch.__open = bX + 1; + tMatch.__close = fX - 1; + tMatch.__selected = Char; + return tMatch; + } + else beep(); + + return new TokenMatch(); + }; + Analyzer.prototype.__getPairs = function( def, reload ) { if( !reload && this.__tokpairs[ def ] ) diff --git a/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js b/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js index 24f603c..ae4b31f 100644 --- a/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js +++ b/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js @@ -6,6 +6,8 @@ Components.Vim.Syntax.Analyzer.bracketAt; /** @type Function */ Components.Vim.Syntax.Analyzer.bracketIn; /** @type Function */ +Components.Vim.Syntax.Analyzer.quoteIn; +/** @type Function */ Components.Vim.Syntax.Analyzer.wordAt; /** @type Function */ Components.Vim.Syntax.Analyzer.quoteAt; From 50686073d8c055a0fea8bad2e5bfaf4caa7580b9 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: Mon, 9 Jan 2017 10:18:21 +0800 Subject: [PATCH 3/4] Replaced quirky tab char with virtual space chars --- botanjs/src/Components/Vim/Cursor.js | 92 ++++++++++++++------ botanjs/src/Components/Vim/LineBuffer.js | 13 +-- botanjs/src/Components/Vim/LineFeeder.js | 16 ++-- botanjs/src/externs/Components.Vim.Cursor.js | 2 + 4 files changed, 87 insertions(+), 36 deletions(-) diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index f8d2dd9..810bf70 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -9,6 +9,8 @@ var Actions = __import( "Components.Vim.Actions.*" ); + var occurence = __import( "System.utils.Perf.CountSubstr" ); + var LineOffset = function( buffs, l ) { /** @type {Components.Vim.LineBuffer} */ @@ -69,6 +71,8 @@ this.cols = feeder.firstBuffer.cols; // The preferred X position + // i.e. last line was at pos 23 + // moving to next line will prefer pos at 23 this.pX = 0; // The displaying X position @@ -129,10 +133,11 @@ var jumpX = aPos < lineStart ? lineStart - aPos : aPos - lineStart; jumpX += Math.ceil( jumpX / pline.cols ) - 1; + jumpX += occurence( content.substring( lineStart + 1, aPos ), "\t" ) * ( pline.tabWidth - 1 ); if( jumpY ) this.moveY( jumpY ); - // This needed because first line does not contain first "\n" character + // This is needed because first line does not contain the first "\n" character if( 0 < this.getLine().lineNum && lineStart <= aPos ) jumpX --; this.moveX( - Number.MAX_VALUE ); @@ -179,26 +184,14 @@ var hasPhantomSpace = true; // Empty lines has length of 1 - // If length larger than a, need to compensate the lineEnd - // for phantomSpace + // Need to compensate the lineEnd for phantomSpace + // if length is 1 < and != line.cols if( 1 < cLen ) { - // Begin check if whether this line contains phantomSpace - var lineNum = line.lineNum - 1; - var str = feeder.content; - for( var i = str.indexOf( "\n" ), j = 0; 0 <= i; i = str.indexOf( "\n", i ), j ++ ) - { - if( lineNum == j ) break; - i ++; - } - - if( j == 0 && i == -1 ) i = 0; - - var end = str.indexOf( "\n", i + 1 ); - end = end == -1 ? str.length : end; - - // Actual LineLength - var hasPhantomSpace = 0 < ( end - i - 1 ) % line.cols; + // Begin check if this line contains phantomSpace + // hasPhantomSpace = 0 < ( rawLine.displayLength ) % cols + var rline = this.rawLine; + hasPhantomSpace = 0 < ( rline.length + occurence( rline, "\t" ) * ( line.tabWidth - 1 ) ) % line.cols; if( hasPhantomSpace ) { @@ -421,14 +414,12 @@ Cursor.prototype.suppressEvent = function() { ++ this.__suppEvt; }; Cursor.prototype.unsuppressEvent = function() { -- this.__suppEvt; }; - Cursor.prototype.getLine = function() + Cursor.prototype.getLine = function( raw ) { var feeder = this.feeder; var line = feeder.firstBuffer; var eBuffer = feeder.lastBuffer.next; - for( var i = 0; - line != eBuffer; - line = line.next ) + for( var i = 0; line != eBuffer; line = line.next ) { if( line.br ) i ++; if( this.Y == i ) return line; @@ -437,26 +428,49 @@ return null; }; + __readOnly( Cursor.prototype, "rawLine", function() + { + var str = this.feeder.content; + var lineNum = this.getLine().lineNum - 1; + var i = str.indexOf( "\n" ), j = 0; + + for( ; 0 <= i; i = str.indexOf( "\n", i ), j ++ ) + { + if( lineNum == j ) break; + i ++; + } + + if( j == 0 && i == -1 ) i = 0; + + var end = str.indexOf( "\n", i + 1 ); + return str.substring( i + 1, end ); + } ); + // The position offset relative to current line __readOnly( Cursor.prototype, "aX", function() { var X = this.X; var f = this.feeder; - var w = 1; + var w = 0; // Calculate wordwrap offset if( f.wrap ) { var lines = this.getLine().visualLines; + // Since w represent valid absX position + // w couldn't handle INSERT at the line end with phantomSpace + // because phantomSpace is not a valid character + // So we calculate along with the phantomSpace here + var phantomSpace = X; for( var i in lines ) { /** @type {Components.Vim.LineBuffer} */ var vline = lines[ i ]; // Actual length - var aLen = vline.content.toString().length; + var aLen = vline.content.length; // Visual length var vLen = vline.toString().length; @@ -467,10 +481,36 @@ if( 0 <= X ) { w += aLen; + phantomSpace -= 1 + occurence( vline.content, "\t" ) * ( vline.tabWidth - 1 ); } else if( X < 0 ) { - w += X + vLen; + X += vLen + 1; + + var rline = this.rawLine.substr( w ); + var l = rline.length; + + var j = 0; + + if( rline[ 0 ] == "\t" ) + { + X -= vline.tabWidth - 1; + phantomSpace -= vline.tabWidth - 1; + } + + for( var i = 1; j < X && i < rline.length; i ++ ) + { + if( rline[ i ] == "\t" ) + { + X -= vline.tabWidth - 1; + phantomSpace -= vline.tabWidth - 1; + } + j ++; + } + + w += j; + + if( w < phantomSpace ) w = phantomSpace; break; } } diff --git a/botanjs/src/Components/Vim/LineBuffer.js b/botanjs/src/Components/Vim/LineBuffer.js index e097f83..88601fb 100644 --- a/botanjs/src/Components/Vim/LineBuffer.js +++ b/botanjs/src/Components/Vim/LineBuffer.js @@ -13,7 +13,9 @@ this.br = false; this.placeholder = true; this.lineNum = 0; - this.tabWidth = 8; + this.tabWidth = 4; + this.__tabc = ""; + for( var i = 0; i < this.tabWidth; i ++ ) this.__tabc += " "; if( nextLineBuffer ) { @@ -105,13 +107,14 @@ LineBuffer.prototype.toString = function() { - var c = this.cols - occurence( this.content, "\t" ) * ( this.tabWidth - 1 ); - if( this.content.length < c ) + var cont = this.content.replace( /\t/g, this.__tabc ); + + if( cont.length < this.cols ) { - return this.content + " "; + return cont + " "; } - return this.content || " "; + return cont || " "; }; __readOnly( LineBuffer.prototype, "nextLine", function() diff --git a/botanjs/src/Components/Vim/LineFeeder.js b/botanjs/src/Components/Vim/LineFeeder.js index 95b5ab4..4098773 100644 --- a/botanjs/src/Components/Vim/LineFeeder.js +++ b/botanjs/src/Components/Vim/LineFeeder.js @@ -158,9 +158,7 @@ } } - this.firstBuffer.Push( - this.content.substr( f + 1 ) - , this.wrap, i ); + this.firstBuffer.Push( this.content.substr( f + 1 ), this.wrap, i ); this.panX = X; this.panY = Y; @@ -213,11 +211,19 @@ var line = this.cursor.getLine(); var tabStat = ""; - var tabs = line.content.match( /\t/g ); + var tabs = 0; + var l = this.cursor.aPos; + var i = l - X; + do + { + if( this.content[ i + 1 ] == "\t" ) tabs ++; + i ++; + } + while( i < l ) if( tabs ) { - tabStat = "-" + ( X + tabs.length * ( line.tabWidth - 1 ) ); + tabStat = "-" + ( X + tabs * ( line.tabWidth - 1 ) ); } return ( line.lineNum + 1 ) + "," + X + tabStat; diff --git a/botanjs/src/externs/Components.Vim.Cursor.js b/botanjs/src/externs/Components.Vim.Cursor.js index 8cf84f8..5bdc8b1 100644 --- a/botanjs/src/externs/Components.Vim.Cursor.js +++ b/botanjs/src/externs/Components.Vim.Cursor.js @@ -63,3 +63,5 @@ Components.Vim.Cursor.position; Components.Vim.Cursor.position.start; /** @type Number */ Components.Vim.Cursor.position.end; +/** @type String */ +Components.Vim.Cursor.rawLine; From 4b200697dcebbbbc004b124c0d002412d1461bc7 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: Mon, 9 Jan 2017 10:48:00 +0800 Subject: [PATCH 4/4] Fixed autoIndent by tab pos error --- botanjs/src/Components/Vim/Actions/INSERT.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js index 08310ea..4beaca5 100644 --- a/botanjs/src/Components/Vim/Actions/INSERT.js +++ b/botanjs/src/Components/Vim/Actions/INSERT.js @@ -197,7 +197,7 @@ { this.__realizeIndent(); feeder.pan(); - cur.moveX( 1, false, true ); + cur.moveX( inputChar == "\t" ? feeder.firstBuffer.tabWidth : 1, false, true ); } feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); @@ -262,7 +262,7 @@ feeder.softReset(); feeder.pan(); - cur.moveX( i, false, true ); + cur.moveX( i * feeder.firstBuffer.tabWidth, false, true ); var a = []; a[ IN_START ] = f;