Some sort of UNDO / REDO ( not functional yet )

This commit is contained in:
斟酌 鵬兄 2016-03-17 04:57:43 +08:00
parent bc3c511c6f
commit f1653727f2
11 changed files with 472 additions and 47 deletions

View File

@ -1,10 +1,12 @@
(function(){
var ns = __namespace( "Components.Vim.Actions" );
var Mesg = __import( "Components.Vim.Message" );
/** @type {Components.Vim.State.Stack} */
var Stack = __import( "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" );
var Translate = function( c )
{
@ -55,13 +57,33 @@
INSERT.prototype.dispose = function()
{
this.__rec( "", true );
};
INSERT.prototype.__storeState = function( c, pos )
INSERT.prototype.__storeState = function()
{
var cur = this.__cursor;
var feeder = cur.feeder;
var insertLength = this.__insertLength;
var contentUndo = this.__contentUndo;
var startPos = this.__startPosition;
if( insertLength < 0 )
{
startPos += insertLength;
insertLength = 0;
}
return function() {
debug.Inf( pos, c );
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;
feeder.pan();
};
};
@ -71,21 +93,67 @@
{
if( this.__stack )
{
var c = this.__content;
// If nothings changed
if( this.__insertLength == 0
&& this.__contentUndo === ""
) return;
this.__stack.store(
this.__storeState( c, this.__startPosition )
this.__storeState()
);
this.__cursor.rec.store( this.__stack );
this.__cursor.rec.record( this.__stack );
}
this.__content = "";
this.__insertLength = 0;
this.__contentUndo = "";
this.__stack = new Stack();
this.__startPosition = ContentPosition( this.__cursor.feeder );
}
this.__content += c;
this.__insertLength += c.length;
};
INSERT.prototype.__specialKey = function( e, inputChar )
{
var cur = this.__cursor;
var feeder = cur.feeder;
switch( e.keyCode )
{
case 8: // Backspace
if( cur.X == 0 ) return;
cur.moveX( -1 );
var f = ContentPosition( feeder );
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 = ContentPosition( feeder );
this.__contentUndo += feeder.content.substr( f, 1 );
this.__insertLength ++;
feeder.content =
feeder.content.substring( 0, f )
+ feeder.content.substring( f + 1 );
break;
default:
// Do nothing
return;
}
feeder.pan();
feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
};
INSERT.prototype.handler = function( e )
@ -93,7 +161,11 @@
e.preventDefault();
var inputChar = Translate( e.key );
if( inputChar.length != 1 ) return;
if( inputChar.length != 1 )
{
this.__specialKey( e, inputChar );
return;
}
var cur = this.__cursor;
var feeder = cur.feeder;

View File

@ -0,0 +1,42 @@
(function(){
var ns = __namespace( "Components.Vim.Actions" );
var Mesg = __import( "Components.Vim.Message" );
/** @type {Components.Vim.Cursor.IAction} */
var REDO = function( Cursor )
{
/** @type {Components.Vim.Cursor} */
this.__cursor = Cursor;
this.__message = "REDO COMMAND";
};
REDO.prototype.dispose = function()
{
};
REDO.prototype.handler = function( e )
{
e.preventDefault();
/** @type {Components.Vim.State.Stack} */
var stack = this.__cursor.rec.redo();
if( stack )
{
stack.play();
this.__message = "<<TODO>>; before #" + stack.id + " " + stack.time;
}
else
{
this.__message = Mesg( "REDO_LIMIT" );
}
};
REDO.prototype.getMessage = function()
{
return this.__message;
};
ns[ NS_EXPORT ]( EX_CLASS, "REDO", REDO );
})();

View File

@ -25,6 +25,7 @@
if( stack )
{
stack.play();
this.__message = "<<TODO>>; before #" + stack.id + " " + stack.time;
}
else
{

View File

@ -3,6 +3,21 @@
var debug = __import( "System.Debug" );
var SHIFT = 1 << 9;
var CTRL = 1 << 10;
var BACKSPACE = 8;
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;
var A = 65; var B = 66; var C = 67; var D = 68; var E = 69;
var F = 70; var G = 71; var H = 72; var I = 73; var J = 74;
var K = 75; var L = 76; var M = 77; var N = 78; var O = 79;
var P = 80; var Q = 81; var R = 82; var S = 83; var T = 84;
var U = 85; var V = 86; var W = 87; var X = 88; var Y = 89;
var Z = 90;
var Controls = function( sender, e )
{
// Neve capture these keys
@ -15,7 +30,7 @@
var cfeeder = sender.contentFeeder;
if( cfeeder.cursor.action )
{
// Esc OR Ctrl+c
// Esc OR Ctrl + c
if( e.keyCode == 27 || ( e.ctrlKey && e.keyCode == 67 ) )
{
e.preventDefault();
@ -29,76 +44,75 @@
}
e.preventDefault();
if( e.ctrlKey )
{
VimComboFunc( sender, e );
return;
}
var kCode = e.keyCode + ( e.shiftKey ? 1000 : 0 );
var kCode = e.keyCode
+ ( e.shiftKey ? SHIFT : 0 )
+ ( e.ctrlKey ? CTRL : 0 );
var cfeeder = sender.contentFeeder;
var sfeeder = sender.statusFeeder;
switch( kCode )
{
// Cursor movements
case 8: // Backspace, go back 1 char, regardless of line
case BACKSPACE: // Backspace, go back 1 char, regardless of line
break;
case 72: // h
case H: // Left
cfeeder.cursor.moveX( -1 );
break;
case 74: // j
cfeeder.cursor.moveY( 1 );
case L: // Right
cfeeder.cursor.moveX( 1 );
break;
case 75: // k
case K: // Up
cfeeder.cursor.moveY( -1 );
break;
case 76: // l
cfeeder.cursor.moveX( 1 );
case J: // Down
cfeeder.cursor.moveY( 1 );
break;
// Insert
case 65: // a
case A: // Append
cfeeder.cursor.moveX( 1 );
cfeeder.cursor.openAction( "INSERT" );
break;
case 73: // i
case I: // Insert
break;
case 85: // u, undo
case U: // Undo
cfeeder.cursor.openRunAction( "UNDO", e );
break;
case 88: // x, del
case CTRL + R: // Redo
cfeeder.cursor.openRunAction( "REDO", e );
break;
case 1065: // A, append at the line end
case X: // Del
break;
case 1088: // X, delete before
case SHIFT + A: // Append at the line end
break;
case 1085: // U, undo previous changes in oneline
case SHIFT + X: // Delete before
break;
case 1073: // I, append before the line start, after spaces
case SHIFT + U: // Undo previous changes in oneline
break;
case SHIFT + I: // Append before the line start, after spaces
break;
// remove characters
case 88: // x, remove in cursor
case X: // Remove in cursor
break;
case 1088: // X, remove before cursor
case SHIFT + X: // Remove before cursor
break;
case 1072: // H, First line buffer
case SHIFT + H: // First line buffer
break;
case 1076: // L, Last line buffer
case SHIFT + L: // Last line buffer
break;
case 1052: // $
case SHIFT + _4: // $, End
cfeeder.cursor.lineEnd();
break;
case 1053: // %
case SHIFT + _5: // %, Find next item
break;
case 1054: // ^
case SHIFT + _6: // ^, Start
cfeeder.cursor.lineStart();
break;
case 1074: // J, Join lines
case SHIFT + J: // Join lines
break;
case 1075: // K, manual entry
case SHIFT + K: // manual entry
break;
case 112: // F1, help
}

View File

@ -0,0 +1,59 @@
(function(){
var ns = __namespace( "Components.Vim.DateTime" );
var messages = {
"AboutAMinuteAgo" : "about a minute ago"
, "AboutAMonthAgo" : "about a month ago"
, "AboutAnHourAgo" : "about an hour ago"
, "AboutAWeekAgo" : "about a week ago"
, "last Friday" : "last Friday"
, "last Monday" : "last Monday"
, "last Saturday" : "last Saturday"
, "last Sunday" : "last Sunday"
, "last Thursday" : "last Thursday"
, "last Tuesday" : "last Tuesday"
, "last Wednesday" : "last Wednesday"
, "on Friday" : "on Friday"
, "on Monday" : "on Monday"
, "on Saturday" : "on Saturday"
, "on Sunday" : "on Sunday"
, "on Thursday" : "on Thursday"
, "on Tuesday" : "on Tuesday"
, "on Wednesday" : "on Wednesday"
, "OverAYearAgo" : "over a year ago"
, "XHoursAgo_2To4" : "%1 hours ago"
, "XHoursAgo_EndsIn1Not11" : "%1 hours ago"
, "XHoursAgo_EndsIn2To4Not12To14" : "%1 hours ago"
, "XHoursAgo_Other" : "%1 hours ago"
, "XMinutesAgo_2To4" : "%1 minutes ago"
, "XMinutesAgo_EndsIn1Not11" : "%1 minutes ago"
, "XMinutesAgo_EndsIn2To4Not12To14" : "%1 minutes ago"
, "XMinutesAgo_Other" : "%1 minutes ago"
, "XMonthsAgo_2To4" : "%1 months ago"
, "XMonthsAgo_5To12" : "%1 months ago"
, "XSecondsAgo_2To4" : "%1 seconds ago"
, "XSecondsAgo_EndsIn1Not11" : "%1 seconds ago"
, "XSecondsAgo_EndsIn2To4Not12To14" : "%1 seconds ago"
, "XSecondsAgo_Other" : "%1 seconds ago"
, "XWeeksAgo_2To4" : "%1 weeks ago"
};
var GetString = function( arr, key, restArgs )
{
if( arr[ key ] == undefined ) return key;
var i = 0;
return arr[ key ].replace( /%\d+/g, function( e )
{
return restArgs[ i ++ ];
} );
};
var DateTimeString = function( key )
{
var restArgs = Array.prototype.slice.call( arguments, 1 );
return GetString( messages, key, restArgs );
};
ns[ NS_EXPORT ]( EX_FUNC, "String", DateTimeString );
})();

View File

@ -0,0 +1,213 @@
(function(){
var ns = __namespace( "Components.Vim.DateTime" );
var Minute = 60;
var Hour = 60 * Minute;
var Day = 24 * Hour;
var Week = 7 * Day;
var Month = 30.5 * Day;
var Year = 365 * Day;
var Mesg = ns[ NS_INVOKE ]( "String" );
var PluralHourStrings = [
"XHoursAgo_2To4",
"XHoursAgo_EndsIn1Not11",
"XHoursAgo_EndsIn2To4Not12To14",
"XHoursAgo_Other"
];
var PluralMinuteStrings = [
"XMinutesAgo_2To4",
"XMinutesAgo_EndsIn1Not11",
"XMinutesAgo_EndsIn2To4Not12To14",
"XMinutesAgo_Other"
];
var PluralSecondStrings = [
"XSecondsAgo_2To4",
"XSecondsAgo_EndsIn1Not11",
"XSecondsAgo_EndsIn2To4Not12To14",
"XSecondsAgo_Other"
];
var DayOfWeek = { Sunday: 0, Monday: 1, Tuesday: 2, Wednesday: 3, Thursday: 4, Friday: 5, Saturday: 6 };
var GetPluralMonth = function( month )
{
if ( month >= 2 && month <= 4 )
{
return Mesg( "XMonthsAgo_2To4", month );
}
else if ( month >= 5 && month <= 12 )
{
return Mesg( "XMonthsAgo_5To12", month );
}
else
{
throw new Error( "Invalid number of Months" );
}
};
var GetLastDayOfWeek = function( dow )
{
var result;
switch ( dow )
{
case DayOfWeek.Monday:
result = Mesg( "last Monday" );
break;
case DayOfWeek.Tuesday:
result = Mesg( "last Tuesday" );
break;
case DayOfWeek.Wednesday:
result = Mesg( "last Wednesday" );
break;
case DayOfWeek.Thursday:
result = Mesg( "last Thursday" );
break;
case DayOfWeek.Friday:
result = Mesg( "last Friday" );
break;
case DayOfWeek.Saturday:
result = Mesg( "last Saturday" );
break;
case DayOfWeek.Sunday:
result = Mesg( "last Sunday" );
break;
default:
result = Mesg( "last Sunday" );
break;
}
return result;
};
var GetOnDayOfWeek = function( dow )
{
var result;
switch( dow )
{
case DayOfWeek.Monday:
result = Mesg( "on Monday" );
break;
case DayOfWeek.Tuesday:
result = Mesg( "on Tuesday" );
break;
case DayOfWeek.Wednesday:
result = Mesg( "on Wednesday" );
break;
case DayOfWeek.Thursday:
result = Mesg( "on Thursday" );
break;
case DayOfWeek.Friday:
result = Mesg( "on Friday" );
break;
case DayOfWeek.Saturday:
result = Mesg( "on Saturday" );
break;
case DayOfWeek.Sunday:
result = Mesg( "on Sunday" );
break;
default:
result = Mesg( "on Sunday" );
break;
}
return result;
};
var GetPluralTimeUnits = function( units, resources )
{
var modTen = units % 10;
var modHundred = units % 100;
if ( units <= 1 )
{
throw new Error( "Invalid number of Time units" );
}
else if ( 2 <= units && units <= 4 )
{
return Mesg( resources[ 0 ], units );
}
else if ( modTen == 1 && modHundred != 11 )
{
return Mesg( resources[ 1 ], units );
}
else if ( ( 2 <= modTen && modTen <= 4 ) && !( 12 <= modHundred && modHundred <= 14 ) )
{
return Mesg( resources[ 2 ], units );
}
else
{
return Mesg( resources[ 3 ], units );
}
};
var RelativeTime = function( given )
{
var diffSecs = Math.round( 0.001 * ( new Date().getTime() - given.getTime() ) );
if( Year < diffSecs )
{
result = Mesg( "OverAYearAgo" );
}
else if( ( 1.5 * Month ) < diffSecs )
{
var nMonths = Math.round( ( diffSecs + Month / 2 ) / Month );
result = GetPluralMonth( nMonths );
}
else if( ( 3.5 * Week ) <= diffSecs )
{
result = Mesg( "AboutAMonthAgo" );
}
else if( Week <= diffSecs )
{
var nWeeks = Math.round( diffSecs / Week );
if ( 1 < nWeeks )
{
result = Mesg( "XWeeksAgo_2To4", nWeeks );
}
else
{
result = Mesg( "AboutAWeekAgo" );
}
}
else if ( ( 5 * Day ) <= diffSecs )
{
result = GetLastDayOfWeek( given.getDay() );
}
else if ( Day <= diffSecs )
{
result = GetOnDayOfWeek( given.getDay() );
}
else if ( ( 2 * Hour ) <= diffSecs )
{
var nHours = Math.round( diffSecs / Hour );
result = GetPluralTimeUnits( nHours, PluralHourStrings );
}
else if ( Hour <= diffSecs )
{
result = Mesg( "AboutAnHourAgo" );
}
else if ( ( 2 * Minute ) <= diffSecs )
{
var nMinutes = Math.round( diffSecs / Minute );
result = GetPluralTimeUnits( nMinutes, PluralMinuteStrings );
}
else if ( Minute <= diffSecs )
{
result = Mesg( "AboutAMinuteAgo" );
}
else
{
var nSeconds = 1 < diffSecs ? diffSecs : 2;
result = GetPluralTimeUnits( nSeconds, PluralSecondStrings );
}
return result;
};
ns[ NS_EXPORT ]( EX_FUNC, "RelativeTime", RelativeTime );
})();

View File

@ -13,7 +13,8 @@
if( i == -1 || !this.__steps.length ) return null;
return this.__steps[ this.__i = i ];
this.__i -= 2;
return this.__steps[ i ];
};
Recorder.prototype.redo = function()
@ -24,7 +25,7 @@
var State = this.__steps[ i ];
if( State )
{
this.__i = i;
this.__i += 2;
return State;
}
@ -34,6 +35,8 @@
Recorder.prototype.record = function( StateObj )
{
this.__steps[ this.__i ] = StateObj;
StateObj.id = this.__i;
delete this.__steps[ ++ this.__i ];
};

View File

@ -1,13 +1,19 @@
(function(){
var ns = __namespace( "Components.Vim.State" );
/** @type {Components.Vim.DateTime} */
var RelativeTime = __import( "Components.Vim.DateTime.RelativeTime" );
var Stack = function()
{
};
Stack.prototype.store = function( handler )
{
this.__handler = handler;
this.__time = new Date();
this.id = 0;
};
Stack.prototype.play = function()
@ -15,5 +21,10 @@
if( this.__handler ) this.__handler();
};
__readOnly( Stack.prototype, "time", function()
{
return RelativeTime( this.__time );
} );
ns[ NS_EXPORT ]( EX_CLASS, "Stack", Stack );
})();

View File

@ -15,6 +15,7 @@
, "EXIT": "Type :quit<Enter> to exit Vim"
, "UNDO_LIMIT": "Already at oldest change"
, "REDO_LIMIT": "Already at newest change"
};
var errors = {

View File

@ -0,0 +1,7 @@
/** @constructor */
Components.Vim.DateTime = function(){};
/** @type Function */
Components.Vim.DateTime.RelativeTime;
/** @type Function */
Components.Vim.DateTime.String;

View File

@ -5,3 +5,5 @@ Components.Vim.State.Stack = function(){};
Components.Vim.State.Stack.play;
/** @type Function */
Components.Vim.State.Stack.store;
/** @type Function */
Components.Vim.State.Stack.time;