enter handler for strings

This commit is contained in:
Maxim.Medvedev
2012-02-06 10:36:50 +04:00
parent 1d61e0a5d9
commit fc939bb1eb
13 changed files with 121 additions and 53 deletions
@@ -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);
}
});
}
@@ -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();
}
@@ -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 '\<caret>'
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;
@@ -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());
}
}
@@ -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;
}
}
@@ -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 {
<caret>
} }
'''
}
}
@@ -0,0 +1,4 @@
"p<caret>\nreved $id medved"
-----
"""p
<caret>\nreved $id medved"""
@@ -0,0 +1,4 @@
"p\<caret>nreved $id medved"
-----
"""p\
<caret>nreved $id medved"""
@@ -0,0 +1,4 @@
"p\n<caret>reved $id medved"
-----
"""p\n
<caret>reved $id medved"""
@@ -0,0 +1,8 @@
def cl = {
print ('foo<caret>\n')
}
-----
def cl = {
print ('''foo
<caret>\n''')
}
@@ -0,0 +1,8 @@
def cl = {
print ('foo\<caret>')
}
-----
def cl = {
print ('''foo\
<caret>''')
}
@@ -0,0 +1,8 @@
def cl = {
print ('foo<caret>\')
}
-----
def cl = {
print ('''foo
<caret>\')'''
}
@@ -0,0 +1,8 @@
def cl = {
print ('foo\<caret>n')
}
-----
def cl = {
print ('''foo\
<caret>n''')
}