StringKind (@NotNull PsiFile file, int offset) { final PsiElement leaf = file.findElementAt(offset); if (leaf == null) { return null; } final IElementType leafType = leaf.getNode().getElementType(); final int leafOffset = leaf.getTextOffset(); if (offset == leafOffset) { // special cases if (leafType == GSTRING_END || leafType == SLASHY_END || leafType == DOLLAR_SLASHY_END) { PsiElement prevSibling = leaf.getPrevSibling(); if (prevSibling != null) { IElementType previousElementType = prevSibling.getNode().getElementType(); if (previousElementType == STRING_INJECTION) { if (leafType == GSTRING_END) { if (isMultiline(leaf.getParent())) { // """...${}<here>""" return StringKind.TRIPLE_DOUBLE_QUOTED; } else { // "...${}<here>" return StringKind.DOUBLE_QUOTED; } } if (leafType == SLASHY_END) { // /...${}<here>/ return StringKind.SLASHY; } else { // $/...${}<here>/$ return StringKind.DOLLAR_SLASHY; } } else if (previousElementType == STRING_CONTENT) { // "...${}...<here>" // """...${}...<here>""" // /...${}...<here>/ // $/...${}...<here>/$ PsiElement contentLeaf = prevSibling.getFirstChild(); return getStringKindByContentTokenType(contentLeaf, contentLeaf.getNode().getElementType()); } else if (previousElementType == DOLLAR_SLASHY_BEGIN) { // $/<here>/$ return StringKind.DOLLAR_SLASHY; } else if (leafType != GSTRING_END) { // /...<here>/ // $/...<here>/$ // // "..." is a single token returned by lexer, // while slashy and dollar slashy strings without injections consist of three tokens start quote, content and end quote return getStringKindByContentTokenType(leaf, previousElementType); } } } else if (leafType == T_DOLLAR) { // Strings with injections, leaf is dollar: // "<here>${}..." or "...<here>${}..." // /<here>${}.../ or /...<here>${}.../ // $/<here>${}.../$ or $/...<here>${}.../$ PsiElement parent = leaf.getParent(); // template string injection PsiElement gParent = parent.getParent(); // template string element; return getStringKindByStringElement(gParent); } } final int leafEndOffset = leafOffset + leaf.getTextLength(); if (leafType == STRING_SQ || leafType == STRING_DQ) { if (leafOffset + 1 <= offset && offset <= leafEndOffset - 1) { // Single token literals: // '<here>......' // '...<here>...' // '......<here>' // "<here>......" // "...<here>..." // "......<here>" return leafType == STRING_SQ ? StringKind.SINGLE_QUOTED : StringKind.DOUBLE_QUOTED; } } if (leafType == STRING_TSQ || leafType == STRING_TDQ) { // Single token literals: // '''<here>...''' // """<here>...""" if (leafOffset + 3 <= offset && offset <= leafEndOffset - 3) { return leafType == STRING_TSQ ? StringKind.TRIPLE_SINGLE_QUOTED : StringKind.TRIPLE_DOUBLE_QUOTED; } } // Strings with injections, leaf is string content: // "<here>...${}..." or "...<here>...${}..." // """<here>...${}...""" or """...<here>...${}...""" // /<here>...${}.../ or /...<here>...${}.../ // $/<here>...${}.../$ or $/...<here>...${}.../$ return getStringKindByContentTokenType(leaf, leafType); }