Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Yurgis Baykshtis committed Jul 12, 2018
1 parent 9bfc8fe commit 21b06bd
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,23 @@ abstract class CompletionTestSuite extends GroovyEclipseTestSuite {
assertEquals('Completion proposal applied but different results found.', expect, actual)
}

/**
* Applies the specified completion proposal to the active editor and checks
* against the expected replacement, the initial parameter selection,
* and the target cursor position after after quitting parameter linked mode.
*/
protected void applyProposalAndCheckCursor(ICompletionProposal proposal, String expected,
int expectedSelectionOffset, int expectedSelectionLength = 0, int expectedCursorPosition = expectedSelectionOffset) {

applyProposalAndCheck(proposal, expected)

JavaContentAssistInvocationContext context = proposal.@fInvocationContext

assertEquals("Unexpected selection range offset", expectedSelectionOffset, proposal.getSelection(context.document).x);
assertEquals("Unexpected selection range length", expectedSelectionLength, proposal.getSelection(context.document).y);
assertEquals("Unexpected cursor position", expectedCursorPosition, proposal.replacementOffset + proposal.computeCursorPosition());
}

protected void checkReplacementRegexp(ICompletionProposal[] proposals, String expectedReplacement, int expectedCount) {
int foundCount = 0
for (proposal in proposals) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ final class GroovyLikeCompletionTests extends CompletionTestSuite {
@Test
void testMethodWithClosure() {
ICompletionProposal[] proposals = createProposalsAtOffset(SCRIPTCONTENTS, getIndexOf(SCRIPTCONTENTS, 'any'))
checkReplacementString(proposals, 'any { it }', 1)
checkReplacementString(proposals, 'any { }', 1)
}

@Test
Expand All @@ -102,7 +102,7 @@ final class GroovyLikeCompletionTests extends CompletionTestSuite {
@Test
void testMethodWith2Args() {
ICompletionProposal[] proposals = createProposalsAtOffset(SCRIPTCONTENTS, getIndexOf(SCRIPTCONTENTS, 'findIndexOf'))
checkReplacementRegexp(proposals, /findIndexOf\(\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*\) \{ it \}/, 1)
checkReplacementRegexp(proposals, /findIndexOf\(\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*\) \{ \}/, 1)
}

@Test
Expand Down Expand Up @@ -168,7 +168,7 @@ final class GroovyLikeCompletionTests extends CompletionTestSuite {
addGroovySource(SCRIPTCONTENTS, nextUnitName())

String contents = 'new Foo().method2'
String expected = 'new Foo().method2(arg) { it }'
String expected = 'new Foo().method2(arg) { }'
checkProposalApplicationNonType(contents, expected, contents.length(), 'method2')
}

Expand All @@ -188,7 +188,7 @@ final class GroovyLikeCompletionTests extends CompletionTestSuite {
addGroovySource(SCRIPTCONTENTS, nextUnitName())

String contents = 'new Foo().method2'
String expected = 'new Foo().method2(arg) c1'
String expected = 'new Foo().method2(arg) { }'
checkProposalApplicationNonType(contents, expected, contents.length(), 'method2')
}

Expand All @@ -208,7 +208,7 @@ final class GroovyLikeCompletionTests extends CompletionTestSuite {
addGroovySource(SCRIPTCONTENTS, nextUnitName())

String contents = 'new Foo().method3'
String expected = 'new Foo().method3(arg, { it }) { it }'
String expected = 'new Foo().method3(arg, { it }) { }'
checkProposalApplicationNonType(contents, expected, contents.length(), 'method3')
}

Expand All @@ -228,7 +228,7 @@ final class GroovyLikeCompletionTests extends CompletionTestSuite {
addGroovySource(SCRIPTCONTENTS, nextUnitName())

String contents = 'new Foo().method3'
String expected = 'new Foo().method3(arg, c1) c2'
String expected = 'new Foo().method3(arg, c1) { }'
checkProposalApplicationNonType(contents, expected, contents.length(), 'method3')
}

Expand Down Expand Up @@ -334,7 +334,7 @@ final class GroovyLikeCompletionTests extends CompletionTestSuite {
void testNamedArguments3() {
groovyPrefs.setValue(GroovyContentAssist.NAMED_ARGUMENTS, true)

checkUniqueProposal((SCRIPTCONTENTS - ~/\s*$/) + '.', 'new Foo().', 'method3', 'method3(arg: arg, c1: { it }) { it }')
checkUniqueProposal((SCRIPTCONTENTS - ~/\s*$/) + '.', 'new Foo().', 'method3', 'method3(arg: arg, c1: { it }) { }')
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ final class TriggerCharacterCompletionTests extends CompletionTestSuite {
closure << ' '
}
if (proposal.toString().contains('Closure')) {
closure << 'it'
if (!isEnabled(GroovyContentAssist.CLOSURE_NOPARENS)) {
closure << 'it'
}
} else {
closure << 'o1'
if (isEnabled(FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_ARRAY_INITIALIZER)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package org.codehaus.groovy.eclipse.dsl.tests
import static org.eclipse.jdt.groovy.core.tests.GroovyBundle.isAtLeastGroovy
import static org.junit.Assume.assumeTrue

import org.codehaus.groovy.eclipse.codeassist.GroovyContentAssist
import org.codehaus.groovy.eclipse.codeassist.tests.CompletionTestSuite
import org.codehaus.groovy.eclipse.dsl.DSLPreferencesInitializer
import org.codehaus.groovy.eclipse.dsl.GroovyDSLCoreActivator
Expand Down Expand Up @@ -271,6 +272,146 @@ final class DSLContentAssistTests extends CompletionTestSuite {
checkUniqueProposal(contents, 'baz {', 'var_foo')
}

@Test
void testTrailingClosure1a() {
setJavaPreference(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, 'false') //ensure default
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_BRACKETS, true)
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, true)
createDsld '''\
contribute(currentType()) {
method name: 'bar', type: 'void', params: ['block':Closure]
}
'''.stripIndent()

String contents = 'foo { }'
ICompletionProposal proposal = checkUniqueProposal(contents, 'foo { ', 'bar', 'bar { }')
String expect = contents.replace('{ }', '{ bar { } }')
applyProposalAndCheckCursor(proposal, expect, getIndexOf(expect, 'bar { '), 0)
}

@Test
void testTrailingClosure1b() {
setJavaPreference(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, 'false') //ensure default
//closure brackets should have no effect on a trailing closure when noparens is set
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_BRACKETS, false)
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, true)
createDsld '''\
contribute(currentType()) {
method name: 'bar', type: 'void', params: ['block':Closure]
}
'''.stripIndent()

String contents = 'foo { }'
ICompletionProposal proposal = checkUniqueProposal(contents, 'foo { ', 'bar', 'bar { }')
String expect = contents.replace('{ }', '{ bar { } }')
applyProposalAndCheckCursor(proposal, expect, getIndexOf(expect, 'bar { '), 0)
}

@Test
void testTrailingClosure1c() {
setJavaPreference(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, 'false') //ensure default
//closure brackets should have effect on a trailing closure when noparens is not set
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_BRACKETS, false)
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, false)
createDsld '''\
contribute(currentType()) {
method name: 'bar', type: 'void', params: ['block':Closure]
}
'''.stripIndent()

String contents = 'foo { }'
ICompletionProposal proposal = checkUniqueProposal(contents, 'foo { ', 'bar', 'bar(block)')
String expect = contents.replace('{ }', '{ bar(block) }')
applyProposalAndCheckCursor(proposal, expect, getIndexOf(expect, 'bar('), 5, getIndexOf(expect, 'bar(block)'))
}

@Test
void testTrailingClosure1d() {
setJavaPreference(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, 'false') //ensure default
//closure brackets should have effect on a non-trailing closure
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_BRACKETS, false)
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, true)
createDsld '''\
contribute(currentType()) {
method name: 'bar', type: 'void', params: ['closure':Closure, 'block':Closure]
}
'''.stripIndent()

