src/language/XMLUtils.js
function _getTagAttributes(editor, constPos) {
        var pos, ctx, ctxPrev, ctxNext, ctxTemp, tagName, exclusionList = [], shouldReplace;

        pos = $.extend({}, constPos);
        ctx = TokenUtils.getInitialContext(editor._codeMirror, pos);

        // Stop if the cursor is before = or an attribute value.
        ctxTemp = $.extend(true, {}, ctx);
        if (ctxTemp.token.type === null && regexWhitespace.test(ctxTemp.token.string)) {
            if (TokenUtils.moveSkippingWhitespace(TokenUtils.moveNextToken, ctxTemp)) {
                if ((ctxTemp.token.type === null && ctxTemp.token.string === "=") ||
                        ctxTemp.token.type === "string") {
                    return null;
                }
                TokenUtils.moveSkippingWhitespace(TokenUtils.movePrevToken, ctxTemp);
            }
        }

        // Incase an attribute is followed by an equal sign, shouldReplace will be used
        // to prevent from appending ="" again.
        if (ctxTemp.token.type === "attribute") {
            if (TokenUtils.moveSkippingWhitespace(TokenUtils.moveNextToken, ctxTemp)) {
                if (ctxTemp.token.type === null && ctxTemp.token.string === "=") {
                    shouldReplace = true;
                }
            }
        }

        // Look-Back and get the attributes and tag name.
        pos = $.extend({}, constPos);
        ctxPrev = TokenUtils.getInitialContext(editor._codeMirror, pos);
        while (TokenUtils.movePrevToken(ctxPrev)) {
            if (ctxPrev.token.type && ctxPrev.token.type.indexOf("tag bracket") >= 0) {
                // Disallow hints in closed tag and inside tag content
                if (ctxPrev.token.string === "</" || ctxPrev.token.string.indexOf(">") !== -1) {
                    return null;
                }
            }

            // Get attributes.
            if (ctxPrev.token.type === "attribute") {
                exclusionList.push(ctxPrev.token.string);
            }

            // Get tag.
            if (ctxPrev.token.type === "tag") {
                tagName = ctxPrev.token.string;
                if (TokenUtils.movePrevToken(ctxPrev)) {
                    if (ctxPrev.token.type === "tag bracket" && ctxPrev.token.string === "<") {
                        break;
                    }
                    return null;
                }
            }
        }

        // Look-Ahead and find rest of the attributes.
        pos = $.extend({}, constPos);
        ctxNext = TokenUtils.getInitialContext(editor._codeMirror, pos);
        while (TokenUtils.moveNextToken(ctxNext)) {
            if (ctxNext.token.type === "string" && ctxNext.token.string === "\"") {
                return null;
            }

            // Stop on closing bracket of its own tag or opening bracket of next tag.
            if (ctxNext.token.type === "tag bracket" &&
                    (ctxNext.token.string.indexOf(">") >= 0 || ctxNext.token.string === "<")) {
                break;
            }
            if (ctxNext.token.type === "attribute" && exclusionList.indexOf(ctxNext.token.string) === -1) {
                exclusionList.push(ctxNext.token.string);
            }
        }
        return {
            tagName: tagName,
            exclusionList: exclusionList,
            shouldReplace: shouldReplace
        };
    }
function _getTagAttributes(editor, constPos) {
        var pos, ctx, ctxPrev, ctxNext, ctxTemp, tagName, exclusionList = [], shouldReplace;

        pos = $.extend({}, constPos);
        ctx = TokenUtils.getInitialContext(editor._codeMirror, pos);

        // Stop if the cursor is before = or an attribute value.
        ctxTemp = $.extend(true, {}, ctx);
        if (ctxTemp.token.type === null && regexWhitespace.test(ctxTemp.token.string)) {
            if (TokenUtils.moveSkippingWhitespace(TokenUtils.moveNextToken, ctxTemp)) {
                if ((ctxTemp.token.type === null && ctxTemp.token.string === "=") ||
                        ctxTemp.token.type === "string") {
                    return null;
                }
                TokenUtils.moveSkippingWhitespace(TokenUtils.movePrevToken, ctxTemp);
            }
        }

        // Incase an attribute is followed by an equal sign, shouldReplace will be used
        // to prevent from appending ="" again.
        if (ctxTemp.token.type === "attribute") {
            if (TokenUtils.moveSkippingWhitespace(TokenUtils.moveNextToken, ctxTemp)) {
                if (ctxTemp.token.type === null && ctxTemp.token.string === "=") {
                    shouldReplace = true;
                }
            }
        }

        // Look-Back and get the attributes and tag name.
        pos = $.extend({}, constPos);
        ctxPrev = TokenUtils.getInitialContext(editor._codeMirror, pos);
        while (TokenUtils.movePrevToken(ctxPrev)) {
            if (ctxPrev.token.type && ctxPrev.token.type.indexOf("tag bracket") >= 0) {
                // Disallow hints in closed tag and inside tag content
                if (ctxPrev.token.string === "</" || ctxPrev.token.string.indexOf(">") !== -1) {
                    return null;
                }
            }

            // Get attributes.
            if (ctxPrev.token.type === "attribute") {
                exclusionList.push(ctxPrev.token.string);
            }

            // Get tag.
            if (ctxPrev.token.type === "tag") {
                tagName = ctxPrev.token.string;
                if (TokenUtils.movePrevToken(ctxPrev)) {
                    if (ctxPrev.token.type === "tag bracket" && ctxPrev.token.string === "<") {
                        break;
                    }
                    return null;
                }
            }
        }

        // Look-Ahead and find rest of the attributes.
        pos = $.extend({}, constPos);
        ctxNext = TokenUtils.getInitialContext(editor._codeMirror, pos);
        while (TokenUtils.moveNextToken(ctxNext)) {
            if (ctxNext.token.type === "string" && ctxNext.token.string === "\"") {
                return null;
            }

            // Stop on closing bracket of its own tag or opening bracket of next tag.
            if (ctxNext.token.type === "tag bracket" &&
                    (ctxNext.token.string.indexOf(">") >= 0 || ctxNext.token.string === "<")) {
                break;
            }
            if (ctxNext.token.type === "attribute" && exclusionList.indexOf(ctxNext.token.string) === -1) {
                exclusionList.push(ctxNext.token.string);
            }
        }
        return {
            tagName: tagName,
            exclusionList: exclusionList,
            shouldReplace: shouldReplace
        };
    }
