java-like pair <> handling in groovy

This commit is contained in:
peter
2011-03-08 19:48:57 +01:00
parent ef087c602c
commit ae03a1e758
15 changed files with 177 additions and 54 deletions
@@ -22,6 +22,7 @@ import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiUtil;
public class JavaBackspaceHandler extends BackspaceHandlerDelegate {
@@ -43,30 +44,33 @@ public class JavaBackspaceHandler extends BackspaceHandlerDelegate {
char c1 = chars.charAt(offset);
if (c == '<' && myToDeleteGt) {
if (c1 != '>') return true;
handleLTDeletion(editor, offset);
handleLTDeletion(editor, offset, JavaTokenType.LT, JavaTokenType.GT, JavaTypedHandler.INVALID_INSIDE_REFERENCE);
return true;
}
return false;
}
private static void handleLTDeletion(final Editor editor, final int offset) {
public static void handleLTDeletion(final Editor editor,
final int offset,
final IElementType lt,
final IElementType gt, final TokenSet invalidInsideReference) {
HighlighterIterator iterator = ((EditorEx)editor).getHighlighter().createIterator(offset);
while (iterator.getStart() > 0 && !JavaTypedHandlerUtil.isTokenInvalidInsideReference(iterator.getTokenType())) {
while (iterator.getStart() > 0 && !invalidInsideReference.contains(iterator.getTokenType())) {
iterator.retreat();
}
if (JavaTypedHandlerUtil.isTokenInvalidInsideReference(iterator.getTokenType())) iterator.advance();
if (invalidInsideReference.contains(iterator.getTokenType())) iterator.advance();
int balance = 0;
while (!iterator.atEnd() && balance >= 0) {
final IElementType tokenType = iterator.getTokenType();
if (tokenType == JavaTokenType.LT) {
if (tokenType == lt) {
balance++;
}
else if (tokenType == JavaTokenType.GT) {
else if (tokenType == gt) {
balance--;
}
else if (JavaTypedHandlerUtil.isTokenInvalidInsideReference(tokenType)) {
else if (invalidInsideReference.contains(tokenType)) {
break;
}
@@ -34,6 +34,7 @@ import com.intellij.psi.filters.position.SuperParentFilter;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.jsp.JspFile;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Nullable;
@@ -42,6 +43,7 @@ import org.jetbrains.annotations.Nullable;
* @author yole
*/
public class JavaTypedHandler extends TypedHandlerDelegate {
static final TokenSet INVALID_INSIDE_REFERENCE = TokenSet.create(JavaTokenType.SEMICOLON, JavaTokenType.LBRACE, JavaTokenType.RBRACE);
private boolean myJavaLTTyped;
public Result checkAutoPopup(final char charTyped, final Project project, final Editor editor, final PsiFile file) {
@@ -106,7 +108,7 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
if (file instanceof PsiJavaFile && !(file instanceof JspFile) &&
CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET &&
PsiUtil.isLanguageLevel5OrHigher(file)) {
if (handleJavaGT(editor)) return Result.STOP;
if (handleJavaGT(editor, JavaTokenType.LT, JavaTokenType.GT, INVALID_INSIDE_REFERENCE)) return Result.STOP;
}
}
@@ -144,7 +146,7 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
public Result charTyped(final char c, final Project project, final Editor editor, final PsiFile file) {
if (myJavaLTTyped) {
myJavaLTTyped = false;
handleAfterJavaLT(editor);
handleAfterJavaLT(editor, JavaTokenType.LT, JavaTokenType.GT, INVALID_INSIDE_REFERENCE);
return Result.STOP;
}
return Result.CONTINUE;
@@ -170,7 +172,10 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
}
//need custom handler, since brace matcher cannot be used
private static boolean handleJavaGT(final Editor editor) {
public static boolean handleJavaGT(final Editor editor,
final IElementType lt,
final IElementType gt,
final TokenSet invalidInsideReference) {
if (!CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) return false;
int offset = editor.getCaretModel().getOffset();
@@ -178,24 +183,23 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
if (offset == editor.getDocument().getTextLength()) return false;
HighlighterIterator iterator = ((EditorEx) editor).getHighlighter().createIterator(offset);
if (iterator.getTokenType() != JavaTokenType.GT) return false;
while (!iterator.atEnd() && !JavaTypedHandlerUtil.isTokenInvalidInsideReference(iterator.getTokenType())) {
if (iterator.getTokenType() != gt) return false;
while (!iterator.atEnd() && !invalidInsideReference.contains(iterator.getTokenType())) {
iterator.advance();
}
if (iterator.atEnd()) return false;
if (JavaTypedHandlerUtil.isTokenInvalidInsideReference(iterator.getTokenType())) iterator.retreat();
if (!iterator.atEnd() && invalidInsideReference.contains(iterator.getTokenType())) iterator.retreat();
int balance = 0;
while (!iterator.atEnd() && balance >= 0) {
final IElementType tokenType = iterator.getTokenType();
if (tokenType == JavaTokenType.LT) {
if (tokenType == lt) {
balance--;
}
else if (tokenType == JavaTokenType.GT) {
else if (tokenType == gt) {
balance++;
}
else if (JavaTypedHandlerUtil.isTokenInvalidInsideReference(tokenType)) {
else if (invalidInsideReference.contains(tokenType)) {
break;
}
@@ -212,27 +216,30 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
}
//need custom handler, since brace matcher cannot be used
private static void handleAfterJavaLT(final Editor editor) {
public static void handleAfterJavaLT(final Editor editor,
final IElementType lt,
final IElementType gt,
final TokenSet invalidInsideReference) {
if (!CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) return;
int offset = editor.getCaretModel().getOffset();
HighlighterIterator iterator = ((EditorEx) editor).getHighlighter().createIterator(offset);
while (iterator.getStart() > 0 && !JavaTypedHandlerUtil.isTokenInvalidInsideReference(iterator.getTokenType())) {
while (iterator.getStart() > 0 && !invalidInsideReference.contains(iterator.getTokenType())) {
iterator.retreat();
}
if (JavaTypedHandlerUtil.isTokenInvalidInsideReference(iterator.getTokenType())) iterator.advance();
if (invalidInsideReference.contains(iterator.getTokenType())) iterator.advance();
int balance = 0;
while (!iterator.atEnd() && balance >= 0) {
final IElementType tokenType = iterator.getTokenType();
if (tokenType == JavaTokenType.LT) {
if (tokenType == lt) {
balance++;
}
else if (tokenType == JavaTokenType.GT) {
else if (tokenType == gt) {
balance--;
}
else if (JavaTypedHandlerUtil.isTokenInvalidInsideReference(tokenType)) {
else if (invalidInsideReference.contains(tokenType)) {
break;
}
@@ -262,7 +269,11 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
if (iterator.getStart() > 0) iterator.retreat();
final IElementType tokenType = iterator.getTokenType();
if (tokenType == JavaTokenType.DOT) return true;
if (tokenType == JavaTokenType.IDENTIFIER && iterator.getEnd() == offset) {
return isClassLikeIdentifier(offset, editor, iterator, JavaTokenType.IDENTIFIER);
}
public static boolean isClassLikeIdentifier(int offset, Editor editor, HighlighterIterator iterator, final IElementType idType) {
if (iterator.getTokenType() == idType && iterator.getEnd() == offset) {
final CharSequence chars = editor.getDocument().getCharsSequence();
final char startChar = chars.charAt(iterator.getStart());
if (!Character.isUpperCase(startChar)) return false;
@@ -1,30 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.JavaTokenType;
public class JavaTypedHandlerUtil {
private JavaTypedHandlerUtil() {
}
public static boolean isTokenInvalidInsideReference(final IElementType tokenType) {
return tokenType == JavaTokenType.SEMICOLON ||
tokenType == JavaTokenType.LBRACE ||
tokenType == JavaTokenType.RBRACE;
}
}
+3
View File
@@ -162,6 +162,9 @@
<typedHandler implementation="org.jetbrains.plugins.groovy.lang.editor.actions.GStringTypedActionHandler"/>
<backspaceHandlerDelegate implementation="org.jetbrains.plugins.groovy.lang.editor.actions.GStringBackspaceHandlerDelegate"/>
<typedHandler implementation="org.jetbrains.plugins.groovy.lang.editor.actions.GroovyTypedHandler"/>
<backspaceHandlerDelegate implementation="org.jetbrains.plugins.groovy.lang.editor.actions.GroovyBackspaceHandler"/>
<liveTemplateContext implementation="org.jetbrains.plugins.groovy.template.GroovyTemplateContextType"/>
<liveTemplateOptionalProcessor implementation="org.jetbrains.plugins.groovy.template.GroovyShortenFQNamesProcessor"/>
@@ -0,0 +1,50 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.lang.editor.actions;
import com.intellij.codeInsight.editorActions.BackspaceHandlerDelegate;
import com.intellij.codeInsight.editorActions.JavaBackspaceHandler;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiFile;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
/**
* @author peter
*/
public class GroovyBackspaceHandler extends BackspaceHandlerDelegate {
private boolean myToDeleteGt;
public void beforeCharDeleted(char c, PsiFile file, Editor editor) {
int offset = editor.getCaretModel().getOffset() - 1;
myToDeleteGt = c =='<' && file instanceof GroovyFile && GroovyTypedHandler.isAfterClassLikeIdentifier(offset, editor);
}
public boolean charDeleted(final char c, final PsiFile file, final Editor editor) {
int offset = editor.getCaretModel().getOffset();
final CharSequence chars = editor.getDocument().getCharsSequence();
if (editor.getDocument().getTextLength() <= offset) return false; //virtual space after end of file
char c1 = chars.charAt(offset);
if (c == '<' && myToDeleteGt) {
if (c1 != '>') return true;
JavaBackspaceHandler.handleLTDeletion(editor, offset, GroovyTokenTypes.mLT, GroovyTokenTypes.mGT, GroovyTypedHandler.INVALID_INSIDE_REFERENCE);
return true;
}
return false;
}
}
@@ -0,0 +1,73 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.lang.editor.actions;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.editorActions.JavaTypedHandler;
import com.intellij.codeInsight.editorActions.TypedHandlerDelegate;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
/**
* @author peter
*/
public class GroovyTypedHandler extends TypedHandlerDelegate {
static final TokenSet INVALID_INSIDE_REFERENCE = TokenSet.create(GroovyTokenTypes.mSEMI, GroovyTokenTypes.mLCURLY, GroovyTokenTypes.mRCURLY);
private boolean myJavaLTTyped;
public Result beforeCharTyped(final char c, final Project project, final Editor editor, final PsiFile file, final FileType fileType) {
int offsetBefore = editor.getCaretModel().getOffset();
//important to calculate before inserting charTyped
myJavaLTTyped = '<' == c &&
file instanceof GroovyFile &&
CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET &&
isAfterClassLikeIdentifier(offsetBefore, editor);
if ('>' == c) {
if (file instanceof GroovyFile && CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) {
if (JavaTypedHandler.handleJavaGT(editor, GroovyTokenTypes.mLT, GroovyTokenTypes.mGT, INVALID_INSIDE_REFERENCE)) return Result.STOP;
}
}
return Result.CONTINUE;
}
public Result charTyped(final char c, final Project project, final Editor editor, final PsiFile file) {
if (myJavaLTTyped) {
myJavaLTTyped = false;
JavaTypedHandler.handleAfterJavaLT(editor, GroovyTokenTypes.mLT, GroovyTokenTypes.mGT, INVALID_INSIDE_REFERENCE);
return Result.STOP;
}
return Result.CONTINUE;
}
public static boolean isAfterClassLikeIdentifier(final int offset, final Editor editor) {
HighlighterIterator iterator = ((EditorEx) editor).getHighlighter().createIterator(offset);
if (iterator.atEnd()) return false;
if (iterator.getStart() > 0) iterator.retreat();
return JavaTypedHandler.isClassLikeIdentifier(offset, editor, iterator, GroovyTokenTypes.mIDENT);
}
}
@@ -39,4 +39,8 @@ public class GroovyEditingTest extends LightCodeInsightFixtureTestCase {
public void testEnterInMultilineString() throws Throwable { doTest('\n'); }
public void testEnterInStringInRefExpr() throws Throwable {doTest('\n');}
public void testEnterInGStringInRefExpr() throws Throwable {doTest('\n');}
public void testPairAngleBracketAfterClassName() throws Throwable {doTest('<');}
public void testPairAngleBracketAfterClassNameOvertype() throws Throwable {doTest('>');}
public void testPairAngleBracketAfterClassNameBackspace() throws Throwable {doTest('\b');}
public void testNoPairLess() throws Throwable {doTest('<');}
}
+1
View File
@@ -0,0 +1 @@
def foo = x<caret>
@@ -0,0 +1 @@
def foo = x<<caret>
@@ -0,0 +1 @@
Foo<caret>
@@ -0,0 +1 @@
Foo<<caret>>
@@ -0,0 +1 @@
Foo<caret>
@@ -0,0 +1 @@
Foo<x<caret>>
@@ -0,0 +1 @@
Foo<x><caret>
@@ -0,0 +1 @@
Foo<<caret>>