String contents = 'foo { }'
ICompletionProposal proposal = checkUniqueProposal(contents, 'foo { ', 'bar', 'bar(closure) { }')
String expect = contents.replace('{ }', '{ bar(closure) { } }')
applyProposalAndCheckCursor(proposal, expect, getIndexOf(expect, 'bar('), 7, getIndexOf(expect, 'bar(closure) { '))
}

@Test
void testTrailingClosure1e() {
setJavaPreference(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, 'false') //ensure default
//closure brackets should have effect on a non-trailing closure
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_BRACKETS, true)
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, true)
createDsld '''\
contribute(currentType()) {
method name: 'bar', type: 'void', params: ['closure':Closure, 'block':Closure]
}
'''.stripIndent()

String contents = 'foo { }'
ICompletionProposal proposal = checkUniqueProposal(contents, 'foo { ', 'bar', 'bar({ it }) { }')
String expect = contents.replace('{ }', '{ bar({ it }) { } }')
applyProposalAndCheckCursor(proposal, expect, getIndexOf(expect, 'bar('), 6, getIndexOf(expect, 'bar({ it }) { '))
}

@Test
void testTrailingClosure2a() {
setJavaPreference(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, 'false') //ensure default
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_BRACKETS, true)
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, true)
createDsld '''\
contribute(currentType()) {
method name: 'bar', type: 'void', namedParams:['name':String], params: ['block':Closure]
}
'''.stripIndent()

String contents = 'foo { }'
ICompletionProposal proposal = checkUniqueProposal(contents, 'foo { ', 'bar', 'bar(name: name) { }')
String expect = contents.replace('{ }', '{ bar(name: name) { } }')
applyProposalAndCheckCursor(proposal, expect, getIndexOf(expect, 'name: '), 4, getIndexOf(expect, 'bar(name: name) { '))
}

@Test
void testTrailingClosure2b() {
setJavaPreference(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, 'false') //ensure default
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_BRACKETS, true)
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, false)
createDsld '''\
contribute(currentType()) {
method name: 'bar', type: 'void', namedParams:['name':String], params: ['block':Closure]
}
'''.stripIndent()

String contents = 'foo { }'
ICompletionProposal proposal = checkUniqueProposal(contents, 'foo { ', 'bar', 'bar(name: name, { it })')
String expect = contents.replace('{ }', '{ bar(name: name, { it }) }')
applyProposalAndCheckCursor(proposal, expect, getIndexOf(expect, 'name: '), 4, getIndexOf(expect, '{ it })'))
}

@Test
void testTrailingClosure2c() {
setJavaPreference(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, 'false') //ensure default
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_BRACKETS, false)
GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, false)
createDsld '''\
contribute(currentType()) {
method name: 'bar', type: 'void', namedParams:['name':String], params: ['block':Closure]
}
'''.stripIndent()

String contents = 'foo { }'
ICompletionProposal proposal = checkUniqueProposal(contents, 'foo { ', 'bar', 'bar(name: name, block)')
String expect = contents.replace('{ }', '{ bar(name: name, block) }')
applyProposalAndCheckCursor(proposal, expect, getIndexOf(expect, 'name: '), 4, getIndexOf(expect, 'block)'))
}

@Test
void testDelegatesToNoParens1() {
createDsld '''\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,12 +434,52 @@ public int getContextInformationPosition() {

@Override
public Point getSelection(IDocument document) {
if (fPositions != null && fPositions.size() == 1 && treatTrailingClosureAsCodeBlock()) {
//if only param is a trailing closure immediately place cursor between closure brackets and do not select anything
return new Point(getReplacementOffset() + computeCursorPosition(), 0);
}
if (fSelectedRegion != null) {
return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength());
}
return super.getSelection(document);
}

@Override
protected int computeCursorPosition() {
int pos = super.computeCursorPosition();
if (treatTrailingClosureAsCodeBlock()) {
//put cursor in a middle of a the trailing closure's brackets after linked mode
pos--;
if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_BRACE_IN_ARRAY_INITIALIZER)) {
pos--;
}
}
return pos;
}

@Override
protected boolean needsLinkedMode() {
if (fPositions != null && fPositions.size() == 1 && treatTrailingClosureAsCodeBlock()) {
//if only param is a trailing closure immediately place cursor inside a block: foo { | }
return false;
}
return super.needsLinkedMode();
}

/**
* Helper function to determine if this method has a trailing closure
* and whether it should be treated as a code block rather than a parameter
*
* @return whether the method has a trailing closure representing a code block
*/
private boolean treatTrailingClosureAsCodeBlock() {
if (fPreferences.isEnabled(GroovyContentAssist.CLOSURE_NOPARENS)) {
char[][] regular = ((GroovyCompletionProposal) fProposal).getRegularParameterTypeNames();
return lastParamIsClosure(regular, CharOperation.NO_CHAR_CHAR);
}
return false;
}

@Override
protected void setUpLinkedMode(IDocument document, char closingCharacter) {
if (getTextViewer() != null) {
Expand All @@ -451,7 +491,9 @@ protected void setUpLinkedMode(IDocument document, char closingCharacter) {
group.addPosition(new LinkedPosition(document, baseOffset + fContextInformationPosition, 0));
model.addGroup(group);
} else {
for (int i = 0, n = fPositions.size(); i < n; i += 1) {
//exclude trailing closure it from the linked mode
int n = treatTrailingClosureAsCodeBlock()? fPositions.size() - 1 : fPositions.size();
for (int i = 0; i < n; i += 1) {
Position position = fPositions.get(i);
// change offset from relative to absolute
position.setOffset(baseOffset + position.getOffset());
Expand Down Expand Up @@ -513,14 +555,18 @@ protected void computeReplacementProposals(char[][] namedParameterNames, char[][
Signature.toString(typeSignature), String.valueOf(name), fPositions.get(i), visibleElements, fillBestGuess);
} else {
StringBuilder buffer = new StringBuilder();

if (fPreferences.isEnabled(GroovyContentAssist.CLOSURE_BRACKETS) && CharOperation.equals(CLOSURE_TYPE_SIGNATURE, type, 1, type.length)) {
boolean isClosure = CharOperation.equals(CLOSURE_TYPE_SIGNATURE, type, 1, type.length);
boolean isTrailingClosure = isClosure && i == n - 1;
boolean isCodeBlock = isTrailingClosure && treatTrailingClosureAsCodeBlock();
if (isCodeBlock || fPreferences.isEnabled(GroovyContentAssist.CLOSURE_BRACKETS) && isClosure) {
buffer.append("{");
if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_BRACE_IN_ARRAY_INITIALIZER)) {
buffer.append(SPACE);
}

buffer.append("it");
if (!isCodeBlock) { //suppress 'it' for the trailing closures when NOPARENS is set
buffer.append("it");
}

if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_BRACE_IN_ARRAY_INITIALIZER)) {
buffer.append(SPACE);
Expand Down

0 comments on commit 21b06bd

Please sign in to comment.