function getContextInfo(editor, constPos, requireParent, requireNextToken) {
        var pos, ctx, ctxPrev, ctxNext, offset, keyName, valueName, parentKeyName,
            isArray, exclusionList, hasColon, hasComma, hasBracket, shouldReplace;

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

        if (ctx.token && ctx.token.type === "string property") {
            // String literals used as keys.

            // Disallow hints if cursor is out of the string.
            if (/^['"]$/.test(ctx.token.string.substr(-1, 1)) &&
                    ctx.token.string.length !== 1 && ctx.token.end === pos.ch) {
                return null;
            }
            keyName = stripQuotes(ctx.token.string);

            // Get parent key name.
            if (requireParent) {
                ctxPrev = $.extend(true, {}, ctx);
                parentKeyName = stripQuotes(_getParentKeyName(ctxPrev));
            }

            // Check if the key is followed by a colon, so we should not append colon again.
            ctxNext = $.extend(true, {}, ctx);
            TokenUtils.moveSkippingWhitespace(TokenUtils.moveNextToken, ctxNext);
            if (ctxNext.token.type === null && ctxNext.token.string === ":") {
                shouldReplace = true;
            }

            // Get an exclusion list of properties.
            pos = $.extend({}, constPos);
            exclusionList = _getExclusionList(editor, pos);

            return _createContextInfo(ctx.token, TOKEN_KEY, offset, keyName, valueName, parentKeyName, null, exclusionList, shouldReplace);
        } else if (ctx.token && (valueTokenTypes.indexOf(ctx.token.type) !== -1 ||
                                (ctx.token.type === null && regexAllowedChars.test(ctx.token.string)))) {
            // Boolean, String, Number and variable literal values.

            // Disallow hints if cursor is out of the string.
            if (ctx.token.type === "string" && /^['"]$/.test(ctx.token.string.substr(-1, 1)) &&
                    ctx.token.string.length !== 1 && ctx.token.end === pos.ch) {
                return null;
            }
            valueName = ctx.token.string;

            // Check the current token
            if (ctx.token.type === null) {
                if (ctx.token.string === ":") {
                    hasColon = true;
                } else if (ctx.token.string === ",") {
                    hasComma = true;
                } else if (ctx.token.string === "[") {
                    hasBracket = true;
                }
            }

            // move context back and find corresponding key name.
            ctxPrev = $.extend(true, {}, ctx);
            while (TokenUtils.moveSkippingWhitespace(TokenUtils.movePrevToken, ctxPrev)) {
                if (ctxPrev.token.type === "string property") {
                    keyName = stripQuotes(ctxPrev.token.string);
                    break;
                } else if (ctxPrev.token.type === null) {
                    if (ctxPrev.token.string === ":") {
                        hasColon = true;
                    } else if (ctxPrev.token.string === ",") {
                        hasComma = true;
                    } else if (ctxPrev.token.string === "[") {
                        hasBracket = true;
                    } else {
                        return null;
                    }
                } else if (!hasComma) {
                    return null;
                }
            }

            // If no key name or colon found OR
            // If we have a comma but no opening bracket, return null.
            if ((!keyName || !hasColon) || (hasComma && !hasBracket)) {
                return null;
            } else {
                isArray = hasBracket;
            }

            // Get parent key name.
            if (requireParent) {
                ctxPrev = $.extend(true, {}, ctx);
                parentKeyName = stripQuotes(_getParentKeyName(ctxPrev));
            }

            // Check if we can replace the next token of a value.
            ctxNext = $.extend(true, {}, ctx);
            TokenUtils.moveNextToken(ctxNext);
            if (requireNextToken && valueTokenTypes.indexOf(ctxNext.token.type) !== -1) {
                shouldReplace = true;
            }

            return _createContextInfo((shouldReplace) ? ctxNext.token : ctx.token, TOKEN_VALUE, offset, keyName, valueName, parentKeyName, isArray, null, shouldReplace);
        }

        return null;
    }
Returns context info at a given position in editor

@param {!Editor} editor
@param {!{line: number, ch: number}} constPos Position of cursor in the editor
@param {Boolean} requireParent If true will look for parent key name
@param {Boolean} requireNextToken if true we can replace the next token of a value.
@return {!{token: Object, tokenType: number, offset: number, keyName: String, valueName: String, parentKeyName: String, isArray: Boolean, exclusionList: Array.<String>, shouldReplace: Boolean}}