forked from Botanical/BotanJS
		
	Partial % motion
This commit is contained in:
		@@ -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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 );
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										267
									
								
								botanjs/src/Components/Vim/Syntax/Analyzer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								botanjs/src/Components/Vim/Syntax/Analyzer.js
									
									
									
									
									
										Normal 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 );
 | 
			
		||||
})();
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								botanjs/src/externs/Components.Vim.Syntax.Analyzer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								botanjs/src/externs/Components.Vim.Syntax.Analyzer.js
									
									
									
									
									
										Normal 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;
 | 
			
		||||
							
								
								
									
										14
									
								
								botanjs/src/externs/Components.Vim.Syntax.TokenMatch.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								botanjs/src/externs/Components.Vim.Syntax.TokenMatch.js
									
									
									
									
									
										Normal 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;
 | 
			
		||||
							
								
								
									
										2
									
								
								botanjs/src/externs/Components.Vim.Syntax.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								botanjs/src/externs/Components.Vim.Syntax.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
/** @object */
 | 
			
		||||
Components.Vim.Syntax = {};
 | 
			
		||||
@@ -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} */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user