diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyFileType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyFileType.java index 8e9a333abfd1..69a1382f00e0 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyFileType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/GroovyFileType.java @@ -16,15 +16,15 @@ package org.jetbrains.plugins.groovy; +import com.intellij.lang.Language; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.fileTypes.EditorHighlighterProvider; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeEditorHighlighterProviders; import com.intellij.openapi.fileTypes.LanguageFileType; -import com.intellij.openapi.editor.highlighter.EditorHighlighter; -import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.lang.Language; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,7 +50,7 @@ public class GroovyFileType extends LanguageFileType { public EditorHighlighter getEditorHighlighter(@Nullable Project project, @NotNull FileType fileType, @Nullable VirtualFile virtualFile, @NotNull EditorColorsScheme colors) { - return new GroovyEditorHighlighter(colors, project, virtualFile); + return new GroovyEditorHighlighter(colors); } }); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/highlighter/GroovyEditorHighlighter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/highlighter/GroovyEditorHighlighter.java index abdc14186e73..c0a235eb0211 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/highlighter/GroovyEditorHighlighter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/highlighter/GroovyEditorHighlighter.java @@ -20,8 +20,6 @@ import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.util.LayerDescriptor; import com.intellij.openapi.editor.ex.util.LayeredLexerEditorHighlighter; import com.intellij.openapi.fileTypes.SyntaxHighlighter; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.plugins.groovy.lang.groovydoc.highlighter.GroovyDocSyntaxHighlighter; import org.jetbrains.plugins.groovy.lang.groovydoc.parser.GroovyDocElementTypes; @@ -30,7 +28,7 @@ import org.jetbrains.plugins.groovy.lang.groovydoc.parser.GroovyDocElementTypes; */ public class GroovyEditorHighlighter extends LayeredLexerEditorHighlighter { - public GroovyEditorHighlighter(EditorColorsScheme scheme, Project project, VirtualFile virtualFile) { + public GroovyEditorHighlighter(EditorColorsScheme scheme) { super(new GroovySyntaxHighlighter(), scheme); registerGroovydocHighlighter(); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/editor/actions/GroovyEnterHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/editor/actions/GroovyEnterHandler.java index 8e3748d4d660..b7df458df553 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/editor/actions/GroovyEnterHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/editor/actions/GroovyEnterHandler.java @@ -33,15 +33,17 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.formatter.GeeseUtil; import org.jetbrains.plugins.groovy.formatter.GroovyCodeStyleSettings; import org.jetbrains.plugins.groovy.lang.editor.HandlerUtils; -import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; +import org.jetbrains.plugins.groovy.lang.lexer.GroovyLexer; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; @@ -152,9 +154,7 @@ public class GroovyEnterHandler extends EnterHandlerDelegateAdapter { if (element == null || !GeeseUtil.isClosureRBrace(element)) return false; element = GeeseUtil.getNextNonWhitespaceToken(element); - if (element == null || - element.getNode().getElementType() != GroovyTokenTypes.mNLS || - StringUtil.countChars(element.getText(), '\n') > 1) { + if (element == null || element.getNode().getElementType() != mNLS || StringUtil.countChars(element.getText(), '\n') > 1) { return false; } @@ -206,8 +206,10 @@ public class GroovyEnterHandler extends EnterHandlerDelegateAdapter { private static final TokenSet AFTER_DOLLAR = TokenSet.create(mLCURLY, mIDENT, mGSTRING_CONTENT, mDOLLAR, mGSTRING_END); - private static final TokenSet ALL_STRINGS = - TokenSet.create(mSTRING_LITERAL, mGSTRING_LITERAL, mGSTRING_BEGIN, mGSTRING_END, mGSTRING_CONTENT, mRCURLY, mIDENT, mDOLLAR); + private static final TokenSet ALL_STRINGS = TokenSet + .create(mSTRING_LITERAL, mGSTRING_LITERAL, mGSTRING_BEGIN, mGSTRING_END, mGSTRING_CONTENT, mRCURLY, mIDENT, mDOLLAR, mREGEX_BEGIN, + mREGEX_CONTENT, mREGEX_END, mDOLLAR_SLASH_REGEX_BEGIN, mDOLLAR_SLASH_REGEX_CONTENT, mDOLLAR_SLASH_REGEX_END, mREGEX_LITERAL, + mDOLLAR_SLASH_REGEX_LITERAL); private static final TokenSet BEFORE_DOLLAR = TokenSet.create(mGSTRING_BEGIN, mGSTRING_CONTENT); @@ -222,7 +224,9 @@ public class GroovyEnterHandler extends EnterHandlerDelegateAdapter { Project project = PlatformDataKeys.PROJECT.getData(dataContext); if (project == null) return false; - PsiFile file = PsiManager.getInstance(project).findFile(FileDocumentManager.getInstance().getFile(editor.getDocument())); + final VirtualFile vfile = FileDocumentManager.getInstance().getFile(editor.getDocument()); + assert vfile != null; + PsiFile file = PsiManager.getInstance(project).findFile(vfile); Document document = editor.getDocument(); String fileText = document.getText(); @@ -241,7 +245,14 @@ public class GroovyEnterHandler extends EnterHandlerDelegateAdapter { if (mSTRING_LITERAL == node.getElementType()) { if (GroovyEditorActionUtil.isPlainStringLiteral(node)) { TextRange literalRange = stringElement.getTextRange(); - document.insertString(literalRange.getEndOffset(), "''"); + + //the case of print '\' + if (fileText.charAt(caretOffset) == '\'' && caretOffset > 0 && fileText.charAt(caretOffset - 1) == '\\') { + convertEndToMultiline(caretOffset, document, fileText); + } + else { + convertEndToMultiline(literalRange.getEndOffset(), document, fileText); + } document.insertString(literalRange.getStartOffset(), "''"); editor.getCaretModel().moveToOffset(caretOffset + 2); EditorModificationUtil.insertStringAtCaret(editor, "\n"); @@ -292,30 +303,37 @@ public class GroovyEnterHandler extends EnterHandlerDelegateAdapter { return false; } - private static boolean checkStringApplicable(Editor editor, int caret) { - final EditorHighlighter highlighter = ((EditorEx)editor).getHighlighter(); - HighlighterIterator iteratorLeft = highlighter.createIterator(caret - 1); - HighlighterIterator iteratorRight = highlighter.createIterator(caret); + private static void convertEndToMultiline(int caretOffset, Document document, String fileText) { + if (caretOffset < fileText.length() && fileText.charAt(caretOffset) == '\'' || + caretOffset > 0 && fileText.charAt(caretOffset - 1) == '\'') { + document.insertString(caretOffset, "''"); + } + else { + document.insertString(caretOffset, "'''"); + } + } - if (iteratorLeft != null && !(ALL_STRINGS.contains(iteratorLeft.getTokenType()))) { + private static boolean checkStringApplicable(Editor editor, int caret) { + final GroovyLexer lexer = new GroovyLexer(); + lexer.start(editor.getDocument().getText()); + + while (lexer.getTokenEnd() < caret) { + lexer.advance(); + } + final IElementType leftToken = lexer.getTokenType(); + if (lexer.getTokenEnd() <= caret) lexer.advance(); + final IElementType rightToken = lexer.getTokenType(); + + if (!(ALL_STRINGS.contains(leftToken))) { return false; } - if (iteratorLeft != null && - BEFORE_DOLLAR.contains(iteratorLeft.getTokenType()) && - iteratorRight != null && - !AFTER_DOLLAR.contains(iteratorRight.getTokenType())) { + if (BEFORE_DOLLAR.contains(leftToken) && !AFTER_DOLLAR.contains(rightToken)) { return false; } - if (iteratorLeft != null && - EXPR_END.contains(iteratorLeft.getTokenType()) && - iteratorRight != null && - !AFTER_EXPR_END.contains(iteratorRight.getTokenType())) { + if (EXPR_END.contains(leftToken) && !AFTER_EXPR_END.contains(rightToken)) { return false; } - if (iteratorLeft != null && - STRING_END.contains(iteratorLeft.getTokenType()) && - iteratorRight != null && - !STRING_END.contains(iteratorRight.getTokenType())) { + if (STRING_END.contains(leftToken) && !STRING_END.contains(rightToken)) { return false; } return true; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/folding/GroovyFoldingBuilder.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/folding/GroovyFoldingBuilder.java index 38f77425389c..ef7a8c26f90f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/folding/GroovyFoldingBuilder.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/folding/GroovyFoldingBuilder.java @@ -209,25 +209,6 @@ public class GroovyFoldingBuilder implements FoldingBuilder, GroovyElementTypes, return element.getText().endsWith("*/"); } - private static boolean isWellEndedString(PsiElement element) { - final String text = element.getText(); - - if (!text.endsWith("'''") && !text.endsWith("\"\"\"") && !text.endsWith("/") && !text.endsWith("/$")) return false; - - - final IElementType type = element.getNode().getElementType(); - if (TokenSets.STRING_LITERAL_SET.contains(type)) return true; - - final PsiElement lastChild = element.getLastChild(); - if (lastChild == null) return false; - - final IElementType lastType = lastChild.getNode().getElementType(); - if (type == GSTRING) return lastType == mGSTRING_END; - if (type == REGEX) return lastType == mREGEX_END || lastType == mDOLLAR_SLASH_REGEX_END; - - return false; - } - private static boolean isMultiline(PsiElement element) { String text = element.getText(); return text.contains("\n") || text.contains("\r") || text.contains("\r\n"); @@ -297,6 +278,6 @@ public class GroovyFoldingBuilder implements FoldingBuilder, GroovyElementTypes, node.getElementType().equals(GSTRING) || node.getElementType().equals(REGEX)) && isMultiline(node.getPsi()) && - isWellEndedString(node.getPsi()); + GrStringUtil.isWellEndedString(node.getPsi()); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GrStringUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GrStringUtil.java index b340263d6b7e..edb3b8798b1f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GrStringUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GrStringUtil.java @@ -8,6 +8,8 @@ import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; +import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; @@ -801,4 +803,23 @@ public class GrStringUtil { final IElementType elementType = literal.getFirstChild().getNode().getElementType(); return elementType == mREGEX_LITERAL || elementType == mDOLLAR_SLASH_REGEX_LITERAL; } + + public static boolean isWellEndedString(PsiElement element) { + final String text = element.getText(); + + if (!text.endsWith("'''") && !text.endsWith("\"\"\"") && !text.endsWith("/") && !text.endsWith("/$")) return false; + + + final IElementType type = element.getNode().getElementType(); + if (TokenSets.STRING_LITERAL_SET.contains(type)) return true; + + final PsiElement lastChild = element.getLastChild(); + if (lastChild == null) return false; + + final IElementType lastType = lastChild.getNode().getElementType(); + if (type == GroovyElementTypes.GSTRING) return lastType == mGSTRING_END; + if (type == GroovyElementTypes.REGEX) return lastType == mREGEX_END || lastType == mDOLLAR_SLASH_REGEX_END; + + return false; + } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/EnterActionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/EnterActionTest.groovy index 1de81cbf2f45..f6ac881554e1 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/EnterActionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/EnterActionTest.groovy @@ -69,6 +69,9 @@ public class EnterActionTest extends GroovyFormatterTestCase { public void testGstring11() throws Throwable { doTest(); } public void testGstring12() throws Throwable { doTest(); } public void testGstring13() throws Throwable { doTest(); } + public void testGstring14() throws Throwable { doTest(); } + public void testGstring15() throws Throwable { doTest(); } + public void testGstring16() throws Throwable { doTest(); } public void testGstring2() throws Throwable { doTest(); } public void testGstring3() throws Throwable { doTest(); } public void testGstring4() throws Throwable { doTest(); } @@ -85,6 +88,10 @@ public class EnterActionTest extends GroovyFormatterTestCase { public void testString4() throws Throwable { doTest(); } public void testString5() throws Throwable { doTest(); } public void testString6() throws Throwable { doTest(); } + public void testString7() throws Throwable { doTest(); } + public void testString8() throws Throwable { doTest(); } + public void testString9() throws Throwable { doTest(); } + public void testString10() throws Throwable { doTest(); } def doTest(String before, String after) { myFixture.configureByText("a.groovy", before) @@ -287,7 +294,6 @@ foo { } } ''' - } } diff --git a/plugins/groovy/testdata/groovy/enterAction/gstring14.test b/plugins/groovy/testdata/groovy/enterAction/gstring14.test new file mode 100644 index 000000000000..8a40d015ca8e --- /dev/null +++ b/plugins/groovy/testdata/groovy/enterAction/gstring14.test @@ -0,0 +1,4 @@ +"p\nreved $id medved" +----- +"""p +\nreved $id medved""" \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/enterAction/gstring15.test b/plugins/groovy/testdata/groovy/enterAction/gstring15.test new file mode 100644 index 000000000000..7be49c36622a --- /dev/null +++ b/plugins/groovy/testdata/groovy/enterAction/gstring15.test @@ -0,0 +1,4 @@ +"p\nreved $id medved" +----- +"""p\ +nreved $id medved""" \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/enterAction/gstring16.test b/plugins/groovy/testdata/groovy/enterAction/gstring16.test new file mode 100644 index 000000000000..077cf356371e --- /dev/null +++ b/plugins/groovy/testdata/groovy/enterAction/gstring16.test @@ -0,0 +1,4 @@ +"p\nreved $id medved" +----- +"""p\n +reved $id medved""" \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/enterAction/string10.test b/plugins/groovy/testdata/groovy/enterAction/string10.test new file mode 100644 index 000000000000..62ac00930273 --- /dev/null +++ b/plugins/groovy/testdata/groovy/enterAction/string10.test @@ -0,0 +1,8 @@ +def cl = { + print ('foo\n') +} +----- +def cl = { + print ('''foo +\n''') +} \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/enterAction/string7.test b/plugins/groovy/testdata/groovy/enterAction/string7.test new file mode 100644 index 000000000000..ef77526f024f --- /dev/null +++ b/plugins/groovy/testdata/groovy/enterAction/string7.test @@ -0,0 +1,8 @@ +def cl = { + print ('foo\') +} +----- +def cl = { + print ('''foo\ +''') +} \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/enterAction/string8.test b/plugins/groovy/testdata/groovy/enterAction/string8.test new file mode 100644 index 000000000000..6ba93cb02352 --- /dev/null +++ b/plugins/groovy/testdata/groovy/enterAction/string8.test @@ -0,0 +1,8 @@ +def cl = { + print ('foo\') +} +----- +def cl = { + print ('''foo +\')''' +} \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/enterAction/string9.test b/plugins/groovy/testdata/groovy/enterAction/string9.test new file mode 100644 index 000000000000..cd1288e6a2c0 --- /dev/null +++ b/plugins/groovy/testdata/groovy/enterAction/string9.test @@ -0,0 +1,8 @@ +def cl = { + print ('foo\n') +} +----- +def cl = { + print ('''foo\ +n''') +} \ No newline at end of file