function _getTagAttributes ( editor , constPos ) { var pos , ctx , ctxPrev , ctxNext , ctxTemp , tagName , exclusionList = [ ] , shouldReplace ; pos = $ . extend ( { } , constPos ) ; ctx = TokenUtils . getInitialContext ( editor . _codeMirror , pos ) ; // Stop if the cursor is before = or an attribute value. ctxTemp = $ . extend ( true , { } , ctx ) ; if ( ctxTemp . token . type === null && regexWhitespace . test ( ctxTemp . token . string ) ) { if ( TokenUtils . moveSkippingWhitespace ( TokenUtils . moveNextToken , ctxTemp ) ) { if ( ( ctxTemp . token . type === null && ctxTemp . token . string === "=" ) || ctxTemp . token . type === "string" ) { return null ; } TokenUtils . moveSkippingWhitespace ( TokenUtils . movePrevToken , ctxTemp ) ; } } // Incase an attribute is followed by an equal sign, shouldReplace will be used // to prevent from appending ="" again. if ( ctxTemp . token . type === "attribute" ) { if ( TokenUtils . moveSkippingWhitespace ( TokenUtils . moveNextToken , ctxTemp ) ) { if ( ctxTemp . token . type === null && ctxTemp . token . string === "=" ) { shouldReplace = true ; } } } // Look-Back and get the attributes and tag name. pos = $ . extend ( { } , constPos ) ; ctxPrev = TokenUtils . getInitialContext ( editor . _codeMirror , pos ) ; while ( TokenUtils . movePrevToken ( ctxPrev ) ) { if ( ctxPrev . token . type && ctxPrev . token . type . indexOf ( "tag bracket" ) >= 0 ) { // Disallow hints in closed tag and inside tag content if ( ctxPrev . token . string === "</" || ctxPrev . token . string . indexOf ( ">" ) !== - 1 ) { return null ; } } // Get attributes. if ( ctxPrev . token . type === "attribute" ) { exclusionList . push ( ctxPrev . token . string ) ; } // Get tag. if ( ctxPrev . token . type === "tag" ) { tagName = ctxPrev . token . string ; if ( TokenUtils . movePrevToken ( ctxPrev ) ) { if ( ctxPrev . token . type === "tag bracket" && ctxPrev . token . string === "<" ) { break ; } return null ; } } } // Look-Ahead and find rest of the attributes. pos = $ . extend ( { } , constPos ) ; ctxNext = TokenUtils . getInitialContext ( editor . _codeMirror , pos ) ; while ( TokenUtils . moveNextToken ( ctxNext ) ) { if ( ctxNext . token . type === "string" && ctxNext . token . string === "\"" ) { return null ; } // Stop on closing bracket of its own tag or opening bracket of next tag. if ( ctxNext . token . type === "tag bracket" && ( ctxNext . token . string . indexOf ( ">" ) >= 0 || ctxNext . token . string === "<" ) ) { break ; } if ( ctxNext . token . type === "attribute" && exclusionList . indexOf ( ctxNext . token . string ) === - 1 ) { exclusionList . push ( ctxNext . token . string ) ; } } return { tagName : tagName , exclusionList : exclusionList , shouldReplace : shouldReplace } ; }
Return the tagName and a list of attributes used by the tag.

@param {!Editor} editor An instance of active editor
@param {!{line: number, ch: number}} constPos The position of cursor in the active editor
@return {!{tagName: string, exclusionList: Array.<string>, shouldReplace: boolean}}
Return the tagName and a list of attributes used by the tag .