package com.jetbrains.lang.dart.ide.editor; import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegateAdapter; import com.intellij.lang.ASTNode; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.tree.IElementType; import com.intellij.xml.util.HtmlUtil; import com.jetbrains.lang.dart.DartLanguage; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import static com.jetbrains.lang.dart.DartTokenTypes.*; public class DartEnterInStringHandler extends EnterHandlerDelegateAdapter { @Override public Result preprocessEnter(@NotNull final PsiFile file, @NotNull final Editor editor, @NotNull final Ref<Integer> caretOffsetRef, @NotNull final Ref<Integer> caretAdvanceRef, @NotNull final DataContext dataContext, final EditorActionHandler originalHandler) { if (file.getLanguage() != DartLanguage.INSTANCE && !HtmlUtil.isHtmlFile(file)) return Result.Continue; int caretOffset = caretOffsetRef.get().intValue(); PsiDocumentManager.getInstance(file.getProject()).commitDocument(editor.getDocument()); PsiElement psiAtOffset = file.findElementAt(caretOffset); int psiOffset; if (psiAtOffset == null || (psiOffset = psiAtOffset.getTextRange().getStartOffset()) > caretOffset) { return Result.Continue; } ASTNode node = psiAtOffset.getNode(); IElementType nodeType = node.getElementType(); if ((nodeType == SHORT_TEMPLATE_ENTRY_START || nodeType == LONG_TEMPLATE_ENTRY_START) && caretOffset == psiOffset) { node = node.getTreeParent(); nodeType = node.getElementType(); } if (nodeType == RAW_TRIPLE_QUOTED_STRING && caretOffset >= psiOffset + "r'''".length()) { return Result.DefaultSkipIndent; // Multiline string gets no indent } if (nodeType == RAW_SINGLE_QUOTED_STRING && caretOffset >= psiOffset + "r'".length()) { char quote = node.getText().charAt(1); breakString("r" + quote, String.valueOf(quote), caretOffsetRef, caretAdvanceRef, editor.getDocument()); return Result.Default; } if (nodeType == REGULAR_STRING_PART || nodeType == CLOSING_QUOTE || nodeType == SHORT_TEMPLATE_ENTRY || nodeType == LONG_TEMPLATE_ENTRY) { final String openingQuoteText = getOpeningQuoteText(node); if (openingQuoteText != null) { if (openingQuoteText.length() == 1) { breakString(openingQuoteText, openingQuoteText, caretOffsetRef, caretAdvanceRef, editor.getDocument()); return Result.Default; } else { return Result.DefaultSkipIndent; // Multiline string gets no indent } } } return Result.Continue; } @Nullable private static String getOpeningQuoteText(@NotNull final ASTNode node) { ASTNode prev = node.getTreePrev(); while (prev != null) { if (prev.getElementType() == OPEN_QUOTE) { return prev.getText(); } prev = prev.getTreePrev(); } return null; } private static void breakString(@NotNull final String startQuote, @NotNull final String endQuote, @NotNull final Ref<Integer> caretOffsetRef, @NotNull final Ref<Integer> caretAdvanceRef, @NotNull final Document document) { // The final effect is to insert matching close-quote, newline, indent, matching open-quote. int caretOffset = caretOffsetRef.get().intValue(); int caretAdvance = caretAdvanceRef.get().intValue(); document.insertString(caretOffset, endQuote + startQuote); caretOffset += endQuote.length(); caretAdvance += startQuote.length(); caretOffsetRef.set(caretOffset); caretAdvanceRef.set(caretAdvance); } }