From 799a911e06355140a78cd1a0240715a863e21d81 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, 30 Mar 2016 04:32:36 +0800 Subject: [PATCH 1/9] Partial % motion --- botanjs/src/Components/Vim/Controls.js | 27 ++ botanjs/src/Components/Vim/Cursor.js | 45 ++- botanjs/src/Components/Vim/Syntax/Analyzer.js | 267 ++++++++++++++++++ botanjs/src/Components/Vim/VimArea.js | 6 +- .../externs/Components.Vim.Syntax.Analyzer.js | 7 + .../Components.Vim.Syntax.TokenMatch.js | 14 + botanjs/src/externs/Components.Vim.Syntax.js | 2 + botanjs/src/externs/Components.Vim.VimArea.js | 2 + 8 files changed, 359 insertions(+), 11 deletions(-) create mode 100644 botanjs/src/Components/Vim/Syntax/Analyzer.js create mode 100644 botanjs/src/externs/Components.Vim.Syntax.Analyzer.js create mode 100644 botanjs/src/externs/Components.Vim.Syntax.TokenMatch.js create mode 100644 botanjs/src/externs/Components.Vim.Syntax.js diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 8dba252..0909015 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -281,6 +281,30 @@ break case SHIFT + _5: // %, Find next item + var analyzer = this.__vimArea.contentAnalyzer; + + /** @type {Components.Vim.Syntax.TokenMatch} */ + var bracketMatch = analyzer.bracketAt( ccur.aPos ); + + if( bracketMatch.open == -1 ) + { + beep(); + break; + } + + var d = 1; + var at = bracketMatch.close; + if( bracketMatch.selected == at ) + { + d = -1; + at = bracketMatch.open; + } + + while( ccur.aPos != at ) + { + ccur.moveX( d, true ); + } + break; case T: // To break; @@ -291,6 +315,8 @@ break; } + var analyzer = this.__vimArea.contentAnalyzer; + this.__cMovement = true; // Word boundary this.__comp( kCode, function(){ @@ -307,6 +333,7 @@ }, SHIFT + S_BRACKET_L ); this.__comp( kCode, function(){ debug.Info( "Bracket boundary }" ); + analyzer.bracketAt( ccur.aPos ); }, SHIFT + S_BRACKET_R ); break; diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index 5f99f5d..240dd10 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -88,9 +88,8 @@ // Set by VimArea Cursor.prototype.Vim; - // Can only be 1, -1 - // 0 will be treated as undefined - Cursor.prototype.moveX = function( d, penentrate, phantomSpace ) + // 0 will be treated as default ( 1 ) + Cursor.prototype.moveX = function( d, penetrate, phantomSpace ) { var x = this.pX; @@ -101,7 +100,7 @@ var buffs = this.feeder.lineBuffers; - if( penentrate ) + if( penetrate ) { if( x < 0 && ( 0 < this.feeder.panY || 0 < this.Y ) ) { @@ -118,15 +117,41 @@ var c = content[ x ]; - // Include empty lines befor cursor end + // Motion includes empty lines before cursor end if( ( phantomSpace && cLen - 1 <= x ) || ( cLen == 1 && c == undefined ) ) { - x = 0 < d ? cLen - 1 : 0; + if( 0 < d ) + { + x = cLen - 1; + if( penetrate ) + { + this.X = 0; + this.moveY( 1 ); + return; + } + } + else + { + x = 0; + } } - // ( 2 < cLen ) Exclude empty lines at cursor end + // ( 2 < cLen ) motion excludes empty lines at cursor end else if( ( 2 <= cLen && x == cLen - 1 && c == " " ) || c == undefined ) { - x = 0 < d ? cLen - 2 : 0; + if( 0 < d ) + { + x = cLen - 2; + if( penetrate ) + { + this.X = 0; + this.moveY( 1 ); + return; + } + } + else + { + x = 0; + } } else if( c == "\n" ) { @@ -174,7 +199,7 @@ this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); }; - Cursor.prototype.moveY = function( d, penentrate ) + Cursor.prototype.moveY = function( d, penetrate ) { var i; var Y = this.Y + d; @@ -197,7 +222,7 @@ { var feeder = this.feeder; - if( penentrate ) + if( penetrate ) { feeder.pan( undefined, Y - moreAt ); } diff --git a/botanjs/src/Components/Vim/Syntax/Analyzer.js b/botanjs/src/Components/Vim/Syntax/Analyzer.js new file mode 100644 index 0000000..b21aace --- /dev/null +++ b/botanjs/src/Components/Vim/Syntax/Analyzer.js @@ -0,0 +1,267 @@ +(function(){ + var ns = __namespace( "Components.Vim.Syntax" ); + + /** @type {System.Debug} */ + var debug = __import( "System.Debug" ); + + var TOK_OPEN = 0; + var TOK_CLOSED = 1; + var TOK_LEVEL = 2; + var TOK_PARENT = 3; + + var TOK_SEP = "\n"; + + var TOK_JOIN = function( a, b ) { return a + TOK_SEP + b; }; + + /*{{{ Private Class */ + var TokenPairs = function( tok, content, esc ) + { + var l = content.length; + var toks = tok.split( TOK_SEP ); + var openToken = toks[0]; + var closeToken = toks[1]; + + var opStack = []; + + var unmatchedEd = []; + + var lv = 0; + + var pairs = []; + + var lvUp = function( i ) + { + opStack[ lv ] = i; + lv ++; + }; + + var lvDown = function( i ) + { + if( lv == 0 ) + { + // Cannot level down. i.e. Unmatched tokens + unmatchedEd.push( i ); + return; + } + + var Token = []; + Token[ TOK_OPEN ] = opStack[ -- lv ]; + Token[ TOK_CLOSED ] = i; + Token[ TOK_LEVEL ] = lv; + Token[ TOK_PARENT ] = 0 < lv ? opStack[ lv - 1 ] : -1; + + pairs.push( Token ); + }; + + var opLen = openToken.length; + var edLen = closeToken.length; + for( var i = 0; i < l; i ++ ) + { + var opTok = content.substr( i, opLen ); + var edTok = content.substr( i, edLen ); + if( opTok == openToken ) + { + lvUp( i ); + i += opLen - 1; + } + else if( edTok == closeToken ) + { + lvDown( i ); + i += edLen - 1; + } + } + + if( unmatchedEd.length ) + { + debug.Info( "Unmatched opening \"" + openToken + "\"@" + unmatchedEd.join( ", " ) ); + } + + if( 0 < lv ) + { + debug.Info( "Unmatched closing \"" + closeToken + "\"@" + opStack.slice( 0, lv ) ); + } + + this.__pairs = pairs; + this.token = toks; + }; + + TokenPairs.prototype.token = ""; + + TokenPairs.prototype.matched = function() + { + return this.__pairs.sort( + function( a, b ) { return a[ TOK_OPEN ] - b[ TOK_OPEN ]; } + ); + }; + + TokenPairs.prototype.find = function( pos, state ) + { + if( state == undefined ) state = TOK_OPEN; + + var pairs = this.__pairs; + var l = pairs.length; + + for( var i = 0; i < l; i ++ ) + { + var pair = pairs[i]; + if( pair[ state ] == pos ) + { + return pair; + } + } + + return null; + }; + /* End Private Class }}}*/ + + var Analyzer = function( feeder ) + { + /* @type {Components.Vim.LineFeeder} */ + this.__feeder = feeder; + this.__tokpairs = {}; + }; + + Analyzer.prototype.bracketAt = function( p ) + { + var c = this.__feeder.content; + var tokState = TOK_CLOSED; + + var BracketPairs = null; + var cTok = c[ p ]; + + switch( cTok ) + { + case "{": tokState = TOK_OPEN; + case "}": + BracketPairs = this.GetPairs( TOK_JOIN( "{", "}" ) ); + break; + + case "[": tokState = TOK_OPEN; + case "]": + BracketPairs = this.GetPairs( TOK_JOIN( "[", "]" ) ); + break; + + case "(": tokState = TOK_OPEN; + case ")": + BracketPairs = this.GetPairs( TOK_JOIN( "(", ")" ) ); + break; + + case "/": + if( c[ p - 1 ] == "*" ) + { + cTok = "*/"; + p --; + break; + } + else if( c[ p + 1 ] == "*" ) + { + cTok = "/*"; + break; + } + return new TokenMatch(); + + case "*": + if( c[ p - 1 ] == "/" ) + { + cTok = "/*"; + p --; + break; + } + else if( c[ p + 1 ] == "/" ) + { + cTok = "*/"; + break; + } + return new TokenMatch(); + + default: + return new TokenMatch(); + } + + // Long Switch + if( !BracketPairs ) switch( cTok ) + { + case "/*": tokState = TOK_OPEN; + case "*/": + BracketPairs = this.GetPairs( TOK_JOIN( "/*", "*/" ) ); + break; + + default: + return new TokenMatch(); + } + + var SetParent = function( pair ) + { + if( !pair ) throw new Error( "Parent not found" ); + + var tMatch = new TokenMatch(); + tMatch.__level = pair[ TOK_LEVEL ]; + tMatch.__open = pair[ TOK_OPEN ]; + tMatch.__close = pair[ TOK_CLOSED ]; + + if( -1 < pair[ TOK_PARENT ] ) + { + var rPair = BracketPairs.find( pair[ TOK_PARENT ] ); + tMatch.__parent = SetParent( rPair ); + } + + return tMatch; + }; + + var rPair = BracketPairs.find( p, tokState ); + var tMatch = SetParent( rPair ) + tMatch.__selected = p; + + return tMatch; + }; + + Analyzer.prototype.GetPairs = function( def, reload ) + { + if( !reload && this.__tokpairs[ def ] ) + { + return this.__tokpairs[ def ]; + } + + var c = this.__feeder.content; + var pairs = new TokenPairs( def, c ); + + this.__tokpairs[ def ] = pairs; + + return pairs; + }; + + Analyzer.prototype.quoteAt = function( p ) + { + var c = this.__feeder.content; + switch( c[ p ] ) + { + case "`": + case "\"": + case "\'": + default: + return { + level: 0 + , open: -1 + , close: -1 + }; + } + }; + + var TokenMatch = function() + { + this.__open = -1; + this.__close = -1; + this.__selected = -1; + this.__level = -1; + this.__parent = null; + }; + + __readOnly( TokenMatch.prototype, "parent", function() { return this.__parent; } ); + __readOnly( TokenMatch.prototype, "open", function() { return this.__open; } ); + __readOnly( TokenMatch.prototype, "close", function() { return this.__close; } ); + __readOnly( TokenMatch.prototype, "level", function() { return this.__level; } ); + __readOnly( TokenMatch.prototype, "selected", function() { return this.__selected; } ); + + ns[ NS_EXPORT ]( EX_CLASS, "Analyzer", Analyzer ); + ns[ NS_EXPORT ]( EX_CLASS, "TokenMatch", TokenMatch ); +})(); diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index 54c7864..116bd46 100644 --- a/botanjs/src/Components/Vim/VimArea.js +++ b/botanjs/src/Components/Vim/VimArea.js @@ -11,7 +11,9 @@ var debug = __import( "System.Debug" ); /** @type {Components.Vim.State.Registers} */ - var Registers = __import( "Components.Vim.State.Registers" ); + var Registers = __import( "Components.Vim.State.Registers" ); + /** @type {Components.Vim.Syntax.Analyzer} */ + var SyntaxAnalyzer = __import( "Components.Vim.Syntax.Analyzer" ); /** @type {Components.Vim.LineFeeder} */ var LineFeeder = ns[ NS_INVOKE ]( "LineFeeder" ); @@ -94,6 +96,7 @@ // Content feeder var cfeeder = new LineFeeder( cRange, c ); + var contentAnalyzer = new SyntaxAnalyzer( cfeeder ); // Feed the contents to content feeder // This "\n" fixes the last line "\n" not displaying @@ -132,6 +135,7 @@ Update(); this.contentFeeder = cfeeder; + this.contentAnalyzer = contentAnalyzer; this.statusFeeder = sfeeder; this.statusBar = statusBar; this.registers = new Registers(); diff --git a/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js b/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js new file mode 100644 index 0000000..f255903 --- /dev/null +++ b/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js @@ -0,0 +1,7 @@ +/** @constructor */ +Components.Vim.Syntax.Analyzer = function(){}; + +/** @type Function */ +Components.Vim.Syntax.Analyzer.bracketAt; +/** @type Function */ +Components.Vim.Syntax.Analyzer.quoteAt; diff --git a/botanjs/src/externs/Components.Vim.Syntax.TokenMatch.js b/botanjs/src/externs/Components.Vim.Syntax.TokenMatch.js new file mode 100644 index 0000000..916f3cd --- /dev/null +++ b/botanjs/src/externs/Components.Vim.Syntax.TokenMatch.js @@ -0,0 +1,14 @@ +/** @constructor */ +Components.Vim.Syntax.TokenMatch = function(){}; + +/** @type {Components.Vim.Syntax.TokenMatch} */ +Components.Vim.Syntax.TokenMatch.parent; + +/** @type Number */ +Components.Vim.Syntax.TokenMatch.open; +/** @type Number */ +Components.Vim.Syntax.TokenMatch.close; +/** @type Number */ +Components.Vim.Syntax.TokenMatch.level; +/** @type Number */ +Components.Vim.Syntax.TokenMatch.selected; diff --git a/botanjs/src/externs/Components.Vim.Syntax.js b/botanjs/src/externs/Components.Vim.Syntax.js new file mode 100644 index 0000000..97fc80c --- /dev/null +++ b/botanjs/src/externs/Components.Vim.Syntax.js @@ -0,0 +1,2 @@ +/** @object */ +Components.Vim.Syntax = {}; diff --git a/botanjs/src/externs/Components.Vim.VimArea.js b/botanjs/src/externs/Components.Vim.VimArea.js index 982acda..51c9825 100644 --- a/botanjs/src/externs/Components.Vim.VimArea.js +++ b/botanjs/src/externs/Components.Vim.VimArea.js @@ -3,6 +3,8 @@ Components.Vim.VimArea = function(){}; /** @type {Components.Vim.LineFeeder} */ Components.Vim.VimArea.contentFeeder; +/** @type {Components.Vim.Syntax.Analyzer} */ +Components.Vim.VimArea.contentAnalyzer; /** @type {Components.Vim.LineFeeder} */ Components.Vim.VimArea.statusFeeder; /** @type {Components.Vim.StatusBar} */ From 564aef86aa2ae402339328bb52434bd5d59595f1 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, 30 Mar 2016 06:17:37 +0800 Subject: [PATCH 2/9] Previous approach had performance issue, fixing it --- botanjs/src/Components/Vim/Controls.js | 17 ++-- botanjs/src/Components/Vim/Cursor.js | 83 +++++++++++--------- botanjs/src/externs/Components.Vim.Cursor.js | 2 + 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 0909015..44334c6 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -292,18 +292,11 @@ break; } - var d = 1; - var at = bracketMatch.close; - if( bracketMatch.selected == at ) - { - d = -1; - at = bracketMatch.open; - } - - while( ccur.aPos != at ) - { - ccur.moveX( d, true ); - } + ccur.moveTo( + bracketMatch.selected == bracketMatch.close + ? bracketMatch.open + : bracketMatch.close + ); break; case T: // To diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index 240dd10..c39080f 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -88,6 +88,37 @@ // Set by VimArea Cursor.prototype.Vim; + // Move to an absolute position + Cursor.prototype.moveTo = function( aPos ) + { + var content = this.feeder.content; + var lastLineNum = this.getLine().lineNum; + + var expLineNum = 0; + var lineStart = 0; + for( var i = content.indexOf( "\n" ); 0 <= i ; i = content.indexOf( "\n", i ) ) + { + if( aPos <= i ) + { + break; + } + + lineStart = i; + i ++; + expLineNum ++; + } + + var jumpY = expLineNum - lastLineNum; + var jumpX = aPos < lineStart ? lineStart - aPos : aPos - lineStart; + + if( jumpY ) this.moveY( jumpY ); + if( 0 < this.getLine().lineNum && lineStart <= aPos ) jumpX --; + + this.moveX( - Number.MAX_VALUE ); + this.moveX( jumpX ); + + }; + // 0 will be treated as default ( 1 ) Cursor.prototype.moveX = function( d, penetrate, phantomSpace ) { @@ -120,38 +151,12 @@ // Motion includes empty lines before cursor end if( ( phantomSpace && cLen - 1 <= x ) || ( cLen == 1 && c == undefined ) ) { - if( 0 < d ) - { - x = cLen - 1; - if( penetrate ) - { - this.X = 0; - this.moveY( 1 ); - return; - } - } - else - { - x = 0; - } + x = 0 < d ? cLen - 1 : 0; } // ( 2 < cLen ) motion excludes empty lines at cursor end else if( ( 2 <= cLen && x == cLen - 1 && c == " " ) || c == undefined ) { - if( 0 < d ) - { - x = cLen - 2; - if( penetrate ) - { - this.X = 0; - this.moveY( 1 ); - return; - } - } - else - { - x = 0; - } + x = 0 < d ? cLen - 2 : 0; } else if( c == "\n" ) { @@ -199,7 +204,7 @@ this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); }; - Cursor.prototype.moveY = function( d, penetrate ) + Cursor.prototype.moveY = function( d ) { var i; var Y = this.Y + d; @@ -208,7 +213,7 @@ if( Y < 0 ) { - feeder.pan( undefined, d ); + feeder.pan( undefined, Y ); this.Y = 0; this.moveX(); @@ -222,11 +227,7 @@ { var feeder = this.feeder; - if( penetrate ) - { - feeder.pan( undefined, Y - moreAt ); - } - else if( feeder.linesTotal < Y ) + if( feeder.linesTotal < Y ) { while( !feeder.EOF ) { @@ -244,18 +245,22 @@ if( !feeder.EOF ) feeder.pan( undefined, lineShift ); + // The line number cursor need to be in + Y = thisLine + d; + // if it turns out to be the same line + // OR the cursor can not reside on the needed line // before after panning // we keep scrolling it ( panning ) // until the entire line cosumes the screen - while( !feeder.EOF && feeder.lastBuffer.lineNum == lastLine ) + while( !feeder.EOF && ( + feeder.lastBuffer.lineNum == lastLine + || feeder.lastBuffer.lineNum < Y + ) ) { feeder.pan( undefined, 1 ); } - // The line number cursor need to be in - Y = thisLine + d; - i = this.Y; this.Y = 0; // Calculate the visual line position "i" diff --git a/botanjs/src/externs/Components.Vim.Cursor.js b/botanjs/src/externs/Components.Vim.Cursor.js index 2f42cdb..8cf84f8 100644 --- a/botanjs/src/externs/Components.Vim.Cursor.js +++ b/botanjs/src/externs/Components.Vim.Cursor.js @@ -10,6 +10,8 @@ Components.Vim.Cursor.action; /** @type {Components.Vim.State.Recorder} */ Components.Vim.Cursor.rec; +/** @type Function */ +Components.Vim.Cursor.moveTo; /** @type Function */ Components.Vim.Cursor.moveX; /** @type Function */ From a7d2e80f4a6acbeec8f553ef65cd4882140f700a 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, 30 Mar 2016 08:39:13 +0800 Subject: [PATCH 3/9] Some boundary foundations --- botanjs/src/Components/Vim/Controls.js | 6 +- botanjs/src/Components/Vim/Syntax/Analyzer.js | 21 ++ botanjs/src/Components/Vim/Syntax/Word.js | 255 ++++++++++++++++++ .../externs/Components.Vim.Syntax.Analyzer.js | 2 + .../src/externs/Components.Vim.Syntax.Word.js | 5 + 5 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 botanjs/src/Components/Vim/Syntax/Word.js create mode 100644 botanjs/src/externs/Components.Vim.Syntax.Word.js diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 44334c6..76f9bd0 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -313,7 +313,11 @@ this.__cMovement = true; // Word boundary this.__comp( kCode, function(){ - debug.Info( "Word boundary" ); + var WordMatch = analyzer.wordAt( ccur.aPos ); + + debug.Info( "Word: " + + ccur.feeder.content.substring( WordMatch.open, WordMatch.close + 1 ) + ); }, W ); this.__comp( kCode, function(){ debug.Info( "Bracket boundary [" ); diff --git a/botanjs/src/Components/Vim/Syntax/Analyzer.js b/botanjs/src/Components/Vim/Syntax/Analyzer.js index b21aace..c328f59 100644 --- a/botanjs/src/Components/Vim/Syntax/Analyzer.js +++ b/botanjs/src/Components/Vim/Syntax/Analyzer.js @@ -4,6 +4,9 @@ /** @type {System.Debug} */ var debug = __import( "System.Debug" ); + /** @type {Components.Vim.Syntax.Word} */ + var Word = ns[ NS_INVOKE ]( "Word" ); + var TOK_OPEN = 0; var TOK_CLOSED = 1; var TOK_LEVEL = 2; @@ -247,6 +250,24 @@ } }; + Analyzer.prototype.wordAt = function( p ) + { + var c = this.__feeder.content; + var Len = c.length; + var i = p, j = p; + + var word = new Word( c[ p ] ); + + if( 0 < p ) while( word.test( c[ -- i ] ) ); + if( p < Len ) while( word.test( c[ ++ j ] ) ); + + var tMatch = new TokenMatch(); + tMatch.__open = i + 1; + tMatch.__close = j - 1; + + return tMatch; + }; + var TokenMatch = function() { this.__open = -1; diff --git a/botanjs/src/Components/Vim/Syntax/Word.js b/botanjs/src/Components/Vim/Syntax/Word.js new file mode 100644 index 0000000..7283d97 --- /dev/null +++ b/botanjs/src/Components/Vim/Syntax/Word.js @@ -0,0 +1,255 @@ +(function(){ + var ns = __namespace( "Components.Vim.Syntax" ); + + var KINGDOMS = [ + [ // Numbers + [ 0x0030, 0x0039 ] + ] + , + [ // Latin + [ 0x0041, 0x005A ], [ 0x0061, 0x007A ] // Basic Latin + , [ 0x00C0, 0x00FF ] // Latin-1 Supplement + , [ 0x0100, 0x017F ] // Latin Extended-A + , [ 0x0180, 0x024F ] // Latin Extended-B + // Latin-2 supplement + , [ 0x1D00, 0x1D7F ] // Phonetic Extensions + , [ 0x1D80, 0x1DBF ] // Phonetic Extensions Supplement + , [ 0x1DC0, 0x1DFF ] // Combining Diacritical Marks Supplement + , [ 0x1E00, 0x1EFF ] // Latin extended additional + , [ 0x1F00, 0x1FFF ] // Greek Extended + ] + , + [ + , [ 0x0250, 0x02AF ] // IPA Extensions + , [ 0x02B0, 0x02FF ] // Spacing Modifier Letters + , [ 0x0300, 0x036F ] // Combining Diacritical Marks + , [ 0x0370, 0x03FF ] // Greek and Coptic + , [ 0x0400, 0x04FF ] // Cyrillic + , [ 0x0500, 0x052F ] // Cyrillic Supplement + , [ 0x0530, 0x058F ] // Armenian + ] + , + [ // Aramaic Scripts + [ 0x0590, 0x05FF ] // Hebrew + , [ 0x0600, 0x06FF ] // Arabic + , [ 0x0700, 0x074F ] // Syriac + , [ 0x0750, 0x077F ] // Arabic Supplement + , [ 0x0780, 0x07BF ] // Thaana + , [ 0x07C0, 0x07FF ] // N'Ko + , [ 0x0800, 0x083F ] // Samaritan + , [ 0x0840, 0x085F ] // Mandaic + , [ 0x08A0, 0x08FF ] // Arabic Extended-A + ] + , + [ // Brahmic scripts + [ 0x0900, 0x097F ] // Devanagari + , [ 0x0980, 0x09FF ] // Bengali + , [ 0x0A00, 0x0A7F ] // Gurmukhi + , [ 0x0A80, 0x0AFF ] // Gujarati + , [ 0x0B00, 0x0B7F ] // Oriya + , [ 0x0B80, 0x0BFF ] // Tamil + , [ 0x0C00, 0x0C7F ] // Telugu + , [ 0x0C80, 0x0CFF ] // Kannada + , [ 0x0D00, 0x0D7F ] // Malayalam + , [ 0x0D80, 0x0DFF ] // Sinhala + , [ 0x0E00, 0x0E7F ] // Thai + , [ 0x0E80, 0x0EFF ] // Lao + , [ 0x0F00, 0x0FFF ] // Tibetan + , [ 0x1000, 0x109F ] // Myanmar + , [ 0x10A0, 0x10FF ] // Georgian + , [ 0x1100, 0x11FF ] // Hangul Jamo + , [ 0x1200, 0x137F ] // Ethiopic + , [ 0x1380, 0x139F ] // Ethiopic Supplement + , [ 0x13A0, 0x13FF ] // Cherokee + , [ 0x1400, 0x167F ] // Unified Canadian Aboriginal Syllabics + , [ 0x1680, 0x169F ] // Ogham + , [ 0x16A0, 0x16FF ] // Runic + ] + , + [ // Philippine scripts + [ 0x1700, 0x171F ] // Tagalog + , [ 0x1720, 0x173F ] // Hanunoo + , [ 0x1740, 0x175F ] // Buhid + , [ 0x1760, 0x177F ] // Tagbanwa + , [ 0x1780, 0x17FF ] // Khmer + , [ 0x1800, 0x18AF ] // Mongolian + , [ 0x18B0, 0x18FF ] // Unified Canadian Aboriginal Syllabics Extended + , [ 0x1900, 0x194F ] // Limbu + ] + , + [ // Tai scripts + [ 0x1950, 0x197F ] // Tai Le + , [ 0x1980, 0x19DF ] // Tai Lue + , [ 0x19E0, 0x19FF ] // Khmer Symbols + , [ 0x1A00, 0x1A1F ] // Buginese + , [ 0x1A20, 0x1AAF ] // Tai Tham + , [ 0x1AB0, 0x1AFF ] // Combining Diacritical Marks Extended + , [ 0x1B00, 0x1B7F ] // Balinese + , [ 0x1B80, 0x1BBF ] // Sundanese + , [ 0x1BC0, 0x1BFF ] // Batak + , [ 0x1C00, 0x1C4F ] // Lepcha + , [ 0x1C50, 0x1C7F ] // Ol Chiki + , [ 0x1CC0, 0x1CCF ] // Sundanese Supplement + , [ 0x1CD0, 0x1CFF ] // Vedic Extensions + ] + , + [ // Symbols + [ 0x2000, 0x206F ] // General Punctuation + , [ 0x2070, 0x209F ] // Superscripts and Subscripts + , [ 0x20A0, 0x20CF ] // Currency Symbols + , [ 0x20D0, 0x20FF ] // Combining Diacritical Marks for Symbols + , [ 0x2100, 0x214F ] // Letterlike Symbols + , [ 0x2150, 0x218F ] // Number Forms + , [ 0x2190, 0x21FF ] // Arrows + , [ 0x2200, 0x22FF ] // Mathematical Operators + , [ 0x2300, 0x23FF ] // Miscellaneous Technical + , [ 0x2400, 0x243F ] // Control Pictures + , [ 0x2440, 0x245F ] // Optical Character Recognition + , [ 0x2460, 0x24FF ] // Enclosed Alphanumerics + , [ 0x2500, 0x257F ] // Box Drawing + , [ 0x2580, 0x259F ] // Block Elements + , [ 0x25A0, 0x25FF ] // Geometric Shapes + , [ 0x2600, 0x26FF ] // Miscellaneous Symbols + , [ 0x2700, 0x27BF ] // Dingbats + , [ 0x27C0, 0x27EF ] // Miscellaneous Mathematical Symbols-A + , [ 0x27F0, 0x27FF ] // Supplemental Arrows-A + , [ 0x2800, 0x28FF ] // Braille Patterns + , [ 0x2900, 0x297F ] // Supplemental Arrows-B + , [ 0x2980, 0x29FF ] // Miscellaneous Mathematical Symbols-B + , [ 0x2A00, 0x2AFF ] // Supplemental Mathematical Operators + , [ 0x2B00, 0x2BFF ] // Miscellaneous Symbols and Arrows + , [ 0x2C00, 0x2C5F ] // Glagolitic + , [ 0x2C60, 0x2C7F ] // Latin Extended-C + , [ 0x2C80, 0x2CFF ] // Coptic + , [ 0x2D00, 0x2D2F ] // Georgian Supplement + , [ 0x2D30, 0x2D7F ] // Tifinagh + , [ 0x2D80, 0x2DDF ] // Ethiopic Extended + , [ 0x2DE0, 0x2DFF ] // Cyrillic Extended-A + , [ 0x2E00, 0x2E7F ] // Supplemental Punctuation + ] + , + [ // CJK scripts and symbols + , [ 0x2E80, 0x2EFF ] // CJK Radicals Supplement + , [ 0x2F00, 0x2FDF ] // Kangxi Radicals + , [ 0x2FF0, 0x2FFF ] // Ideographic Description Characters + , [ 0x3000, 0x303F ] // CJK Symbols and Punctuation + , [ 0x3040, 0x309F ] // Hiragana + , [ 0x30A0, 0x30FF ] // Katakana + , [ 0x3100, 0x312F ] // Bopomofo + , [ 0x3130, 0x318F ] // Hangul Compatibility Jamo + , [ 0x3190, 0x319F ] // Kanbun + , [ 0x31A0, 0x31BF ] // Bopomofo Extended + , [ 0x31C0, 0x31EF ] // CJK Strokes + , [ 0x31F0, 0x31FF ] // Katakana Phonetic Extensions + , [ 0x3200, 0x32FF ] // Enclosed CJK Letters and Months + , [ 0x3300, 0x33FF ] // CJK Compatibility + , [ 0x3400, 0x4DBF ] // CJK Unified Ideographs Extension A + , [ 0x4DC0, 0x4DFF ] // Yijing Hexagram Symbols + , [ 0x4E00, 0x9FFF ] // CJK Unified Ideographs + , [ 0xA000, 0xA48F ] // Yi Syllables + , [ 0xA490, 0xA4CF ] // Yi Radicals + , [ 0xA4D0, 0xA4FF ] // Lisu + , [ 0xA500, 0xA63F ] // Vai + , [ 0xA640, 0xA69F ] // Cyrillic Extended-B + , [ 0xA6A0, 0xA6FF ] // Bamum + , [ 0xA700, 0xA71F ] // Modifier Tone Letters + , [ 0xA720, 0xA7FF ] // Latin Extended-D + , [ 0xA800, 0xA82F ] // Syloti Nagri + , [ 0xA830, 0xA83F ] // Common Indic Number Forms + , [ 0xA840, 0xA87F ] // Phags-pa + , [ 0xA880, 0xA8DF ] // Saurashtra + , [ 0xA8E0, 0xA8FF ] // Devanagari Extended + , [ 0xA900, 0xA92F ] // Kayah Li + , [ 0xA930, 0xA95F ] // Rejang + , [ 0xA960, 0xA97F ] // Hangul Jamo Extended-A + , [ 0xA980, 0xA9DF ] // Javanese + , [ 0xA9E0, 0xA9FF ] // Myanmar Extended-B + , [ 0xAA00, 0xAA5F ] // Cham + , [ 0xAA60, 0xAA7F ] // Myanmar Extended-A + , [ 0xAA80, 0xAADF ] // Tai Viet + , [ 0xAAE0, 0xAAFF ] // Meetei Mayek Extensions + , [ 0xAB00, 0xAB2F ] // Ethiopic Extended-A + , [ 0xAB30, 0xAB6F ] // Latin Extended-E + , [ 0xAB70, 0xABBF ] // Cherokee Supplement + , [ 0xABC0, 0xABFF ] // Meetei Mayek + , [ 0xAC00, 0xD7AF ] // Hangul Syllables + , [ 0xD7B0, 0xD7FF ] // Hangul Jamo Extended-B + ] + , + [ // Surrogates + , [ 0xD800, 0xDBFF ] // High Surrogates + , [ 0xDC00, 0xDFFF ] // Low Surrogates + , [ 0xE000, 0xF8FF ] // Private Use Area + , [ 0xF900, 0xFAFF ] // CJK Compatibility Ideographs + , [ 0xFB00, 0xFB4F ] // Alphabetic Presentation Forms + , [ 0xFB50, 0xFDFF ] // Arabic Presentation Forms-A + , [ 0xFE00, 0xFE0F ] // Variation Selectors + , [ 0xFE10, 0xFE1F ] // Vertical Forms + , [ 0xFE20, 0xFE2F ] // Combining Half Marks + , [ 0xFE30, 0xFE4F ] // CJK Compatibility Forms + , [ 0xFE50, 0xFE6F ] // Small Form Variants + , [ 0xFE70, 0xFEFF ] // Arabic Presentation Forms-B + , [ 0xFF00, 0xFFEF ] // Halfwidth and Fullwidth Forms + , [ 0xFFF0, 0xFFFF ] // Specials + ] + , + [ // Symbols + // Basic Latin + [ 0x0021, 0x002F ], [ 0x003A, 0x0040 ], [ 0x005B, 0x0060 ], [ 0x007B, 0x007E ], + // C1 Controls and Latin-1 Supplement (Extended ASCII) + [ 0x00A1, 0x00AC ], [ 0x00AE, 0x00BF ], + ] + ]; + + var NUM_KINGDOM = KINGDOMS.length; + + var START = 0; + var END = 1; + + var GetKingdom = function( Char ) + { + var charCode = Char.charCodeAt( 0 ); + for( var i = 0; i < NUM_KINGDOM; i ++ ) + { + var kingdom = KINGDOMS[ i ]; + var zLen = kingdom.length; + for( var j = 0; j < zLen; j ++ ) + { + var zone = kingdom[ j ]; + if( zone[ START ] <= charCode && charCode <= zone[ END ] ) + { + return kingdom; + } + } + } + + return []; + }; + + var Word = function( p ) + { + this.kingdom = GetKingdom( p ); + }; + + Word.prototype.test = function( p ) + { + if( p == undefined || p == null || !p.charCodeAt ) return false; + + var kingdom = this.kingdom; + var zLen = kingdom.length; + var charCode = p.charCodeAt( 0 ); + + for( var j = 0; j < zLen; j ++ ) + { + var zone = kingdom[ j ]; + if( zone[ START ] <= charCode && charCode <= zone[ END ] ) + { + return true; + } + } + + return false; + }; + + ns[ NS_EXPORT ]( EX_CLASS, "Word", Word ); +})(); diff --git a/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js b/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js index f255903..6be9e80 100644 --- a/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js +++ b/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js @@ -4,4 +4,6 @@ Components.Vim.Syntax.Analyzer = function(){}; /** @type Function */ Components.Vim.Syntax.Analyzer.bracketAt; /** @type Function */ +Components.Vim.Syntax.Analyzer.wordAt; +/** @type Function */ Components.Vim.Syntax.Analyzer.quoteAt; diff --git a/botanjs/src/externs/Components.Vim.Syntax.Word.js b/botanjs/src/externs/Components.Vim.Syntax.Word.js new file mode 100644 index 0000000..a448be5 --- /dev/null +++ b/botanjs/src/externs/Components.Vim.Syntax.Word.js @@ -0,0 +1,5 @@ +/** @constructor */ +Components.Vim.Syntax.Word = function(){}; + +/** @type Function */ +Components.Vim.Syntax.Word.test; From 9faf2b492184cd4421ed155ccc612b0500655db7 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, 30 Mar 2016 20:11:48 +0800 Subject: [PATCH 4/9] InputEvent carrying range --- botanjs/src/Components/Vim/Controls.js | 40 ++++++++++++++----- botanjs/src/Components/Vim/Syntax/Analyzer.js | 2 +- botanjs/src/Components/Vim/Syntax/Word.js | 8 ++-- .../Components.Vim.Controls.InputEvent.js | 2 + 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 76f9bd0..29e86c4 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -128,7 +128,7 @@ this.__ccur = this.__cfeeder.cursor; }; - Controls.prototype.__comp = function( kCode, handler ) + Controls.prototype.__comp = function( e, handler ) { if( handler ) { @@ -141,6 +141,8 @@ return true; } + var kCode = e.keyCode; + for( var i = 0; i < this.__compReg.length; i ++ ) { var compReg = this.__compReg[i]; @@ -150,7 +152,7 @@ { if( compReg.i == keys.length ) { - compReg.handler(); + compReg.handler( e ); this.__compReg = null; this.__cMovement = false; } @@ -249,7 +251,7 @@ { if( !e.ModKeys ) { - this.__comp( kCode ); + this.__comp( e ); return true; } } @@ -312,23 +314,26 @@ this.__cMovement = true; // Word boundary - this.__comp( kCode, function(){ + this.__comp( e, function( e2 ) { var WordMatch = analyzer.wordAt( ccur.aPos ); debug.Info( "Word: " + ccur.feeder.content.substring( WordMatch.open, WordMatch.close + 1 ) ); + + e2.__range = WordMatch; + }, W ); - this.__comp( kCode, function(){ + this.__comp( e, function(){ debug.Info( "Bracket boundary [" ); }, S_BRACKET_L ); - this.__comp( kCode, function(){ + this.__comp( e, function(){ debug.Info( "Bracket boundary ]" ); }, S_BRACKET_R ); - this.__comp( kCode, function(){ + this.__comp( e, function(){ debug.Info( "Bracket boundary {" ); }, SHIFT + S_BRACKET_L ); - this.__comp( kCode, function(){ + this.__comp( e, function(){ debug.Info( "Bracket boundary }" ); analyzer.bracketAt( ccur.aPos ); }, SHIFT + S_BRACKET_R ); @@ -336,11 +341,11 @@ case G: // Go to top this.__cMovement = true; - this.__comp( kCode, function(){ + this.__comp( e, function(){ ccur.moveY( -Number.MAX_VALUE ); ccur.moveX( -Number.MAX_VALUE, true ); }, G ); - this.__comp( kCode, function(){ + this.__comp( e, function(){ ccur.openRunAction( "PRINT_HEX", e ); }, _8 ); break; @@ -420,6 +425,8 @@ this.__modKeys = c == KEY_SHIFT || c == KEY_CTRL || c == KEY_ALT; this.__key = e.key; + + this.__range = null; }; __readOnly( InputEvent.prototype, "target", function() { return this.__target; } ); @@ -428,6 +435,19 @@ __readOnly( InputEvent.prototype, "ModKeys", function() { return this.__modKeys; } ); __readOnly( InputEvent.prototype, "Escape", function() { return this.__escape; } ); + __readOnly( InputEvent.prototype, "range", function() { + + /** @type {Components.Vim.Syntax.TokenMatch} */ + var r = this.__range; + + if( r && r.open == -1 && r.close == -1 ) + { + return null; + } + + return r; + } ); + InputEvent.prototype.kMap = function( map ) { return this.__kCode == Map( map ); diff --git a/botanjs/src/Components/Vim/Syntax/Analyzer.js b/botanjs/src/Components/Vim/Syntax/Analyzer.js index c328f59..cb816cf 100644 --- a/botanjs/src/Components/Vim/Syntax/Analyzer.js +++ b/botanjs/src/Components/Vim/Syntax/Analyzer.js @@ -262,7 +262,7 @@ if( p < Len ) while( word.test( c[ ++ j ] ) ); var tMatch = new TokenMatch(); - tMatch.__open = i + 1; + tMatch.__open = 0 < i ? i + 1 : 0; tMatch.__close = j - 1; return tMatch; diff --git a/botanjs/src/Components/Vim/Syntax/Word.js b/botanjs/src/Components/Vim/Syntax/Word.js index 7283d97..d879e59 100644 --- a/botanjs/src/Components/Vim/Syntax/Word.js +++ b/botanjs/src/Components/Vim/Syntax/Word.js @@ -20,7 +20,7 @@ ] , [ - , [ 0x0250, 0x02AF ] // IPA Extensions + [ 0x0250, 0x02AF ] // IPA Extensions , [ 0x02B0, 0x02FF ] // Spacing Modifier Letters , [ 0x0300, 0x036F ] // Combining Diacritical Marks , [ 0x0370, 0x03FF ] // Greek and Coptic @@ -129,7 +129,7 @@ ] , [ // CJK scripts and symbols - , [ 0x2E80, 0x2EFF ] // CJK Radicals Supplement + [ 0x2E80, 0x2EFF ] // CJK Radicals Supplement , [ 0x2F00, 0x2FDF ] // Kangxi Radicals , [ 0x2FF0, 0x2FFF ] // Ideographic Description Characters , [ 0x3000, 0x303F ] // CJK Symbols and Punctuation @@ -177,7 +177,7 @@ ] , [ // Surrogates - , [ 0xD800, 0xDBFF ] // High Surrogates + [ 0xD800, 0xDBFF ] // High Surrogates , [ 0xDC00, 0xDFFF ] // Low Surrogates , [ 0xE000, 0xF8FF ] // Private Use Area , [ 0xF900, 0xFAFF ] // CJK Compatibility Ideographs @@ -197,7 +197,7 @@ // Basic Latin [ 0x0021, 0x002F ], [ 0x003A, 0x0040 ], [ 0x005B, 0x0060 ], [ 0x007B, 0x007E ], // C1 Controls and Latin-1 Supplement (Extended ASCII) - [ 0x00A1, 0x00AC ], [ 0x00AE, 0x00BF ], + [ 0x00A1, 0x00AC ], [ 0x00AE, 0x00BF ] ] ]; diff --git a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js index a8e1bdb..afe109d 100644 --- a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js +++ b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js @@ -3,6 +3,8 @@ Components.Vim.Controls.InputEvent = function(){}; /** @type {Components.Vim.VimArea} */ Components.Vim.Controls.InputEvent.target; +/** @type {Components.Vim.Syntax.TokenMatch} */ +Components.Vim.Controls.InputEvent.range; /** @type String */ Components.Vim.Controls.InputEvent.key; /** @type Boolean */ From 030fb3226bc8abd668d11e24d0de7b19b2ee48b5 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, 30 Mar 2016 21:12:01 +0800 Subject: [PATCH 5/9] Word range viw --- botanjs/src/Components/Vim/Actions/VISUAL.js | 31 ++++++++++++++++---- botanjs/src/Components/Vim/Controls.js | 1 - 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js index 8072e6b..1574d95 100644 --- a/botanjs/src/Components/Vim/Actions/VISUAL.js +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -13,6 +13,16 @@ /** @type {Components.Vim.Cursor.IAction} */ var VISUAL = function( Cursor ) + { + this.__reset( Cursor ); + this.__msg = Mesg( "VISUAL" ); + this.__leaveMesg = ""; + + Cursor.blink = false; + Cursor.pSpace = true; + }; + + VISUAL.prototype.__reset = function( Cursor ) { /** @type {Components.Vim.Cursor} */ this.__cursor = Cursor; @@ -20,11 +30,6 @@ this.__startP = { x: Cursor.X, y: Cursor.Y, p: Cursor.P }; this.__start = Cursor.PStart; this.__selStart = Cursor.PStart; - this.__msg = Mesg( "VISUAL" ); - this.__leaveMesg = ""; - - Cursor.blink = false; - Cursor.pSpace = true; }; VISUAL.prototype.allowMovement = true; @@ -84,6 +89,22 @@ } else { + if( e.range ) + { + cur.suppressEvent(); + + var r = e.range; + + if( cur.aPos == this.__startaP ) + { + cur.moveX( r.open - this.__startaP ); + this.__reset( cur ); + } + + cur.unsuppressEvent(); + cur.moveX( r.close - cur.aPos ); + } + var prevPos = this.__start; var newPos = cur.PStart; diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index 29e86c4..f318060 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -322,7 +322,6 @@ ); e2.__range = WordMatch; - }, W ); this.__comp( e, function(){ debug.Info( "Bracket boundary [" ); From 553cab97763cde3b3c54a324772d17aab3f42ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Thu, 31 Mar 2016 02:25:39 +0800 Subject: [PATCH 6/9] Visual viw, viw{bracket}, Ctrl+f, Ctcl+b --- botanjs/src/Components/Vim/Actions/DELETE.js | 21 +++-- botanjs/src/Components/Vim/Actions/VISUAL.js | 12 +-- botanjs/src/Components/Vim/Controls.js | 65 +++++++++---- botanjs/src/Components/Vim/Cursor.js | 4 +- botanjs/src/Components/Vim/LineFeeder.js | 4 +- botanjs/src/Components/Vim/Syntax/Analyzer.js | 92 ++++++++++++++----- .../externs/Components.Vim.Syntax.Analyzer.js | 2 + 7 files changed, 139 insertions(+), 61 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/DELETE.js b/botanjs/src/Components/Vim/Actions/DELETE.js index 922f703..b509fb5 100644 --- a/botanjs/src/Components/Vim/Actions/DELETE.js +++ b/botanjs/src/Components/Vim/Actions/DELETE.js @@ -41,19 +41,22 @@ var feeder = cur.feeder; var Triggered = false; - if( sp == undefined && this.__startX != cur.aPos ) + + if( sp == undefined ) { - Triggered = true; - - if( e.kMap( "l" ) ) + if( this.__startX != cur.aPos ) { - cur.moveX( -1 ); + Triggered = true; + + if( e.kMap( "l" ) ) + { + cur.moveX( -1 ); + } + + sp = this.__startX; } - - sp = this.__startX; + else return; } - else if( sp == undefined ) return; - var c = feeder.content; diff --git a/botanjs/src/Components/Vim/Actions/VISUAL.js b/botanjs/src/Components/Vim/Actions/VISUAL.js index 1574d95..7cac789 100644 --- a/botanjs/src/Components/Vim/Actions/VISUAL.js +++ b/botanjs/src/Components/Vim/Actions/VISUAL.js @@ -27,7 +27,6 @@ /** @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; }; @@ -71,10 +70,9 @@ // 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; + var o = cur.aPos; + cur.moveTo( this.__startaP, true ); + this.__startaP = o; } Action.handler( e, this.__startaP ); @@ -97,12 +95,12 @@ if( cur.aPos == this.__startaP ) { - cur.moveX( r.open - this.__startaP ); + cur.moveTo( r.open, true ); this.__reset( cur ); } cur.unsuppressEvent(); - cur.moveX( r.close - cur.aPos ); + cur.moveTo( r.close, true ); } var prevPos = this.__start; diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index f318060..950ddbc 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -257,6 +257,8 @@ } var ccur = this.__ccur; + var vima = this.__vimArea; + var cfeeder = ccur.feeder; var cursorHandled = true; switch( kCode ) @@ -267,6 +269,29 @@ case K: this.__cMoveY( -1 ); break; // Up case J: this.__cMoveY( 1 ); break; // Down + case CTRL + F: // Page Down + if( cfeeder.firstBuffer.next.placeholder ) + { + beep(); + break; + } + + var oPan = cfeeder.panY; + cfeeder.pan( undefined, vima.rows - 1 ); + ccur.moveY( -ccur.Y ); + + break; + case CTRL + B: // Page Up + if( cfeeder.panY == 0 ) + { + beep(); + break; + } + cfeeder.pan( undefined, -vima.rows + 1 ); + ccur.moveY( -ccur.Y ); + if( !cfeeder.EOF ) ccur.moveY( cfeeder.moreAt ); + break; + case SHIFT + H: // First line buffer break; case SHIFT + L: // Last line buffer @@ -313,29 +338,33 @@ var analyzer = this.__vimArea.contentAnalyzer; this.__cMovement = true; + // Word boundary this.__comp( e, function( e2 ) { var WordMatch = analyzer.wordAt( ccur.aPos ); - - debug.Info( "Word: " - + ccur.feeder.content.substring( WordMatch.open, WordMatch.close + 1 ) - ); - e2.__range = WordMatch; }, W ); - this.__comp( e, function(){ - debug.Info( "Bracket boundary [" ); - }, S_BRACKET_L ); - this.__comp( e, function(){ - debug.Info( "Bracket boundary ]" ); - }, S_BRACKET_R ); - this.__comp( e, function(){ - debug.Info( "Bracket boundary {" ); - }, SHIFT + S_BRACKET_L ); - this.__comp( e, function(){ - debug.Info( "Bracket boundary }" ); - analyzer.bracketAt( ccur.aPos ); - }, SHIFT + S_BRACKET_R ); + + var bracket = function( e2 ) { + var BracketMatch = analyzer.bracketIn( "(", ccur.aPos ); + e2.__range = BracketMatch; + }; + var curlyBracket = function( e2 ) { + var BracketMatch = analyzer.bracketIn( "{", ccur.aPos ); + e2.__range = BracketMatch; + }; + var squareBracket = function( e2 ) { + var BracketMatch = analyzer.bracketIn( "[", ccur.aPos ); + e2.__range = BracketMatch; + }; + + // Bracket boundaries + this.__comp( e, bracket , SHIFT + _0 ); + this.__comp( e, bracket, SHIFT + _9 ); + this.__comp( e, squareBracket, S_BRACKET_L ); + this.__comp( e, squareBracket, S_BRACKET_R ); + this.__comp( e, curlyBracket, SHIFT + S_BRACKET_L ); + this.__comp( e, curlyBracket, SHIFT + S_BRACKET_R ); break; case G: // Go to top diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index c39080f..47b10ad 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -89,7 +89,7 @@ Cursor.prototype.Vim; // Move to an absolute position - Cursor.prototype.moveTo = function( aPos ) + Cursor.prototype.moveTo = function( aPos, phantomSpace ) { var content = this.feeder.content; var lastLineNum = this.getLine().lineNum; @@ -115,7 +115,7 @@ if( 0 < this.getLine().lineNum && lineStart <= aPos ) jumpX --; this.moveX( - Number.MAX_VALUE ); - this.moveX( jumpX ); + this.moveX( jumpX, false, phantomSpace ); }; diff --git a/botanjs/src/Components/Vim/LineFeeder.js b/botanjs/src/Components/Vim/LineFeeder.js index e50f4ac..b27779f 100644 --- a/botanjs/src/Components/Vim/LineFeeder.js +++ b/botanjs/src/Components/Vim/LineFeeder.js @@ -149,7 +149,9 @@ var a = this.content.indexOf( "\n", f + 1 ); if( a == -1 ) { - Y = i; + Y = i - 1; + // -2 to compensate the last "\n" content placeholder + f -= 2; break; } f = a; diff --git a/botanjs/src/Components/Vim/Syntax/Analyzer.js b/botanjs/src/Components/Vim/Syntax/Analyzer.js index cb816cf..131ec75 100644 --- a/botanjs/src/Components/Vim/Syntax/Analyzer.js +++ b/botanjs/src/Components/Vim/Syntax/Analyzer.js @@ -117,6 +117,24 @@ }; /* End Private Class }}}*/ + var SetParent = function( BracketPairs, pair ) + { + if( !pair ) throw new Error( "Parent not found" ); + + var tMatch = new TokenMatch(); + tMatch.__level = pair[ TOK_LEVEL ]; + tMatch.__open = pair[ TOK_OPEN ]; + tMatch.__close = pair[ TOK_CLOSED ]; + + if( -1 < pair[ TOK_PARENT ] ) + { + var rPair = BracketPairs.find( pair[ TOK_PARENT ] ); + tMatch.__parent = SetParent( BracketPairs, rPair ); + } + + return tMatch; + }; + var Analyzer = function( feeder ) { /* @type {Components.Vim.LineFeeder} */ @@ -136,17 +154,17 @@ { case "{": tokState = TOK_OPEN; case "}": - BracketPairs = this.GetPairs( TOK_JOIN( "{", "}" ) ); + BracketPairs = this.__getPairs( TOK_JOIN( "{", "}" ) ); break; case "[": tokState = TOK_OPEN; case "]": - BracketPairs = this.GetPairs( TOK_JOIN( "[", "]" ) ); + BracketPairs = this.__getPairs( TOK_JOIN( "[", "]" ) ); break; case "(": tokState = TOK_OPEN; case ")": - BracketPairs = this.GetPairs( TOK_JOIN( "(", ")" ) ); + BracketPairs = this.__getPairs( TOK_JOIN( "(", ")" ) ); break; case "/": @@ -186,39 +204,65 @@ { case "/*": tokState = TOK_OPEN; case "*/": - BracketPairs = this.GetPairs( TOK_JOIN( "/*", "*/" ) ); + BracketPairs = this.__getPairs( TOK_JOIN( "/*", "*/" ) ); break; default: return new TokenMatch(); } - var SetParent = function( pair ) - { - if( !pair ) throw new Error( "Parent not found" ); - - var tMatch = new TokenMatch(); - tMatch.__level = pair[ TOK_LEVEL ]; - tMatch.__open = pair[ TOK_OPEN ]; - tMatch.__close = pair[ TOK_CLOSED ]; - - if( -1 < pair[ TOK_PARENT ] ) - { - var rPair = BracketPairs.find( pair[ TOK_PARENT ] ); - tMatch.__parent = SetParent( rPair ); - } - - return tMatch; - }; - var rPair = BracketPairs.find( p, tokState ); - var tMatch = SetParent( rPair ) + var tMatch = SetParent( BracketPairs, rPair ) tMatch.__selected = p; return tMatch; }; - Analyzer.prototype.GetPairs = function( def, reload ) + Analyzer.prototype.bracketIn = function( b, p ) + { + var bro = "{[("; + var brc = "}])"; + + var i = bro.indexOf( b ); + if( i < 0 ) i = brc.indexOf( b ); + if( i < 0 ) throw new Error( "Unsupported bracket: " + b ); + + var tokPairs = this.__getPairs( TOK_JOIN( bro[i], brc[i] ) ); + var pairs = tokPairs.__pairs; + + var l = pairs.length; + + var highest = null; + + // Find the range of highest level + for( var i = 0; i < l; i ++ ) + { + var pair = pairs[ i ]; + + if( pair[ TOK_OPEN ] <= p && p <= pair[ TOK_CLOSED ] ) + { + if( ( highest && highest[ TOK_LEVEL ] < pair[ TOK_LEVEL ] ) || !highest ) + { + highest = pair; + } + } + + } + + var tMatch = SetParent( tokPairs, highest ); + var oMatch = tMatch; + + do { + oMatch.__open ++; + oMatch.__close --; + } while( oMatch = oMatch.parent ) + + if( highest ) return tMatch; + + 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 6be9e80..47a3b3b 100644 --- a/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js +++ b/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js @@ -4,6 +4,8 @@ Components.Vim.Syntax.Analyzer = function(){}; /** @type Function */ Components.Vim.Syntax.Analyzer.bracketAt; /** @type Function */ +Components.Vim.Syntax.Analyzer.bracketIn; +/** @type Function */ Components.Vim.Syntax.Analyzer.wordAt; /** @type Function */ Components.Vim.Syntax.Analyzer.quoteAt; From 1bc1a90b31fc3e4c308b318f6fdd1c72bbdcb814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Thu, 31 Mar 2016 02:51:42 +0800 Subject: [PATCH 7/9] Fixed a -> Enter -> BS -> Esc -> u inconsistency --- botanjs/src/Components/Vim/Actions/INSERT.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/botanjs/src/Components/Vim/Actions/INSERT.js b/botanjs/src/Components/Vim/Actions/INSERT.js index e67f083..7eb38eb 100644 --- a/botanjs/src/Components/Vim/Actions/INSERT.js +++ b/botanjs/src/Components/Vim/Actions/INSERT.js @@ -39,8 +39,8 @@ INSERT.prototype.dispose = function() { - this.__cursor.moveX( -1 ); this.__rec( "", true ); + this.__cursor.moveX( -1 ); }; INSERT.prototype.__rec = function( c, newRec ) @@ -92,12 +92,13 @@ 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 ); + + this.__insertLength --; } else if( e.kMap( "Del" ) ) { From 79705e9b1404b848cd11bc2801270633e2ee657a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Thu, 31 Mar 2016 03:12:18 +0800 Subject: [PATCH 8/9] Reset analyzed content when needed --- botanjs/src/Components/Vim/Cursor.js | 15 ++++++++++----- botanjs/src/Components/Vim/Syntax/Analyzer.js | 5 +++++ .../src/externs/Components.Vim.Syntax.Analyzer.js | 2 ++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/botanjs/src/Components/Vim/Cursor.js b/botanjs/src/Components/Vim/Cursor.js index 47b10ad..ce66bd8 100644 --- a/botanjs/src/Components/Vim/Cursor.js +++ b/botanjs/src/Components/Vim/Cursor.js @@ -191,10 +191,10 @@ this.PEnd = P + 1; this.__p = P; - this.__fireUpdate(); + this.__visualUpdate(); }; - Cursor.prototype.__fireUpdate = function() + Cursor.prototype.__visualUpdate = function() { if( 0 < this.__suppEvt ) { @@ -313,7 +313,7 @@ this.action = new (Actions[ name ])( this ); this.__pulseMsg = null; - this.__fireUpdate(); + this.__visualUpdate(); }; Cursor.prototype.closeAction = function() @@ -323,7 +323,10 @@ this.__pulseMsg = this.action.getMessage(); this.action = null; - this.__fireUpdate(); + // Reset the analyzed content + this.Vim.contentAnalyzer.reset(); + + this.__visualUpdate(); }; // Open, Run, then close an action @@ -335,7 +338,9 @@ this.__pulseMsg = action.getMessage(); action.dispose(); - this.__fireUpdate(); + this.Vim.contentAnalyzer.reset(); + + this.__visualUpdate(); }; Cursor.prototype.suppressEvent = function() { ++ this.__suppEvt; }; diff --git a/botanjs/src/Components/Vim/Syntax/Analyzer.js b/botanjs/src/Components/Vim/Syntax/Analyzer.js index 131ec75..54014ce 100644 --- a/botanjs/src/Components/Vim/Syntax/Analyzer.js +++ b/botanjs/src/Components/Vim/Syntax/Analyzer.js @@ -277,6 +277,11 @@ return pairs; }; + Analyzer.prototype.reset = function() + { + this.__tokpairs = {}; + }; + Analyzer.prototype.quoteAt = function( p ) { var c = this.__feeder.content; diff --git a/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js b/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js index 47a3b3b..24f603c 100644 --- a/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js +++ b/botanjs/src/externs/Components.Vim.Syntax.Analyzer.js @@ -9,3 +9,5 @@ Components.Vim.Syntax.Analyzer.bracketIn; Components.Vim.Syntax.Analyzer.wordAt; /** @type Function */ Components.Vim.Syntax.Analyzer.quoteAt; +/** @type Function */ +Components.Vim.Syntax.Analyzer.reset; From 27ce6b3e50b43301e01c3d70cac021fe4b503f27 Mon Sep 17 00:00:00 2001 From: tgckpg Date: Thu, 31 Mar 2016 03:17:40 +0800 Subject: [PATCH 9/9] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 649663e..fb2bbb2 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ The below commands are currently working. ( will be updated if I remembered, mig Cursor movements hjkl G +Ctrl + f, Ctrl + b +%$^ INSERT: aA i @@ -18,7 +20,7 @@ gg g8 VISUAL: -v ( hightlight only ) +v ( viw, vi{bracket} ) Undo / Redo: ( might have bugs, please file issue if bugged. But I think I will finish other features first if it is not very critical ) u / Ctrl + r