This is an automated email from the ASF dual-hosted git repository. lkishalmi pushed a commit to branch release122 in repository https://gitbox.apache.org/repos/asf/netbeans.git
commit 45bf3b3beec5ee7bb1c41778a2be8ebb8b3a2f95 Author: Arthur Sadykov <arthur-sady...@mail.ru> AuthorDate: Fri Oct 23 06:08:21 2020 +0500 [NETBEANS-3588] Code Templates not working in Java Editor in for loops (#2444) * [NETBEANS-3588] Code Templates not working in Java Editor in for loops * [NETBEANS-3588] Code Templates not working in Java Editor in for loops --- .../editor/java/JavaCodeTemplateFilter.java | 175 ++++++++++++--------- .../editor/java/JavaCodeTemplateProcessorTest.java | 47 +++++- 2 files changed, 150 insertions(+), 72 deletions(-) diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCodeTemplateFilter.java b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCodeTemplateFilter.java index 3d8b6ef..bbfbe4f 100644 --- a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCodeTemplateFilter.java +++ b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCodeTemplateFilter.java @@ -20,27 +20,28 @@ package org.netbeans.modules.editor.java; import com.sun.source.tree.Tree; - import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Logger; import javax.swing.text.JTextComponent; - import com.sun.source.tree.CaseTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.ExpressionTree; import com.sun.source.util.SourcePositions; +import com.sun.source.util.TreePath; import org.netbeans.api.java.lexer.JavaTokenId; import org.netbeans.api.java.source.CompilationController; import org.netbeans.api.java.source.JavaSource.Phase; import org.netbeans.api.java.source.SourceUtils; import org.netbeans.api.java.source.TreeUtilities; +import org.netbeans.api.lexer.Token; +import org.netbeans.api.lexer.TokenHierarchy; +import org.netbeans.api.lexer.TokenId; import org.netbeans.api.lexer.TokenSequence; -import org.netbeans.api.progress.ProgressUtils; +import org.netbeans.api.progress.BaseProgressUtils; import org.netbeans.lib.editor.codetemplates.api.CodeTemplate; import org.netbeans.lib.editor.codetemplates.spi.CodeTemplateFilter; import org.netbeans.modules.parsing.api.ParserManager; @@ -58,7 +59,6 @@ import org.openide.util.NbBundle; */ public class JavaCodeTemplateFilter implements CodeTemplateFilter { - private static final Logger LOG = Logger.getLogger(JavaCodeTemplateFilter.class.getName()); private static final String EXPRESSION = "EXPRESSION"; //NOI18N private static final String CLASS_HEADER = "CLASS_HEADER"; //NOI18N @@ -72,88 +72,121 @@ public class JavaCodeTemplateFilter implements CodeTemplateFilter { final Source source = Source.create(component.getDocument()); if (source != null) { final AtomicBoolean cancel = new AtomicBoolean(); - ProgressUtils.runOffEventDispatchThread(new Runnable() { - @Override - public void run() { - try { - ParserManager.parse(Collections.singleton(source), new UserTask() { - @Override - public void run(ResultIterator resultIterator) throws Exception { - if (cancel.get()) { - return; + BaseProgressUtils.runOffEventDispatchThread(() -> { + try { + ParserManager.parse(Collections.singleton(source), new UserTask() { + @Override + public void run(ResultIterator resultIterator) throws Exception { + if (cancel.get()) { + return; + } + Parser.Result result = resultIterator.getParserResult(startOffset); + CompilationController controller = result != null ? CompilationController.get(result) : null; + if (controller != null && Phase.PARSED.compareTo(controller.toPhase(Phase.PARSED)) <= 0) { + TreeUtilities tu = controller.getTreeUtilities(); + int eo = endOffset; + int so = startOffset; + if (so >= 0) { + so = result.getSnapshot().getEmbeddedOffset(startOffset); } - Parser.Result result = resultIterator.getParserResult(startOffset); - CompilationController controller = result != null ? CompilationController.get(result) : null; - if (controller != null && Phase.PARSED.compareTo(controller.toPhase(Phase.PARSED)) <= 0) { - TreeUtilities tu = controller.getTreeUtilities(); - int eo = endOffset; - int so = startOffset; - if (so >= 0) { - so = result.getSnapshot().getEmbeddedOffset(startOffset); - } - if (endOffset >= 0) { - eo = result.getSnapshot().getEmbeddedOffset(endOffset); - TokenSequence<JavaTokenId> ts = SourceUtils.getJavaTokenSequence(controller.getTokenHierarchy(), so); - int delta = ts.move(so); + if (endOffset >= 0) { + eo = result.getSnapshot().getEmbeddedOffset(endOffset); + TokenSequence<JavaTokenId> ts = SourceUtils.getJavaTokenSequence(controller.getTokenHierarchy(), so); + int delta = ts.move(so); + if (delta == 0 || ts.moveNext() && ts.token().id() == JavaTokenId.WHITESPACE) { + delta = ts.move(eo); if (delta == 0 || ts.moveNext() && ts.token().id() == JavaTokenId.WHITESPACE) { - delta = ts.move(eo); - if (delta == 0 || ts.moveNext() && ts.token().id() == JavaTokenId.WHITESPACE) { - String selectedText = controller.getText().substring(so, eo).trim(); - SourcePositions[] sp = new SourcePositions[1]; - ExpressionTree expr = selectedText.length() > 0 ? tu.parseExpression(selectedText, sp) : null; - if (expr != null && expr.getKind() != Tree.Kind.IDENTIFIER && !Utilities.containErrors(expr) && sp[0].getEndPosition(null, expr) >= selectedText.length()) { - stringCtx = EXPRESSION; - } + String selectedText = controller.getText().substring(so, eo).trim(); + SourcePositions[] sp = new SourcePositions[1]; + ExpressionTree expr = selectedText.length() > 0 ? tu.parseExpression(selectedText, sp) : null; + if (expr != null && expr.getKind() != Tree.Kind.IDENTIFIER && !Utilities.containErrors(expr) && sp[0].getEndPosition(null, expr) >= selectedText.length()) { + stringCtx = EXPRESSION; } } } - Tree tree = tu.pathFor(so).getLeaf(); - if (eo >= 0 && so != eo) { - if (tu.pathFor(eo).getLeaf() != tree) { - return; - } + } + Tree tree = tu.pathFor(so).getLeaf(); + if (eo >= 0 && so != eo) { + if (tu.pathFor(eo).getLeaf() != tree) { + return; } - treeKindCtx = tree.getKind(); - switch (treeKindCtx) { - case CASE: - if (so < controller.getTrees().getSourcePositions().getEndPosition(controller.getCompilationUnit(), ((CaseTree)tree).getExpression())) { - treeKindCtx = null; - } - break; - case CLASS: - SourcePositions sp = controller.getTrees().getSourcePositions(); - int startPos = (int)sp.getEndPosition(controller.getCompilationUnit(), ((ClassTree)tree).getModifiers()); - if (startPos <= 0) { - startPos = (int)sp.getStartPosition(controller.getCompilationUnit(), tree); - } - String headerText = controller.getText().substring(startPos, so); - int idx = headerText.indexOf('{'); //NOI18N - if (idx < 0) { - treeKindCtx = null; - stringCtx = CLASS_HEADER; - } - break; - case FOR_LOOP: - case ENHANCED_FOR_LOOP: - case WHILE_LOOP: - sp = controller.getTrees().getSourcePositions(); + } + treeKindCtx = tree.getKind(); + switch (treeKindCtx) { + case CASE: + if (so < controller.getTrees().getSourcePositions().getEndPosition(controller.getCompilationUnit(), ((CaseTree)tree).getExpression())) { + treeKindCtx = null; + } + break; + case CLASS: + SourcePositions sp = controller.getTrees().getSourcePositions(); + int startPos = (int)sp.getEndPosition(controller.getCompilationUnit(), ((ClassTree)tree).getModifiers()); + if (startPos <= 0) { startPos = (int)sp.getStartPosition(controller.getCompilationUnit(), tree); - String text = controller.getText().substring(startPos, so); - if (!text.trim().endsWith(")")) { + } + String headerText = controller.getText().substring(startPos, so); + int idx = headerText.indexOf('{'); //NOI18N + if (idx < 0) { + treeKindCtx = null; + stringCtx = CLASS_HEADER; + } + break; + case FOR_LOOP: + case ENHANCED_FOR_LOOP: + if (!isRightParenthesisOfLoopPresent(controller, so)) { + treeKindCtx = null; + } + break; + case PARENTHESIZED: + if (isPartOfWhileLoop(controller, so)) { + if (!isRightParenthesisOfLoopPresent(controller, so)) { treeKindCtx = null; } - } + } + break; } } - }); - } catch (ParseException ex) { - Exceptions.printStackTrace(ex); - } + } + }); + } catch (ParseException ex) { + Exceptions.printStackTrace(ex); } }, NbBundle.getMessage(JavaCodeTemplateProcessor.class, "JCT-init"), cancel, false); //NOI18N } } } + + private boolean isRightParenthesisOfLoopPresent(CompilationController controller, int abbrevStartOffset) { + TokenHierarchy<?> tokenHierarchy = controller.getTokenHierarchy(); + TokenSequence<?> tokenSequence = tokenHierarchy.tokenSequence(); + tokenSequence.move(abbrevStartOffset); + if (tokenSequence.moveNext()) { + TokenId tokenId = skipNextWhitespaces(tokenSequence); + return tokenId == null ? false : (tokenId == JavaTokenId.RPAREN); + } + return false; + } + + private TokenId skipNextWhitespaces(TokenSequence<?> tokenSequence) { + TokenId tokenId = null; + while (tokenSequence.moveNext()) { + Token<?> token = tokenSequence.token(); + if (token != null) { + tokenId = token.id(); + } + if (tokenId != JavaTokenId.WHITESPACE) { + break; + } + } + return tokenId; + } + + private boolean isPartOfWhileLoop(CompilationController controller, int abbrevStartOffset) { + TreeUtilities treeUtilities = controller.getTreeUtilities(); + TreePath currentPath = treeUtilities.pathFor(abbrevStartOffset); + TreePath parentPath = treeUtilities.getPathElementOfKind(Tree.Kind.WHILE_LOOP, currentPath); + return parentPath != null; + } @Override public synchronized boolean accept(CodeTemplate template) { diff --git a/java/java.editor/test/unit/src/org/netbeans/modules/editor/java/JavaCodeTemplateProcessorTest.java b/java/java.editor/test/unit/src/org/netbeans/modules/editor/java/JavaCodeTemplateProcessorTest.java index 08b5d41..e4843ca 100644 --- a/java/java.editor/test/unit/src/org/netbeans/modules/editor/java/JavaCodeTemplateProcessorTest.java +++ b/java/java.editor/test/unit/src/org/netbeans/modules/editor/java/JavaCodeTemplateProcessorTest.java @@ -157,7 +157,52 @@ public class JavaCodeTemplateProcessorTest extends NbTestCase { "}"); assertFileObjectTextMatchesRegex("(?s)\\s*?public class Test.*?"); } - + + public void testCodeTemplatesShouldWorkInsideParenthesesOfForEachLoop() throws Exception { + doTestTemplateInsert("${name newVarName}", + "public class Test {\n" + + " private void t(String... args) {\n" + + " for (String |) {\n" + + " }\n" + + " }\n" + + "}", + "public class Test {\n" + + " private void t(String... args) {\n" + + " for (String name|) {\n" + + " }\n" + + " }\n" + + "}"); + doTestTemplateInsert("${names iterable}", + "public class Test {\n" + + " private void t(String... args) {\n" + + " for (String name: |) {\n" + + " }\n" + + " }\n" + + "}", + "public class Test {\n" + + " private void t(String... args) {\n" + + " for (String name: args|) {\n" + + " }\n" + + " }\n" + + "}"); + } + + public void testCodeTemplatesShouldWorkInsideParenthesesOfWhileLoop() throws Exception { + doTestTemplateInsert("${list instanceof=\"java.util.List\"}.isEmpty()", + "public class Test {\n" + + " private void t(String... args) {\n" + + " while (|) {\n" + + " }\n" + + " }\n" + + "}", + "public class Test {\n" + + " private void t(String... args) {\n" + + " while (list|.isEmpty()) {\n" + + " }\n" + + " }\n" + + "}"); + } + private void assertFileObjectTextMatchesRegex(String regex) throws IOException { String text = testFile.asText(); assertTrue("The file text must match the regular expression", text.matches(regex)); --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists