Commit 45bf3b3b authored by Arthur Sadykov's avatar Arthur Sadykov Committed by Laszlo Kishalmi
Browse files

[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
parent 3e742325
......@@ -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) {
......
......@@ -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));
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment