From fa25797d05eda3c30fd8aef9242e4ccf5d70b419 Mon Sep 17 00:00:00 2001 From: "Maxim.Medvedev" Date: Sat, 4 Aug 2012 20:31:51 +0400 Subject: [PATCH] new select word handlers for Groovy --- plugins/groovy/src/META-INF/plugin.xml | 4 +- .../GroovyInspectionBundle.properties | 182 +++++++++--------- .../GroovyAssignabilityCheckInspection.java | 18 +- .../GroovyFieldSemicolonSelectioner.java | 56 ------ .../selection/GroovyGStringSelectioner.java | 2 +- .../selection/GroovyStatementSelectioner.java | 143 ++++++++++++++ .../selection/GroovyWordSelectionFilter.java | 16 +- .../selection/GroovyWordSelectionHandler.java | 91 +++++++++ .../plugins/groovy/GroovyActionsTest.groovy | 42 +++- 9 files changed, 392 insertions(+), 162 deletions(-) delete mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyFieldSemicolonSelectioner.java create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyStatementSelectioner.java create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyWordSelectionHandler.java diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 390479e5847f..a6e4e0807f6b 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -366,11 +366,13 @@ - + + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties index b98744badf14..c0e067a77652 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties @@ -1,91 +1,91 @@ -# -# Copyright 2000-2007 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. -# - -######################################################################################################################## -# Unuzed imports -######################################################################################################################## -unused.import=Unused import -optimize.imports=Optimize imports -optimize.all.imports=Optimize unused import statements - -######################################################################################################################## -# Create class fix -######################################################################################################################## -dialog.create.class.package.chooser.title=Choose destination package -create.directory.command=Create directory -destination.package=Destination package: -dialog.create.class.label.0=Create class ''{0}'' -second.unsafe.call=Second unsafe call -call.can.throw.npe=Calling chain can throw NullPointerException - -groovy.dfa.issues=Data flow issues - -unused.assignment=Unused Assignment -unused.assignment.tooltip=Assignment is not used - -unused.inc.dec=Unused Incrementing or Decrementing - -unassigned.access=Variable Not Assigned -unassigned.access.short.name=VariableNotAssigned -unassigned.access.tooltip=Variable ''{0}'' might not be assigned -no.return.display.name=Missing Return Statement -no.return.short.name=MissingReturnStatement -no.return.message=Not all execution paths return a value -create.dynamic.property=Create dynamic property -dynamic.property=Dynamic property -unresolved.type.status=Type ''{0}'' is not resolved -resolved.type.status=Type ''{0}'' is resolved -no.type.specified=No type specified -dynamic.element=Dynamic Element - -######################################################################################################################## -# InpectorGroovy properties -######################################################################################################################## -pointless.boolean.problem.descriptor=#ref can be simplified to ''{0}'' #loc -pointless.arithmetic.error.message=#ref can be replaced with ''{0}'' #loc -pointless.boolean.display.name=Pointless boolean expression -pointless.boolean.quickfix=Simplify - -Cannot.perform.undo.operation=Cannot perform undo operation -Undo.disable=Undo Disabled -field.already.defined=Variables with field names -clashing.getters=Clashing getters - -access.to.inaccessible.element=Access to inaccessible element - -cannot.reference.nonstatic=Cannot reference nonstatic symbol ''{0}'' from static context - -type.doesnt.contain.method=Type ''{0}'' cannot be iterated in range because it does not have method ''{1}'' -incorrect.range.argument=Incorrect range arguments -type.doesnt.implemnt.comparable=Type ''{0}'' doesnt implement Comparable -add.method=Add method ''{0}()'' to class ''{1}'' -implement.class=Implement {0} -fix.class=Fix class {0} -check.labeled.statement=Labeled statement inspection -unnecessary.qualified.reference=Unnecessary qualified reference -rtype.cannot.contain.ltype=''{1}'' cannot contain ''{0}'' -new.instance.of.singleton=New instance of class annotated with @groovy.lang.Singleton -replace.new.expression.with.0.instance=Replace with ''{0}.instance'' -getter.0.clashes.with.getter.1={0} clashes with {1} -unused.0=Unused {0} -remove.0=Remove {0} -replace.postfix.0.with.prefix.0=Replace postfix {0} with prefix {0} -replace.0.with.1=Replace {0} with binary {1} -gr.deprecated.api.usage=Deprecated API inspection -category,method.0.cannot.be.applied.to.1=Category method ''{0}'' cannot be applied to ''{1}'' -local.var.0.is.reassigned.in.closure=Local variable {0} is reassigned in {1} with other type -anonymous.class=anonymous class -closure=closure -other.scope=Other scope +# +# Copyright 2000-2007 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. +# + +######################################################################################################################## +# Unuzed imports +######################################################################################################################## +unused.import=Unused import +optimize.imports=Optimize imports +optimize.all.imports=Optimize unused import statements + +######################################################################################################################## +# Create class fix +######################################################################################################################## +dialog.create.class.package.chooser.title=Choose destination package +create.directory.command=Create directory +destination.package=Destination package: +dialog.create.class.label.0=Create class ''{0}'' +second.unsafe.call=Second unsafe call +call.can.throw.npe=Calling chain can throw NullPointerException + +groovy.dfa.issues=Data flow issues + +unused.assignment=Unused Assignment +unused.assignment.tooltip=Assignment is not used + +unused.inc.dec=Unused Incrementing or Decrementing + +unassigned.access=Variable Not Assigned +unassigned.access.short.name=VariableNotAssigned +unassigned.access.tooltip=Variable ''{0}'' might not be assigned +no.return.display.name=Missing Return Statement +no.return.short.name=MissingReturnStatement +no.return.message=Not all execution paths return a value +create.dynamic.property=Create dynamic property +dynamic.property=Dynamic property +unresolved.type.status=Type ''{0}'' is not resolved +resolved.type.status=Type ''{0}'' is resolved +no.type.specified=No type specified +dynamic.element=Dynamic Element + +######################################################################################################################## +# InpectorGroovy properties +######################################################################################################################## +pointless.boolean.problem.descriptor=#ref can be simplified to ''{0}'' #loc +pointless.arithmetic.error.message=#ref can be replaced with ''{0}'' #loc +pointless.boolean.display.name=Pointless boolean expression +pointless.boolean.quickfix=Simplify + +Cannot.perform.undo.operation=Cannot perform undo operation +Undo.disable=Undo Disabled +field.already.defined=Variables with field names +clashing.getters=Clashing getters + +access.to.inaccessible.element=Access to inaccessible element + +cannot.reference.nonstatic=Cannot reference nonstatic symbol ''{0}'' from static context + +type.doesnt.contain.method=Type ''{0}'' cannot be iterated in range because it does not have method ''{1}'' +incorrect.range.argument=Incorrect range arguments +type.doesnt.implemnt.comparable=Type ''{0}'' doesnt implement Comparable +add.method=Add method ''{0}()'' to class ''{1}'' +implement.class=Implement {0} +fix.class=Fix class {0} +check.labeled.statement=Labeled statement inspection +unnecessary.qualified.reference=Unnecessary qualified reference +rtype.cannot.contain.ltype=''{1}'' cannot contain ''{0}'' +new.instance.of.singleton=New instance of class annotated with @groovy.lang.Singleton +replace.new.expression.with.0.instance=Replace with ''{0}.instance'' +getter.0.clashes.with.getter.1={0} clashes with {1} +unused.0=Unused {0} +remove.0=Remove {0} +replace.postfix.0.with.prefix.0=Replace postfix {0} with prefix {0} +replace.0.with.1=Replace {0} with binary {1} +gr.deprecated.api.usage=Deprecated API inspection +category.method.0.cannot.be.applied.to.1=Category method ''{0}'' cannot be applied to ''{1}'' +local.var.0.is.reassigned.in.closure=Local variable {0} is reassigned in {1} with other type +anonymous.class=anonymous class +closure=closure +other.scope=Other scope diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GroovyAssignabilityCheckInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GroovyAssignabilityCheckInspection.java index f8bf7e9ec443..d040d25406c5 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GroovyAssignabilityCheckInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GroovyAssignabilityCheckInspection.java @@ -293,6 +293,8 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection { private void checkConstructorCall(GrConstructorCall constructorCall, GroovyPsiElement refElement) { final GrArgumentList argList = constructorCall.getArgumentList(); + if (hasErrorElements(argList)) return; + if (!checkCannotInferArgumentTypes(refElement)) return; final GroovyResolveResult constructorResolveResult = constructorCall.advancedResolve(); final PsiElement constructor = constructorResolveResult.getElement(); @@ -555,7 +557,21 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection { } } + /** + * checks only children of e + */ + private static boolean hasErrorElements(@Nullable PsiElement e) { + if (e == null) return false; + + for (PsiElement child = e.getFirstChild(); child != null; child = child.getNextSibling()) { + if (child instanceof PsiErrorElement) return true; + } + return false; + } + private void checkMethodCall(GrMethodCall call) { + if (hasErrorElements(call.getArgumentList())) return; + final GrExpression expression = call.getInvokedExpression(); if (!(expression instanceof GrReferenceExpression)) { //it checks in visitRefExpr(...) final PsiType type = expression.getType(); @@ -709,7 +725,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection { final PsiType qualifier = inferQualifierTypeByPlace((GrReferenceExpression)place); if (qualifier != null && !MixinMemberContributor.isCategoryMethod(staticMethod, qualifier, methodResolveResult.getSubstitutor())) { registerError(((GrReferenceExpression)place).getReferenceNameElement(), - GroovyInspectionBundle.message("category,method.0.cannot.be.applied.to.1", method.getName(), + GroovyInspectionBundle.message("category.method.0.cannot.be.applied.to.1", method.getName(), qualifier.getCanonicalText())); return false; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyFieldSemicolonSelectioner.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyFieldSemicolonSelectioner.java deleted file mode 100644 index 069415407696..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyFieldSemicolonSelectioner.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2000-2012 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.editor.selection; - -import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; -import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * @author Max Medvedev - */ -public class GroovyFieldSemicolonSelectioner extends ExtendWordSelectionHandlerBase { - @Override - public boolean canSelect(PsiElement e) { - return e instanceof GrVariableDeclaration; - } - - @Override - public List select(PsiElement e, CharSequence editorText, int cursorOffset, Editor editor) { - PsiElement next = e.getNextSibling(); - while (next != null && - next.getNode() != null && - TokenSets.WHITE_SPACES_SET.contains(next.getNode().getElementType()) && - !next.textContains('\n')) { - next = next.getNextSibling(); - } - - final List ranges = new ArrayList(); - ranges.add(e.getTextRange()); - if (next != null && next.getNode().getElementType() == GroovyTokenTypes.mSEMI) { - ranges.add(new TextRange(e.getTextRange().getStartOffset(), next.getTextRange().getEndOffset())); - } - return ranges; - } -} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyGStringSelectioner.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyGStringSelectioner.java index 2a1e1cf5a3f9..02793cb424c3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyGStringSelectioner.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyGStringSelectioner.java @@ -42,7 +42,7 @@ public class GroovyGStringSelectioner extends ExtendWordSelectionHandlerBase { @Override public List select(PsiElement e, CharSequence editorText, int cursorOffset, Editor editor) { - List ranges = new ArrayList(); + List ranges = super.select(e, editorText, cursorOffset, editor); PsiElement parent = e.getParent(); if (parent instanceof GrString) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyStatementSelectioner.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyStatementSelectioner.java new file mode 100644 index 000000000000..14c165ebe7f4 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyStatementSelectioner.java @@ -0,0 +1,143 @@ +/* + * Copyright 2000-2012 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.editor.selection; + +import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandler; +import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiWhiteSpace; +import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; +import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; + +import java.util.List; + +/** + * @author Max Medvedev + */ +public class GroovyStatementSelectioner implements ExtendWordSelectionHandler { + @Override + public boolean canSelect(PsiElement e) { + return e instanceof GrExpression && PsiUtil.isExpressionStatement(e) || + (e instanceof GrStatement && !(e instanceof GrExpression)) || + e.getNode().getElementType() == GroovyTokenTypes.mSEMI; + } + + @Override + public List select(PsiElement e, CharSequence editorText, int cursorOffset, Editor editor) { + TextRange originalRange; + + PsiElement first; + PsiElement last; + if (e instanceof GrStatement) { + first = e; + PsiElement next = e.getNextSibling(); + next = skipWhitespacesForward(next); + if (next != null && next.getNode().getElementType() == GroovyTokenTypes.mSEMI) { + originalRange = new TextRange(e.getTextRange().getStartOffset(), next.getTextRange().getEndOffset()); + last = next; + } + else { + originalRange = e.getTextRange(); + last = e; + } + } + + else { + last = e; + PsiElement prev = e.getPrevSibling(); + prev = skipWhitespaceBack(prev); + if (prev instanceof GrStatement) { + originalRange = new TextRange(prev.getTextRange().getStartOffset(), e.getTextRange().getEndOffset()); + first = prev; + } + else { + originalRange = e.getTextRange(); + first = e; + } + } + + + final List ranges = ExtendWordSelectionHandlerBase.expandToWholeLine(editorText, originalRange); + + + final TextRange blockRange = inferBlockRange(first, last); + + if (!blockRange.equals(originalRange)) { + ranges.addAll(ExtendWordSelectionHandlerBase.expandToWholeLine(editorText, blockRange, true)); + } + + return ranges; + } + + private static TextRange inferBlockRange(PsiElement first, PsiElement last) { + while (true) { + PsiElement prev = first.getPrevSibling(); + + prev = skipWhitespaceBack(prev); + if (isOneLineFeed(prev)) prev = prev.getPrevSibling(); + prev = skipWhitespaceBack(prev); + if (prev != null && prev.getNode().getElementType() == GroovyTokenTypes.mSEMI || prev instanceof GrStatement) { + first = prev; + } + else { + break; + } + } + + while (true) { + PsiElement next = last.getNextSibling(); + + next = skipWhitespacesForward(next); + if (isOneLineFeed(next)) next = next.getNextSibling(); + next = skipWhitespacesForward(next); + if (next != null && next.getNode().getElementType() == GroovyTokenTypes.mSEMI || next instanceof GrStatement) { + last = next; + } + else { + break; + } + } + + return new TextRange(first.getTextRange().getStartOffset(), last.getTextRange().getEndOffset()); + } + + private static PsiElement skipWhitespacesForward(PsiElement next) { + while (isSpaceWithoutLineFeed(next)) next = next.getNextSibling(); + return next; + } + + private static PsiElement skipWhitespaceBack(PsiElement prev) { + while (isSpaceWithoutLineFeed(prev)) prev = prev.getPrevSibling(); + return prev; + } + + private static boolean isOneLineFeed(PsiElement e) { + if (e == null) return false; + if (!TokenSets.WHITE_SPACES_SET.contains(e.getNode().getElementType())) return false; + + final String text = e.getText(); + return text.indexOf('\n') == text.lastIndexOf('\n'); + } + + private static boolean isSpaceWithoutLineFeed(PsiElement e) { + return e instanceof PsiWhiteSpace && e.getText().indexOf('\n') == -1; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyWordSelectionFilter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyWordSelectionFilter.java index 2f946f54f7a7..9dc7c35d5e5f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyWordSelectionFilter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyWordSelectionFilter.java @@ -15,24 +15,18 @@ */ package org.jetbrains.plugins.groovy.editor.selection; -import com.intellij.lang.ASTNode; import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiElement; -import com.intellij.psi.tree.IElementType; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList; - -import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*; +import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; /** * @author Maxim.Medvedev */ public class GroovyWordSelectionFilter implements Condition { public boolean value(PsiElement element) { - - final ASTNode node = element.getNode(); +return !(element instanceof GroovyPsiElement || element.getLanguage() == GroovyFileType.GROOVY_LANGUAGE); +/* final ASTNode node = element.getNode(); if (node == null) return false; final IElementType type = node.getElementType(); @@ -61,6 +55,6 @@ public class GroovyWordSelectionFilter implements Condition { !(element instanceof GrArgumentList) && !(type == mDOLLAR) - ; + ;*/ } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyWordSelectionHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyWordSelectionHandler.java new file mode 100644 index 000000000000..e19137ef12ea --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/editor/selection/GroovyWordSelectionHandler.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2012 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.editor.selection; + +import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandler; +import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase; +import com.intellij.codeInsight.editorActions.SelectWordUtil; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.impl.source.tree.LeafPsiElement; +import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrClassInitializer; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Max Medvedev + */ +public class GroovyWordSelectionHandler implements ExtendWordSelectionHandler { + private static final Logger LOG = Logger.getInstance(GroovyWordSelectionHandler.class); + + @Override + public boolean canSelect(PsiElement e) { + return (e instanceof GroovyPsiElement || e.getLanguage() == GroovyFileType.GROOVY_LANGUAGE) && + !(e.getNode().getElementType() == GroovyTokenTypes.mDOLLAR); + } + + @Override + public List select(PsiElement e, CharSequence editorText, int cursorOffset, Editor editor) { + final TextRange originalRange = e.getTextRange(); + LOG.assertTrue(originalRange.getEndOffset() <= editorText.length(), getClass() + "; " + e); + + List ranges; + if (isSeparateStatement(e)) { + ranges = ExtendWordSelectionHandlerBase.expandToWholeLine(editorText, originalRange, true); + if (ranges.size() == 1 && ranges.contains(originalRange)) { + ranges = ExtendWordSelectionHandlerBase.expandToWholeLine(editorText, originalRange, false); + } + } + else { + ranges = new ArrayList(); + ranges.add(e.getTextRange()); + } + + SelectWordUtil.addWordSelection(editor.getSettings().isCamelWords(), editorText, cursorOffset, ranges); + + return ranges; + } + + private static boolean isSeparateStatement(PsiElement e) { + return + e instanceof LeafPsiElement || + e instanceof GrExpression && PsiUtil.isExpressionStatement(e) || + e instanceof GrVariableDeclaration || + e instanceof GrStatement && !(e instanceof GrExpression) || + e instanceof GrMethod || + e instanceof GrClassInitializer || + e instanceof GrImportStatement || + e instanceof GrPackageDefinition || + e instanceof GrOpenBlock || + e instanceof GrTypeDefinition && !((GrTypeDefinition)e).isAnonymous() + ; + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyActionsTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyActionsTest.groovy index 9aabfc97ea65..277c9324e357 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyActionsTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyActionsTest.groovy @@ -57,7 +57,7 @@ public class GroovyActionsTest extends LightCodeInsightFixtureTestCase { "a.foo(b)" } - public void testSWInCodeBlock() {doTestForSelectWord 3} + public void testSWInCodeBlock() {doTestForSelectWord 5} public void testElseBranch() { doTestForSelectWord (3, '''\ @@ -77,6 +77,46 @@ def foo() { ''') } + void testBlocksOfCode() { + doTestForSelectWord(8, '''\ +this.allOptions = [:]; + confTag.option.each{ opt -> + def value = opt.'@value'; + if (value == null) { + value = opt.value ? opt.value[0].'@defaultName' : null; + } + this.allOptions[opt.'@name'] = value; + } + + def moduleNode = confTag.module[0] ; + if (moduleNode != null && !"wholeProject".equals(this.allOptions['TEST_SEARCH_SCOPE'])) { + this.moduleRef = JpsElementFactory.instance.createModuleReference(moduleNode.'@name'); + } else { + this.moduleRef = null; + } + + this.macroExpander = macroExpander; +''', '''\ +this.allOptions = [:]; + confTag.option.each{ opt -> + def value = opt.'@value'; + if (value == null) { + value = opt.value ? opt.value[0].'@defaultName' : null; + } + this.allOptions[opt.'@name'] = value; + } + + def moduleNode = confTag.module[0] ; + if (moduleNode != null && !"wholeProject".equals(this.allOptions['TEST_SEARCH_SCOPE'])) { + this.moduleRef = JpsElementFactory.instance.createModuleReference(moduleNode.'@name'); + } else { + this.moduleRef = null; + } + + this.macroExpander = macroExpander; +''') + } + private void doTestForSelectWord(int count, String input, String expected) { myFixture.configureByText("a.groovy", input); selectWord(count)