package org.elixir_lang;
import com.google.common.collect.ImmutableMap;
import com.intellij.codeInsight.editorActions.MultiCharQuoteHandler;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.elixir_lang.lexer.StackFrame;
import org.elixir_lang.psi.ElixirTypes;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
public class QuoteHandler implements MultiCharQuoteHandler {
/*
* CONSTANTS
*/
private final static Map<IElementType, IElementType> CLOSING_QUOTE_BY_OPENING_QUOTE = ImmutableMap
.<IElementType, IElementType>builder()
.put(ElixirTypes.CHAR_LIST_HEREDOC_PROMOTER, ElixirTypes.CHAR_LIST_HEREDOC_TERMINATOR)
.put(ElixirTypes.CHAR_LIST_SIGIL_HEREDOC_PROMOTER, ElixirTypes.CHAR_LIST_SIGIL_HEREDOC_TERMINATOR)
.put(ElixirTypes.CHAR_LIST_SIGIL_PROMOTER, ElixirTypes.CHAR_LIST_SIGIL_TERMINATOR)
.put(ElixirTypes.CHAR_LIST_PROMOTER, ElixirTypes.CHAR_LIST_PROMOTER)
.put(ElixirTypes.REGEX_HEREDOC_PROMOTER, ElixirTypes.REGEX_HEREDOC_TERMINATOR)
.put(ElixirTypes.REGEX_PROMOTER, ElixirTypes.REGEX_TERMINATOR)
.put(ElixirTypes.SIGIL_HEREDOC_PROMOTER, ElixirTypes.SIGIL_HEREDOC_TERMINATOR)
.put(ElixirTypes.SIGIL_PROMOTER, ElixirTypes.SIGIL_TERMINATOR)
.put(ElixirTypes.STRING_HEREDOC_PROMOTER, ElixirTypes.STRING_HEREDOC_TERMINATOR)
.put(ElixirTypes.STRING_SIGIL_HEREDOC_PROMOTER, ElixirTypes.STRING_SIGIL_HEREDOC_TERMINATOR)
.put(ElixirTypes.STRING_SIGIL_PROMOTER, ElixirTypes.STRING_SIGIL_TERMINATOR)
.put(ElixirTypes.STRING_PROMOTER, ElixirTypes.STRING_TERMINATOR)
.put(ElixirTypes.WORDS_HEREDOC_PROMOTER, ElixirTypes.WORDS_HEREDOC_TERMINATOR)
.put(ElixirTypes.WORDS_PROMOTER, ElixirTypes.WORDS_TERMINATOR)
.build();
private final static TokenSet CLOSING_QUOTES = TokenSet.create(
CLOSING_QUOTE_BY_OPENING_QUOTE
.values()
.toArray(new IElementType[0])
);
private final static TokenSet LITERALS = TokenSet.create(
ElixirTypes.ATOM_FRAGMENT,
ElixirTypes.CHAR_LIST_FRAGMENT,
ElixirTypes.REGEX_FRAGMENT,
ElixirTypes.SIGIL_FRAGMENT,
ElixirTypes.STRING_FRAGMENT,
ElixirTypes.WORDS_FRAGMENT
);
private final static TokenSet OPENING_QUOTES = TokenSet.create(
CLOSING_QUOTE_BY_OPENING_QUOTE
.keySet()
.toArray(new IElementType[0])
);
/*
* Instance Methods
*/
@Nullable
@Override
public CharSequence getClosingQuote(HighlighterIterator highlighterIterator, int offset) {
Document document = highlighterIterator.getDocument();
CharSequence closingQuote = null;
if (document != null) {
if (offset >= 3) {
String openingQuote = document.getText(new TextRange(offset - 3, offset));
closingQuote = StackFrame.TERMINATOR_BY_PROMOTER.get(openingQuote);
if (closingQuote != null) {
closingQuote = "\n" + closingQuote;
}
}
if (closingQuote == null && offset >= 1) {
String openingQuote = document.getText(new TextRange(offset - 1, offset));
closingQuote = StackFrame.TERMINATOR_BY_PROMOTER.get(openingQuote);
}
}
return closingQuote;
}
/**
*
* @param editor
* @param highlighterIterator
* @param offset the offset of the element with {@link HighlighterIterator#getTokenType()}
* @return {@code true} to automatically insert a closing quote
* (from {@link #getClosingQuote(HighlighterIterator, int)}
* @see <a href="https://github.com/JetBrains/intellij-community/blob/eeefa20d7c43856143c825ca65de6d5089241b35/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java#L472-L478">TypeHandler#handleQuote</a>
* @see <a href="https://github.com/JetBrains/intellij-community/blob/eeefa20d7c43856143c825ca65de6d5089241b35/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java#L529">TypeHandler#hasNonClosedLiterals</a>
*/
@Override
public boolean hasNonClosedLiteral(Editor editor, HighlighterIterator highlighterIterator, int offset) {
/* it is safe to always return true based on XMLQuoteHandler
@see https://github.com/JetBrains/intellij-community/blob/eeefa20d7c43856143c825ca65de6d5089241b35/xml/impl/src/com/intellij/codeInsight/editorActions/XmlQuoteHandler.java#L38 */
return true;
}
/**
*
* @param highlighterIterator
* @param offset the current offset in the file of the {@code highlighterIterator}
* @return {@code true} if {@link HighlighterIterator#getTokenType()} is one of {@link #CLOSING_QUOTES} and
* {@code offset} is {@link HighlighterIterator#getStart()}
*/
@Override
public boolean isClosingQuote(HighlighterIterator highlighterIterator, int offset) {
boolean isClosingQuote = false;
if (CLOSING_QUOTES.contains(highlighterIterator.getTokenType())) {
int start = highlighterIterator.getStart();
int end = highlighterIterator.getEnd();
isClosingQuote = end - start >= 1 && offset == end - 1;
}
return isClosingQuote;
}
@Override
public boolean isInsideLiteral(HighlighterIterator highlighterIterator) {
return LITERALS.contains(highlighterIterator.getTokenType());
}
@Override
public boolean isOpeningQuote(HighlighterIterator highlighterIterator, int offset) {
boolean isOpeningQuote = false;
if (OPENING_QUOTES.contains(highlighterIterator.getTokenType())){
int start = highlighterIterator.getStart();
isOpeningQuote = offset == start;
}
return isOpeningQuote;
}
}