From 3ed7d2be846607d28ebdc417711d45a1d99b416d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=9F=E9=85=8C=20=E9=B5=AC=E5=85=84?= Date: Sat, 2 Apr 2016 18:35:12 +0800 Subject: [PATCH] History stacks --- botanjs/src/Components/Vim/Controls.js | 15 ++- .../Vim/Ex/{Search.js => Command.js} | 105 ++++++++++++++---- botanjs/src/Components/Vim/State/History.js | 96 ++++++++++++++++ botanjs/src/Components/Vim/VimArea.js | 16 +-- .../Components.Vim.Controls.InputEvent.js | 4 + .../externs/Components.Vim.State.History.js | 9 ++ 6 files changed, 212 insertions(+), 33 deletions(-) rename botanjs/src/Components/Vim/Ex/{Search.js => Command.js} (55%) create mode 100644 botanjs/src/Components/Vim/State/History.js create mode 100644 botanjs/src/externs/Components.Vim.State.History.js diff --git a/botanjs/src/Components/Vim/Controls.js b/botanjs/src/Components/Vim/Controls.js index e129357..631d736 100644 --- a/botanjs/src/Components/Vim/Controls.js +++ b/botanjs/src/Components/Vim/Controls.js @@ -4,8 +4,8 @@ /** @type {System.Debug} */ var debug = __import( "System.Debug" ); - /** @type {Components.Vim.Ex.Search} */ - var ExSearch = __import( "Components.Vim.Ex.Search" ); + /** @type {Components.Vim.Ex.Command} */ + var ExCommand = __import( "Components.Vim.Ex.Command" ); var beep = ns[ NS_INVOKE ]( "Beep" ); @@ -441,10 +441,10 @@ }, _8 ); break; - case SLASH: // "/" Seach movement + case SLASH: // "/" Search movement this.__cMovement = true; - this.__divedCCmd = new ExSearch( ccur ); + this.__divedCCmd = new ExCommand( ccur, "/" ); this.__divedCCmd.handler( e ); break; default: @@ -499,7 +499,8 @@ this.__cMovement = false; this.__divedCCmd = null; } - else return; + + if( e.canceled ) return; } var cfeeder = this.__cfeeder; @@ -545,6 +546,7 @@ var InputEvent = function( sender, e ) { this.__target = sender; + this.__canceled = false; if( typeof( e ) == "string" ) { @@ -572,11 +574,14 @@ this.__range = null; }; + InputEvent.prototype.cancel = function() { this.__canceled = true; }; + __readOnly( InputEvent.prototype, "target", function() { return this.__target; } ); __readOnly( InputEvent.prototype, "key", function() { return this.__key; } ); __readOnly( InputEvent.prototype, "keyCode", function() { return this.__kCode; } ); __readOnly( InputEvent.prototype, "ModKeys", function() { return this.__modKeys; } ); __readOnly( InputEvent.prototype, "Escape", function() { return this.__escape; } ); + __readOnly( InputEvent.prototype, "canceled", function() { return this.__canceled; } ); __readOnly( InputEvent.prototype, "range", function() { diff --git a/botanjs/src/Components/Vim/Ex/Search.js b/botanjs/src/Components/Vim/Ex/Command.js similarity index 55% rename from botanjs/src/Components/Vim/Ex/Search.js rename to botanjs/src/Components/Vim/Ex/Command.js index 169f5a5..cd8600b 100644 --- a/botanjs/src/Components/Vim/Ex/Search.js +++ b/botanjs/src/Components/Vim/Ex/Command.js @@ -8,27 +8,37 @@ /** @type {System.utils.Perf} */ var Perf = __import( "System.utils.Perf" ); - var Mesg = __import( "Components.Vim.Message" ); + /**j@type {Components.Vim.State.History} */ + var History = __import( "Components.Vim.State.History" ); + var Mesg = __import( "Components.Vim.Message" ); + var beep = __import( "Components.Vim.Beep" ); + + // This is for security & privacy concerns? + var ZMap = { + "/": Perf.uuid + , ":" : Perf.uuid + }; /** @type {Components.Vim.Cursor.IAction} */ - var Search = function( Cursor ) + var Command = function( Cursor, Mode ) { var _self = this; + if( !ZMap[ Mode ] ) throw new Error( "Unsupport mode: " + Mode ); /** @type {Components.Vim.Cursor} */ this.__cursor = Cursor; this.__statusBar = Cursor.Vim.statusBar; + this.__mode = Mode; + this.__hist = new History( ZMap[ Mode ] ); + this.__command = []; - this.__blinkId = "ExSearchBlinkCycle" + Perf.uuid; + this.__currentCommand = null; + this.__blinkId = "ExCommandBlinkCycle" + Perf.uuid; this.__curPos = 0; - this.__disp = function() - { - }; - - var feeder = Cursor.feeder; + var feeder = Cursor.feeder; var __blink = false; var __holdBlink = false; @@ -77,7 +87,7 @@ this.__statusBar.override = this.__doBlink; }; - Search.prototype.dispose = function() + Command.prototype.dispose = function() { this.__statusBar.override = null; @@ -86,7 +96,7 @@ feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); }; - Search.prototype.handler = function( e ) + Command.prototype.handler = function( e ) { e.preventDefault(); @@ -96,6 +106,8 @@ var InputKey = null; + var histNav = false; + if( e.kMap( "Tab" ) ) { InputKey = "^I"; @@ -106,8 +118,15 @@ } else if( e.kMap( "BS" ) ) { + if( this.__curPos == 1 && 1 < this.__command.length ) + return false; + this.__command.splice( --this.__curPos, 1 ); - if( this.__command.length == 0 ) return true; + if( this.__command.length == 0 ) + { + e.cancel(); + return true; + } } else if( e.kMap( "Del" ) ) { @@ -115,23 +134,56 @@ } else if( e.kMap( "Enter" ) ) { + this.__process(); return true; } - else if( e.kMap( "Up" ) ) // History navigations - { - } - else if( e.kMap( "Down" ) ) - { - } else if( e.kMap( "Left" ) ) { - if( 0 < this.__curPos ) this.__curPos --; + if( 1 < this.__curPos ) this.__curPos --; } else if( e.kMap( "Right" ) ) { if( this.__curPos < this.__command.length ) this.__curPos ++; } + + // History stepping + else if( histNav = e.kMap( "Up" ) ) // History navigations + { + if( !this.__currentCommand ) + { + this.__currentCommand = this.__command; + } + + var n = this.__hist.prev( this.__currentCommand ); + + if( n ) + { + this.__command = n; + this.__curPos = n.length; + } + else + { + beep(); + } + } + else if( histNav = e.kMap( "Down" ) ) + { + var n = this.__hist.next( this.__currentCommand ); + + if( n ) + { + this.__command = n; + this.__curPos = n.length; + } + else if( this.__currentCommand ) + { + this.__command = this.__currentCommand; + this.__currentCommand = null; + } + + else beep(); + } else { InputKey = e.key; @@ -142,9 +194,22 @@ this.__command.splice( this.__curPos ++, 0, InputKey ); } + if( !histNav ) + { + this.__hist.reset(); + if( this.__currentCommand ) this.__currentCommand = this.__command; + } + + e.cancel(); + var feeder = this.__cursor.feeder; feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); - } + }; - ns[ NS_EXPORT ]( EX_CLASS, "Search", Search ); + Command.prototype.__process = function() + { + this.__hist.push( this.__command ); + }; + + ns[ NS_EXPORT ]( EX_CLASS, "Command", Command ); })(); diff --git a/botanjs/src/Components/Vim/State/History.js b/botanjs/src/Components/Vim/State/History.js new file mode 100644 index 0000000..18c67be --- /dev/null +++ b/botanjs/src/Components/Vim/State/History.js @@ -0,0 +1,96 @@ +(function(){ + var ns = __namespace( "Components.Vim.State" ); + + /** @type {System.Debug} */ + var debug = __import( "System.Debug" ); + + // private static + var Zones = {}; + + var PartialBA = function( a, b ) + { + var l = b.length; + if( a.length < l ) return false; + + for( var i = 0; i < l; i ++ ) + { + if( a[ i ] != b[ i ] ) return false; + } + + return true; + }; + + var ExactAB = function( a, b ) + { + var l = a.length < b.length ? b.length : a.length; + for( var i = 0; i < l; i ++ ) + { + if( a[ i ] != b[ i ] ) return false; + } + + return true; + } + + var History = function( z ) + { + if( !Zones[ z ] ) Zones[ z ] = []; + + this.__pi = 0; + this.__zone = Zones[ z ]; + this.reset(); + }; + + History.prototype.push = function( stack ) + { + if( this.__zone.length + && ExactAB( this.__zone[ this.__zone.length - 1 ], stack ) + ) { + debug.Info( "This is the previous command, skipping" ); + return; + } + this.__zone.push( stack ); + }; + + History.prototype.prev = function( stack ) + { + if( this.__zone.length <= this.__i ) this.reset(); + + while( -1 < this.__i ) + { + var st = this.__zone[ this.__i -- ]; + if( st && PartialBA( st, stack ) ) + { + return st.slice(); + } + } + + return null; + }; + + History.prototype.next = function( stack ) + { + if( this.__i < 0 ) + { + this.__i ++; + this.next( stack ); + } + + while( this.__i < this.__zone.length ) + { + var st = this.__zone[ this.__i ++ ]; + if( st && PartialBA( st, stack ) ) + { + return st.slice(); + } + } + + return null; + }; + + History.prototype.reset = function() + { + this.__i = this.__zone.length - 1; + }; + + ns[ NS_EXPORT ]( EX_CLASS, "History", History ); +})(); diff --git a/botanjs/src/Components/Vim/VimArea.js b/botanjs/src/Components/Vim/VimArea.js index c5480ec..ee76302 100644 --- a/botanjs/src/Components/Vim/VimArea.js +++ b/botanjs/src/Components/Vim/VimArea.js @@ -2,18 +2,18 @@ var ns = __namespace( "Components.Vim" ); /** @type {Dandelion.IDOMElement} */ - var IDOMElement = __import( "Dandelion.IDOMElement" ); + var IDOMElement = __import( "Dandelion.IDOMElement" ); /** @type {System.utils.DataKey} */ - var DataKey = __import( "System.utils.DataKey" ); + var DataKey = __import( "System.utils.DataKey" ); /** @type {System.Cycle} */ - var Cycle = __import( "System.Cycle" ); + var Cycle = __import( "System.Cycle" ); /** @type {System.Debug} */ - var debug = __import( "System.Debug" ); + var debug = __import( "System.Debug" ); /** @type {Components.Vim.State.Registers} */ - var Registers = __import( "Components.Vim.State.Registers" ); + var Registers = __import( "Components.Vim.State.Registers" ); /** @type {Components.Vim.Syntax.Analyzer} */ - var SyntaxAnalyzer = __import( "Components.Vim.Syntax.Analyzer" ); + var SyntaxAnalyzer = __import( "Components.Vim.Syntax.Analyzer" ); /** @type {Components.Vim.LineFeeder} */ var LineFeeder = ns[ NS_INVOKE ]( "LineFeeder" ); @@ -96,7 +96,7 @@ // Content feeder var cfeeder = new LineFeeder( cRange, c ); - var contentAnalyzer = new SyntaxAnalyzer( cfeeder ); + var contentAnalyzer = new SyntaxAnalyzer( cfeeder ); // Feed the contents to content feeder // This "\n" fixes the last line "\n" not displaying @@ -136,7 +136,7 @@ Update(); this.contentFeeder = cfeeder; - this.contentAnalyzer = contentAnalyzer; + this.contentAnalyzer = contentAnalyzer; this.statusFeeder = sfeeder; this.statusBar = statusBar; this.registers = new Registers(); diff --git a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js index afe109d..6678a27 100644 --- a/botanjs/src/externs/Components.Vim.Controls.InputEvent.js +++ b/botanjs/src/externs/Components.Vim.Controls.InputEvent.js @@ -11,7 +11,11 @@ Components.Vim.Controls.InputEvent.key; Components.Vim.Controls.InputEvent.ModKeys; /** @type Boolean */ Components.Vim.Controls.InputEvent.Escape; +/** @type Boolean */ +Components.Vim.Controls.InputEvent.canceled; /** @type Number */ Components.Vim.Controls.InputEvent.keyCode; /** @type Function */ Components.Vim.Controls.InputEvent.kMap; +/** @type Function */ +Components.Vim.Controls.InputEvent.cancel; diff --git a/botanjs/src/externs/Components.Vim.State.History.js b/botanjs/src/externs/Components.Vim.State.History.js new file mode 100644 index 0000000..f28f348 --- /dev/null +++ b/botanjs/src/externs/Components.Vim.State.History.js @@ -0,0 +1,9 @@ +/** @constructor */ +Components.Vim.State.History = function(){}; + +/** @type Function */ +Components.Vim.State.History.push; +/** @type Function */ +Components.Vim.State.History.prev; +/** @type Function */ +Components.Vim.State.History.next;