Partial % motion

This commit is contained in:
斟酌 鵬兄 2016-03-30 04:32:36 +08:00
parent 3f687cb704
commit 799a911e06
8 changed files with 359 additions and 11 deletions

View File

@ -281,6 +281,30 @@
break break
case SHIFT + _5: // %, Find next item 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; break;
case T: // To case T: // To
break; break;
@ -291,6 +315,8 @@
break; break;
} }
var analyzer = this.__vimArea.contentAnalyzer;
this.__cMovement = true; this.__cMovement = true;
// Word boundary // Word boundary
this.__comp( kCode, function(){ this.__comp( kCode, function(){
@ -307,6 +333,7 @@
}, SHIFT + S_BRACKET_L ); }, SHIFT + S_BRACKET_L );
this.__comp( kCode, function(){ this.__comp( kCode, function(){
debug.Info( "Bracket boundary }" ); debug.Info( "Bracket boundary }" );
analyzer.bracketAt( ccur.aPos );
}, SHIFT + S_BRACKET_R ); }, SHIFT + S_BRACKET_R );
break; break;

View File

@ -88,9 +88,8 @@
// Set by VimArea // Set by VimArea
Cursor.prototype.Vim; Cursor.prototype.Vim;
// Can only be 1, -1 // 0 will be treated as default ( 1 )
// 0 will be treated as undefined Cursor.prototype.moveX = function( d, penetrate, phantomSpace )
Cursor.prototype.moveX = function( d, penentrate, phantomSpace )
{ {
var x = this.pX; var x = this.pX;
@ -101,7 +100,7 @@
var buffs = this.feeder.lineBuffers; var buffs = this.feeder.lineBuffers;
if( penentrate ) if( penetrate )
{ {
if( x < 0 && ( 0 < this.feeder.panY || 0 < this.Y ) ) if( x < 0 && ( 0 < this.feeder.panY || 0 < this.Y ) )
{ {
@ -118,15 +117,41 @@
var c = content[ x ]; 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 ) ) 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 ) 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" ) else if( c == "\n" )
{ {
@ -174,7 +199,7 @@
this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) ); this.feeder.dispatcher.dispatchEvent( new BotanEvent( "VisualUpdate" ) );
}; };
Cursor.prototype.moveY = function( d, penentrate ) Cursor.prototype.moveY = function( d, penetrate )
{ {
var i; var i;
var Y = this.Y + d; var Y = this.Y + d;
@ -197,7 +222,7 @@
{ {
var feeder = this.feeder; var feeder = this.feeder;
if( penentrate ) if( penetrate )
{ {
feeder.pan( undefined, Y - moreAt ); feeder.pan( undefined, Y - moreAt );
} }

View File

@ -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 );
})();

View File

@ -11,7 +11,9 @@
var debug = __import( "System.Debug" ); var debug = __import( "System.Debug" );
/** @type {Components.Vim.State.Registers} */ /** @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} */ /** @type {Components.Vim.LineFeeder} */
var LineFeeder = ns[ NS_INVOKE ]( "LineFeeder" ); var LineFeeder = ns[ NS_INVOKE ]( "LineFeeder" );
@ -94,6 +96,7 @@
// Content feeder // Content feeder
var cfeeder = new LineFeeder( cRange, c ); var cfeeder = new LineFeeder( cRange, c );
var contentAnalyzer = new SyntaxAnalyzer( cfeeder );
// Feed the contents to content feeder // Feed the contents to content feeder
// This "\n" fixes the last line "\n" not displaying // This "\n" fixes the last line "\n" not displaying
@ -132,6 +135,7 @@
Update(); Update();
this.contentFeeder = cfeeder; this.contentFeeder = cfeeder;
this.contentAnalyzer = contentAnalyzer;
this.statusFeeder = sfeeder; this.statusFeeder = sfeeder;
this.statusBar = statusBar; this.statusBar = statusBar;
this.registers = new Registers(); this.registers = new Registers();

View File

@ -0,0 +1,7 @@
/** @constructor */
Components.Vim.Syntax.Analyzer = function(){};
/** @type Function */
Components.Vim.Syntax.Analyzer.bracketAt;
/** @type Function */
Components.Vim.Syntax.Analyzer.quoteAt;

View File

@ -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;

View File

@ -0,0 +1,2 @@
/** @object */
Components.Vim.Syntax = {};

View File

@ -3,6 +3,8 @@ Components.Vim.VimArea = function(){};
/** @type {Components.Vim.LineFeeder} */ /** @type {Components.Vim.LineFeeder} */
Components.Vim.VimArea.contentFeeder; Components.Vim.VimArea.contentFeeder;
/** @type {Components.Vim.Syntax.Analyzer} */
Components.Vim.VimArea.contentAnalyzer;
/** @type {Components.Vim.LineFeeder} */ /** @type {Components.Vim.LineFeeder} */
Components.Vim.VimArea.statusFeeder; Components.Vim.VimArea.statusFeeder;
/** @type {Components.Vim.StatusBar} */ /** @type {Components.Vim.StatusBar} */