lib/utils/action.js
function(str) {
			var curOffset = str.length;
			var startIndex = -1;
			var groupCount = 0;
			var braceCount = 0;
			var textCount = 0;
			
			while (true) {
				curOffset--;
				if (curOffset < 0) {
					// moved to the beginning of the line
					startIndex = 0;
					break;
				}
				
				var ch = str.charAt(curOffset);
				
				if (ch == ']') {
					braceCount++;
				} else if (ch == '[') {
					if (!braceCount) { // unexpected brace
						startIndex = curOffset + 1;
						break;
					}
					braceCount--;
				} else if (ch == '}') {
					textCount++;
				} else if (ch == '{') {
					if (!textCount) { // unexpected brace
						startIndex = curOffset + 1;
						break;
					}
					textCount--;
				} else if (ch == ')') {
					groupCount++;
				} else if (ch == '(') {
					if (!groupCount) { // unexpected brace
						startIndex = curOffset + 1;
						break;
					}
					groupCount--;
				} else {
					if (braceCount || textCount) 
						// respect all characters inside attribute sets or text nodes
						continue;
					else if (!abbreviationParser.isAllowedChar(ch) || (ch == '>' && utils.endsWithTag(str.substring(0, curOffset + 1)))) {
						// found stop symbol
						startIndex = curOffset + 1;
						break;
					}
				}
			}
			
			if (startIndex != -1 && !textCount && !braceCount && !groupCount) 
				// found something, remove some invalid symbols from the 
				// beginning and return abbreviation
				return str.substring(startIndex).replace(/^[\*\+\>\^]+/, '');
			else
				return '';
		}
function(str) {
			var curOffset = str.length;
			var startIndex = -1;
			var groupCount = 0;
			var braceCount = 0;
			var textCount = 0;
			
			while (true) {
				curOffset--;
				if (curOffset < 0) {
					// moved to the beginning of the line
					startIndex = 0;
					break;
				}
				
				var ch = str.charAt(curOffset);
				
				if (ch == ']') {
					braceCount++;
				} else if (ch == '[') {
					if (!braceCount) { // unexpected brace
						startIndex = curOffset + 1;
						break;
					}
					braceCount--;
				} else if (ch == '}') {
					textCount++;
				} else if (ch == '{') {
					if (!textCount) { // unexpected brace
						startIndex = curOffset + 1;
						break;
					}
					textCount--;
				} else if (ch == ')') {
					groupCount++;
				} else if (ch == '(') {
					if (!groupCount) { // unexpected brace
						startIndex = curOffset + 1;
						break;
					}
					groupCount--;
				} else {
					if (braceCount || textCount) 
						// respect all characters inside attribute sets or text nodes
						continue;
					else if (!abbreviationParser.isAllowedChar(ch) || (ch == '>' && utils.endsWithTag(str.substring(0, curOffset + 1)))) {
						// found stop symbol
						startIndex = curOffset + 1;
						break;
					}
				}
			}
			
			if (startIndex != -1 && !textCount && !braceCount && !groupCount) 
				// found something, remove some invalid symbols from the 
				// beginning and return abbreviation
				return str.substring(startIndex).replace(/^[\*\+\>\^]+/, '');
			else
				return '';
		}
function ( str ) { var curOffset = str . length ; var startIndex = - 1 ; var groupCount = 0 ; var braceCount = 0 ; var textCount = 0 ; while ( true ) { curOffset -- ; if ( curOffset < 0 ) { // moved to the beginning of the line startIndex = 0 ; break ; } var ch = str . charAt ( curOffset ) ; if ( ch == ']' ) { braceCount ++ ; } else if ( ch == '[' ) { if ( ! braceCount ) { // unexpected brace startIndex = curOffset + 1 ; break ; } braceCount -- ; } else if ( ch == '}' ) { textCount ++ ; } else if ( ch == '{' ) { if ( ! textCount ) { // unexpected brace startIndex = curOffset + 1 ; break ; } textCount -- ; } else if ( ch == ')' ) { groupCount ++ ; } else if ( ch == '(' ) { if ( ! groupCount ) { // unexpected brace startIndex = curOffset + 1 ; break ; } groupCount -- ; } else { if ( braceCount || textCount ) // respect all characters inside attribute sets or text nodes continue ; else if ( ! abbreviationParser . isAllowedChar ( ch ) || ( ch == '>' && utils . endsWithTag ( str . substring ( 0 , curOffset + 1 ) ) ) ) { // found stop symbol startIndex = curOffset + 1 ; break ; } } } if ( startIndex != - 1 && ! textCount && ! braceCount && ! groupCount ) // found something, remove some invalid symbols from the  // beginning and return abbreviation return str . substring ( startIndex ) . replace ( / ^[\*\+\>\^]+ / , '' ) ; else return '' ; }
Extracts abbreviations from text stream, starting from the end
@param {String} str
@return {String} Abbreviation or empty string
@memberOf emmet.actionUtils
Extracts abbreviations from text stream starting from the end