diff --git a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/CompletionTestSuite.groovy b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/CompletionTestSuite.groovy index 5f954e9e96..fa9035cfcf 100644 --- a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/CompletionTestSuite.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/CompletionTestSuite.groovy @@ -46,6 +46,7 @@ import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext import org.eclipse.jface.text.Document import org.eclipse.jface.text.IDocument import org.eclipse.jface.text.contentassist.ICompletionProposal +import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2 import org.junit.After import org.junit.AfterClass import org.junit.Before @@ -90,7 +91,9 @@ abstract class CompletionTestSuite extends GroovyEclipseTestSuite { protected ICompletionProposal[] performContentAssist(ICompilationUnit unit, int offset, Class computerClass) { JavaEditor editor = openInEditor(unit) SynchronizationUtils.waitForIndexingToComplete(unit) - JavaSourceViewer viewer = (JavaSourceViewer) editor.viewer + JavaSourceViewer viewer = editor.viewer + viewer.setSelectedRange(offset, 0) + JavaContentAssistInvocationContext context = new JavaContentAssistInvocationContext(viewer, offset, editor) SimpleProgressMonitor monitor = new SimpleProgressMonitor("Create completion proposals for $unit.elementName") List proposals = computerClass.newInstance().computeCompletionProposals(context, monitor) @@ -162,7 +165,7 @@ abstract class CompletionTestSuite extends GroovyEclipseTestSuite { int i = indexOfProposal(proposals, name, 0, isType) if (i != -1) return proposals[i] - fail("Expected at least one proposal that matches '$name', but found none") + fail("Expected at least one proposal that matches '$name', but found none.") } protected void applyProposalAndCheck(IDocument document, ICompletionProposal proposal, String expected) { @@ -172,6 +175,21 @@ 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 result. Assumes performContentAssist(...) was called + * by some means to get {@code proposal}. + */ + protected void applyProposalAndCheck(ICompletionProposal proposal, String expected, char trigger = 0, int stateMask = 0) { + assert proposal instanceof ICompletionProposalExtension2 + JavaContentAssistInvocationContext context = proposal.@fInvocationContext + proposal.apply(context.viewer, trigger, stateMask, context.invocationOffset) + + String expect = expected.normalize() + String actual = context.document.get().normalize() + assertEquals('Completion proposal applied but different results found.', expect, actual) + } + protected void checkReplacementRegexp(ICompletionProposal[] proposals, String expectedReplacement, int expectedCount) { int foundCount = 0 for (proposal in proposals) { @@ -308,7 +326,7 @@ abstract class CompletionTestSuite extends GroovyEclipseTestSuite { private String elementsToNames(IJavaElement[] visibleElements) { String[] names = new String[visibleElements.length] - for (int i = 0; i < names.length; i++) { + for (int i = 0; i < names.length; i += 1) { names[i] = visibleElements[i].elementName } return Arrays.toString(names) diff --git a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/ConstructorCompletionTests.groovy b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/ConstructorCompletionTests.groovy index f7a5940320..e656960ea1 100644 --- a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/ConstructorCompletionTests.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/ConstructorCompletionTests.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,8 +32,9 @@ final class ConstructorCompletionTests extends CompletionTestSuite { // filter some legacy packages setJavaPreference(PreferenceConstants.TYPEFILTER_ENABLED, 'sun.*;com.sun.*;org.omg.*') + setJavaPreference(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES, 'true') + setJavaPreference(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, 'true') GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, false) - GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.PARAMETER_GUESSING, true) } @Test diff --git a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/FieldCompletionTests.groovy b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/FieldCompletionTests.groovy index 9c7d74b7ed..a152658d87 100644 --- a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/FieldCompletionTests.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/FieldCompletionTests.groovy @@ -19,7 +19,6 @@ import groovy.transform.NotYetImplemented import org.eclipse.jdt.internal.codeassist.impl.AssistOptions import org.eclipse.jdt.ui.PreferenceConstants -import org.eclipse.jface.text.Document import org.eclipse.jface.text.contentassist.ICompletionProposal import org.junit.Test @@ -404,7 +403,7 @@ final class FieldCompletionTests extends CompletionTestSuite { meth(B) '''.stripIndent() ICompletionProposal proposal = checkUniqueProposal(contents, 'B', 'BLACK') - applyProposalAndCheck(new Document(contents), proposal, '''\ + applyProposalAndCheck(proposal, '''\ |import static tree.node.Color.BLACK | |def meth(tree.node.Color c) { } @@ -426,7 +425,7 @@ final class FieldCompletionTests extends CompletionTestSuite { ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'BL')) proposalExists(proposals, 'BLACK', 2) - applyProposalAndCheck(new Document(contents), orderByRelevance(proposals)[0], '''\ + applyProposalAndCheck(orderByRelevance(proposals)[0], '''\ |import static a.b.c.D.BLACK | |import tree.node.Color @@ -496,7 +495,7 @@ final class FieldCompletionTests extends CompletionTestSuite { '''.stripIndent() ICompletionProposal proposal = checkUniqueProposal(contents, 'DOT', 'DOTALL') - applyProposalAndCheck(new Document(contents), proposal, '''\ + applyProposalAndCheck(proposal, '''\ |import static java.util.regex.Pattern.DOTALL | |DOTALL @@ -512,7 +511,7 @@ final class FieldCompletionTests extends CompletionTestSuite { '''.stripIndent() ICompletionProposal proposal = checkUniqueProposal(contents, 'DOT', 'DOTALL') - applyProposalAndCheck(new Document(contents), proposal, '''\ + applyProposalAndCheck(proposal, '''\ |import static java.util.regex.Pattern.DOTALL | |DOTALL @@ -529,7 +528,7 @@ final class FieldCompletionTests extends CompletionTestSuite { '''.stripIndent() ICompletionProposal proposal = checkUniqueProposal(contents, 'DOT', 'DOTALL') - applyProposalAndCheck(new Document(contents), proposal, '''\ + applyProposalAndCheck(proposal, '''\ |import java.util.regex.Pattern | |Pattern.DOTALL @@ -549,7 +548,7 @@ final class FieldCompletionTests extends CompletionTestSuite { '''.stripIndent() ICompletionProposal proposal = checkUniqueProposal(contents, 'DOT', 'DOTALL') - applyProposalAndCheck(new Document(contents), proposal, '''\ + applyProposalAndCheck(proposal, '''\ java.util.regex.Pattern.DOTALL '''.stripIndent()) } diff --git a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/GroovyLikeCompletionTests.groovy b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/GroovyLikeCompletionTests.groovy index 8babb44707..34afcde18a 100644 --- a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/GroovyLikeCompletionTests.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/GroovyLikeCompletionTests.groovy @@ -86,7 +86,6 @@ final class GroovyLikeCompletionTests extends CompletionTestSuite { groovyPrefs.setValue(GroovyContentAssist.CLOSURE_BRACKETS, true) groovyPrefs.setValue(GroovyContentAssist.CLOSURE_NOPARENS, true) groovyPrefs.setValue(GroovyContentAssist.NAMED_ARGUMENTS, false) - groovyPrefs.setValue(GroovyContentAssist.PARAMETER_GUESSING, false) setJavaPreference(PreferenceConstants.TYPEFILTER_ENABLED, 'com.sun.*;sun.*') } diff --git a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/GuessingCompletionTests.groovy b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/GuessingCompletionTests.groovy index d4c4c6c834..9cb05be6b4 100644 --- a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/GuessingCompletionTests.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/GuessingCompletionTests.groovy @@ -16,8 +16,7 @@ package org.codehaus.groovy.eclipse.codeassist.tests import org.codehaus.groovy.eclipse.codeassist.GroovyContentAssist -import org.eclipse.jface.text.Document -import org.eclipse.jface.text.IDocument +import org.eclipse.jdt.ui.PreferenceConstants import org.eclipse.jface.text.contentassist.ICompletionProposal import org.junit.Assert import org.junit.Before @@ -27,9 +26,10 @@ final class GuessingCompletionTests extends CompletionTestSuite { @Before void setUp() { + setJavaPreference(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES, 'true') + setJavaPreference(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, 'true') GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_BRACKETS, true) GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, true) - GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.PARAMETER_GUESSING, true) } @Test @@ -83,22 +83,14 @@ final class GuessingCompletionTests extends CompletionTestSuite { // GroovyExtendedCompletionContext.computeVisibleElements(String) String contents = '''\ Closure yyy - def zzz = { } + def zzz = { -> } def xxx(Closure c) { } xxx '''.stripIndent() String[][] expectedChoices = [ ['zzz', 'yyy', '{ }'] as String[] ] - try { - checkProposalChoices(contents, 'xxx', 'xxx {', expectedChoices) - } catch (AssertionError e) { - try { - checkProposalChoices(contents, 'xxx', 'xxx yyy', expectedChoices) - } catch (AssertionError e2) { - checkProposalChoices(contents, 'xxx', 'xxx zzz', expectedChoices) - } - } + checkProposalChoices(contents, 'xxx', 'xxx { }', expectedChoices) } @Test @@ -116,11 +108,10 @@ final class GuessingCompletionTests extends CompletionTestSuite { | |pack.Util.ut |'''.stripMargin() - IDocument document = new Document(contents) ICompletionProposal proposal = checkUniqueProposal(contents, 'ut', 'util', 'util(MILLIS)') // apply initial proposal to generate parameter proposals - applyProposalAndCheck(document, proposal, '''\ + applyProposalAndCheck(proposal, '''\ |import static java.util.concurrent.TimeUnit.MILLISECONDS as MILLIS | |pack.Util.util(MILLIS) @@ -132,7 +123,7 @@ final class GuessingCompletionTests extends CompletionTestSuite { choices*.displayString.join('\n')) // TODO: Something below is not matching the real editor's application of the parameter proposal - /*applyProposalAndCheck(document, choices[1], '''\ + /*applyProposalAndCheck(choices[1], '''\ |import static java.util.concurrent.TimeUnit.DAYS |import static java.util.concurrent.TimeUnit.MILLISECONDS as MILLIS | diff --git a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/MethodCompletionTests.groovy b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/MethodCompletionTests.groovy index 3a046dd5c2..c44af09eb5 100644 --- a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/MethodCompletionTests.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/MethodCompletionTests.groovy @@ -22,24 +22,16 @@ import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.stmt.ExpressionStatement import org.codehaus.groovy.ast.stmt.ReturnStatement import org.codehaus.groovy.ast.stmt.Statement -import org.codehaus.groovy.eclipse.codeassist.GroovyContentAssist import org.codehaus.groovy.eclipse.codeassist.proposals.GroovyMethodProposal import org.codehaus.jdt.groovy.model.GroovyCompilationUnit import org.eclipse.jdt.core.compiler.CharOperation import org.eclipse.jdt.internal.codeassist.impl.AssistOptions import org.eclipse.jdt.ui.PreferenceConstants -import org.eclipse.jface.text.Document import org.eclipse.jface.text.contentassist.ICompletionProposal -import org.junit.Before import org.junit.Test final class MethodCompletionTests extends CompletionTestSuite { - @Before - void setUp() { - GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.PARAMETER_GUESSING, false) - } - private List delegateTestParameterNames(GroovyCompilationUnit unit) { waitForIndex() List methods = extract(unit).getMethods('m') @@ -404,7 +396,7 @@ final class MethodCompletionTests extends CompletionTestSuite { ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'comp')) proposalExists(proposals, 'compile', 2) - applyProposalAndCheck(new Document(contents), findFirstProposal(proposals, 'compile(String regex)'), '''\ + applyProposalAndCheck(findFirstProposal(proposals, 'compile(String regex)'), '''\ |import static java.util.regex.Pattern.compile | |compile(regex) @@ -433,7 +425,7 @@ final class MethodCompletionTests extends CompletionTestSuite { ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'comp')) proposalExists(proposals, 'compile', 2) - applyProposalAndCheck(new Document(contents), findFirstProposal(proposals, 'compile(String regex)'), '''\ + applyProposalAndCheck(findFirstProposal(proposals, 'compile(String regex)'), '''\ |import static java.util.regex.Pattern.compile | |compile(regex) @@ -451,7 +443,7 @@ final class MethodCompletionTests extends CompletionTestSuite { ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'comp')) proposalExists(proposals, 'compile', 2) - applyProposalAndCheck(new Document(contents), findFirstProposal(proposals, 'compile(String regex)'), '''\ + applyProposalAndCheck(findFirstProposal(proposals, 'compile(String regex)'), '''\ |import java.util.regex.Pattern | |Pattern.compile(regex) @@ -472,7 +464,7 @@ final class MethodCompletionTests extends CompletionTestSuite { ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'comp')) proposalExists(proposals, 'compile', 2) - applyProposalAndCheck(new Document(contents), findFirstProposal(proposals, 'compile(String regex)'), '''\ + applyProposalAndCheck(findFirstProposal(proposals, 'compile(String regex)'), '''\ java.util.regex.Pattern.compile(regex) '''.stripIndent()) } @@ -483,27 +475,16 @@ final class MethodCompletionTests extends CompletionTestSuite { ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'isE')) proposalExists(proposals, 'isEmpty', 1) - applyProposalAndCheck(new Document(contents), findFirstProposal(proposals, 'isEmpty'), 'String.&isEmpty') + applyProposalAndCheck(findFirstProposal(proposals, 'isEmpty'), 'String.&isEmpty') } @Test void testMethodPointer2() { - GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.PARAMETER_GUESSING, true) - - String contents = 'String.&isE' - ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'isE')) - proposalExists(proposals, 'isEmpty', 1) - - applyProposalAndCheck(new Document(contents), findFirstProposal(proposals, 'isEmpty'), 'String.&isEmpty') - } - - @Test - void testMethodPointer3() { String contents = 'String.& isE' ICompletionProposal[] proposals = createProposalsAtOffset(contents, getLastIndexOf(contents, 'isE')) proposalExists(proposals, 'isEmpty', 1) - applyProposalAndCheck(new Document(contents), findFirstProposal(proposals, 'isEmpty'), 'String.& isEmpty') + applyProposalAndCheck(findFirstProposal(proposals, 'isEmpty'), 'String.& isEmpty') } @Test diff --git a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/OtherCompletionTests.groovy b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/OtherCompletionTests.groovy index 2a170fbc1d..de8e3d6e5f 100644 --- a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/OtherCompletionTests.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/OtherCompletionTests.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,6 @@ final class OtherCompletionTests extends CompletionTestSuite { @Before void setUp() { GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, false) - GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.PARAMETER_GUESSING, false) } @Test // GRECLIPSE-414 diff --git a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/TriggerCharacterCompletionTests.groovy b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/TriggerCharacterCompletionTests.groovy index 925cac47dd..5befe9ac0e 100644 --- a/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/TriggerCharacterCompletionTests.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.codeassist.completion.test/src/org/codehaus/groovy/eclipse/codeassist/tests/TriggerCharacterCompletionTests.groovy @@ -43,7 +43,6 @@ final class TriggerCharacterCompletionTests extends CompletionTestSuite { GroovyContentAssist.NAMED_ARGUMENTS, GroovyContentAssist.CLOSURE_BRACKETS, GroovyContentAssist.CLOSURE_NOPARENS, - //GroovyContentAssist.PARAMETER_GUESSING ] // produce every combination of all prefs, each paired with a boolean @@ -88,15 +87,23 @@ final class TriggerCharacterCompletionTests extends CompletionTestSuite { StringBuilder closure = new StringBuilder() closure << '{' - if (true || isEnabled(FORMATTER_INSERT_SPACE_AFTER_OPENING_BRACE_IN_ARRAY_INITIALIZER)) { + if (isEnabled(FORMATTER_INSERT_SPACE_AFTER_OPENING_BRACE_IN_ARRAY_INITIALIZER)) { closure << ' ' } if (proposal.toString().contains('Closure')) { closure << 'it' } else { - closure << 'o1, o2 ->' + closure << 'o1' + if (isEnabled(FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_ARRAY_INITIALIZER)) { + closure << ' ' + } + closure << ',' + if (isEnabled(FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_ARRAY_INITIALIZER)) { + closure << ' ' + } + closure << 'o2 ->' } - if (true || isEnabled(FORMATTER_INSERT_SPACE_BEFORE_CLOSING_BRACE_IN_ARRAY_INITIALIZER)) { + if (isEnabled(FORMATTER_INSERT_SPACE_BEFORE_CLOSING_BRACE_IN_ARRAY_INITIALIZER)) { closure << ' ' } closure << '}' @@ -119,7 +126,14 @@ final class TriggerCharacterCompletionTests extends CompletionTestSuite { expected << ' ' } if (isEnabled(GroovyContentAssist.NAMED_ARGUMENTS)) { - expected << (proposal.toString() =~ /(\w+)\)/)[0][1] << ': ' + expected << (proposal.toString() =~ /(\w+)\)/)[0][1] + if (isEnabled(FORMATTER_INSERT_SPACE_BEFORE_COLON_IN_LABELED_STATEMENT)) { + expected << ' ' + } + expected << ':' + if (isEnabled(FORMATTER_INSERT_SPACE_AFTER_COLON_IN_LABELED_STATEMENT)) { + expected << ' ' + } } expected << closure @@ -138,7 +152,14 @@ final class TriggerCharacterCompletionTests extends CompletionTestSuite { expected << ' ' } if (isEnabled(GroovyContentAssist.NAMED_ARGUMENTS)) { - expected << 'mutate: ' + expected << 'mutate' + if (isEnabled(FORMATTER_INSERT_SPACE_BEFORE_COLON_IN_LABELED_STATEMENT)) { + expected << ' ' + } + expected << ':' + if (isEnabled(FORMATTER_INSERT_SPACE_AFTER_COLON_IN_LABELED_STATEMENT)) { + expected << ' ' + } } expected << 'mutate' @@ -163,7 +184,14 @@ final class TriggerCharacterCompletionTests extends CompletionTestSuite { expected << ' ' } if (isEnabled(GroovyContentAssist.NAMED_ARGUMENTS)) { - expected << (proposal.toString() =~ /(\w+)\)/)[0][1] << ': ' + expected << (proposal.toString() =~ /(\w+)\)/)[0][1] + if (isEnabled(FORMATTER_INSERT_SPACE_BEFORE_COLON_IN_LABELED_STATEMENT)) { + expected << ' ' + } + expected << ':' + if (isEnabled(FORMATTER_INSERT_SPACE_AFTER_COLON_IN_LABELED_STATEMENT)) { + expected << ' ' + } } } else { if (isEnabled(FORMATTER_INSERT_SPACE_BEFORE_CLOSING_PAREN_IN_METHOD_INVOCATION)) { diff --git a/ide-test/org.codehaus.groovy.eclipse.dsl.tests/src/org/codehaus/groovy/eclipse/dsl/tests/DSLContentAssistTests.groovy b/ide-test/org.codehaus.groovy.eclipse.dsl.tests/src/org/codehaus/groovy/eclipse/dsl/tests/DSLContentAssistTests.groovy index b71a5889fd..df9a42d107 100644 --- a/ide-test/org.codehaus.groovy.eclipse.dsl.tests/src/org/codehaus/groovy/eclipse/dsl/tests/DSLContentAssistTests.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.dsl.tests/src/org/codehaus/groovy/eclipse/dsl/tests/DSLContentAssistTests.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,13 +17,12 @@ package org.codehaus.groovy.eclipse.dsl.tests 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 import org.eclipse.core.resources.IFile import org.eclipse.core.resources.IProject -import org.eclipse.jface.text.Document +import org.eclipse.jdt.ui.PreferenceConstants import org.eclipse.jface.text.contentassist.ICompletionProposal import org.junit.Before import org.junit.BeforeClass @@ -59,7 +58,6 @@ final class DSLContentAssistTests extends CompletionTestSuite { @BeforeClass static void setUpTests() { - GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.PARAMETER_GUESSING, true) GroovyDSLCoreActivator.default.preferenceStore.setValue(DSLPreferencesInitializer.AUTO_ADD_DSL_SUPPORT, false) } @@ -71,6 +69,9 @@ final class DSLContentAssistTests extends CompletionTestSuite { GroovyDSLCoreActivator.default.contextStoreManager.initialize(project, true) //GroovyDSLCoreActivator.default.contextStoreManager.ignoreProject(project) } + + setJavaPreference(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES, 'true') + setJavaPreference(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, 'true') } private String[] createDsls(String... dsls) { @@ -129,10 +130,8 @@ final class DSLContentAssistTests extends CompletionTestSuite { def val = new Inner() val.fla '''.stripIndent() - ICompletionProposal[] proposals = createProposalsAtOffset(contents, getIndexOf(contents, '.fla')) - proposalExists(proposals, 'flart', 1) - ICompletionProposal proposal = findFirstProposal(proposals, 'flart', false) - applyProposalAndCheck(new Document(contents), proposal, contents.replace('val.fla', 'val.flart')) + ICompletionProposal proposal = checkUniqueProposal(contents, '.fla', 'flart', 'flart()') + applyProposalAndCheck(proposal, contents.replace('val.fla', 'val.flart()')) } @Test @@ -143,10 +142,8 @@ final class DSLContentAssistTests extends CompletionTestSuite { def val = new Inner() val.flart foo fl '''.stripIndent() - ICompletionProposal[] proposals = createProposalsAtOffset(contents, getIndexOf(contents, ' fl')) - proposalExists(proposals, 'flart', 1) - ICompletionProposal proposal = findFirstProposal(proposals, 'flart', false) - applyProposalAndCheck(new Document(contents), proposal, contents.replace(' fl', ' flart')) + ICompletionProposal proposal = checkUniqueProposal(contents, ' fl', 'flart', 'flart()') + applyProposalAndCheck(proposal, contents.replace(' fl', ' flart()')) } @Test @@ -157,10 +154,8 @@ final class DSLContentAssistTests extends CompletionTestSuite { def val = new Inner() val.flart foo, baz fl '''.stripIndent() - ICompletionProposal[] proposals = createProposalsAtOffset(contents, getIndexOf(contents, ' fl')) - proposalExists(proposals, 'flart', 1) - ICompletionProposal proposal = findFirstProposal(proposals, 'flart', false) - applyProposalAndCheck(new Document(contents), proposal, contents.replace(' fl', ' flart')) + ICompletionProposal proposal = checkUniqueProposal(contents, ' fl', 'flart', 'flart()') + applyProposalAndCheck(proposal, contents.replace(' fl', ' flart()')) } @Test @@ -171,10 +166,9 @@ final class DSLContentAssistTests extends CompletionTestSuite { def val = new Inner() val.flart foo, baz fl '''.stripIndent() - ICompletionProposal[] proposals = createProposalsAtOffset(contents, getIndexOf(contents, ' fl')) - proposalExists(proposals, 'flart', 1) - ICompletionProposal proposal = findFirstProposal(proposals, 'flart', false) - applyProposalAndCheck(new Document(contents), proposal, contents.replace(' fl', ' flart 0 ')) + + ICompletionProposal proposal = checkUniqueProposal(contents, ' fl', 'flart', 'flart 0') + applyProposalAndCheck(proposal, contents.replace(' fl', ' flart 0')) } @Test @@ -185,10 +179,8 @@ final class DSLContentAssistTests extends CompletionTestSuite { def val = new Inner() val.flart foo, baz fl '''.stripIndent() - ICompletionProposal[] proposals = createProposalsAtOffset(contents, getIndexOf(contents, ' fl')) - proposalExists(proposals, 'flart', 1) - ICompletionProposal proposal = findFirstProposal(proposals, 'flart', false) - applyProposalAndCheck(new Document(contents), proposal, contents.replace(' fl', ' flart 0, "" ')) + ICompletionProposal proposal = checkUniqueProposal(contents, ' fl', 'flart', 'flart 0, ""') + applyProposalAndCheck(proposal, contents.replace(' fl', ' flart 0, ""')) } @Test @@ -203,9 +195,8 @@ final class DSLContentAssistTests extends CompletionTestSuite { def val = new Inner() val.bl '''.stripIndent() - ICompletionProposal[] proposals = createProposalsAtOffset(contents, getIndexOf(contents, 'val.bl')) - ICompletionProposal proposal = findFirstProposal(proposals, 'blart', false) - applyProposalAndCheck(new Document(contents), proposal, contents.replace('val.bl', 'val.blart val, val, val ')) + ICompletionProposal proposal = checkUniqueProposal(contents, 'val.bl', 'blart', 'blart val, val, val') + applyProposalAndCheck(proposal, contents.replace('val.bl', 'val.blart val, val, val')) } @Test @@ -220,9 +211,8 @@ final class DSLContentAssistTests extends CompletionTestSuite { def val = new Inner() val.fl '''.stripIndent() - ICompletionProposal[] proposals = createProposalsAtOffset(contents, getIndexOf(contents, 'val.fl')) - ICompletionProposal proposal = findFirstProposal(proposals, 'flart', false) - applyProposalAndCheck(new Document(contents), proposal, contents.replace('val.fl', 'val.flart val ')) + ICompletionProposal proposal = checkUniqueProposal(contents, 'val.fl', 'flart', 'flart val') + applyProposalAndCheck(proposal, contents.replace('val.fl', 'val.flart val')) } @Test diff --git a/ide-test/org.codehaus.groovy.eclipse.dsl.tests/src/org/codehaus/groovy/eclipse/dsl/tests/DSLNamedArgContentAssistTests.groovy b/ide-test/org.codehaus.groovy.eclipse.dsl.tests/src/org/codehaus/groovy/eclipse/dsl/tests/DSLNamedArgContentAssistTests.groovy index dd1ccc7a1e..0dcd673f12 100644 --- a/ide-test/org.codehaus.groovy.eclipse.dsl.tests/src/org/codehaus/groovy/eclipse/dsl/tests/DSLNamedArgContentAssistTests.groovy +++ b/ide-test/org.codehaus.groovy.eclipse.dsl.tests/src/org/codehaus/groovy/eclipse/dsl/tests/DSLNamedArgContentAssistTests.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import org.codehaus.groovy.eclipse.codeassist.tests.CompletionTestSuite import org.codehaus.groovy.eclipse.dsl.DSLPreferencesInitializer import org.codehaus.groovy.eclipse.dsl.GroovyDSLCoreActivator import org.eclipse.core.resources.IProject +import org.eclipse.jdt.ui.PreferenceConstants import org.eclipse.jface.text.contentassist.ICompletionProposal import org.junit.Before import org.junit.BeforeClass @@ -31,11 +32,6 @@ final class DSLNamedArgContentAssistTests extends CompletionTestSuite { @BeforeClass static void setUpTests() { - GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_BRACKETS, true) - GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, true) - GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.NAMED_ARGUMENTS, true) - GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.PARAMETER_GUESSING, true) - GroovyDSLCoreActivator.default.preferenceStore.setValue(DSLPreferencesInitializer.AUTO_ADD_DSL_SUPPORT, false) } @@ -47,6 +43,12 @@ final class DSLNamedArgContentAssistTests extends CompletionTestSuite { GroovyDSLCoreActivator.default.contextStoreManager.initialize(project, true) //GroovyDSLCoreActivator.default.contextStoreManager.ignoreProject(project) } + + setJavaPreference(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES, 'true') + setJavaPreference(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, 'true') + GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.NAMED_ARGUMENTS, true) + GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_BRACKETS, true) + GroovyContentAssist.default.preferenceStore.setValue(GroovyContentAssist.CLOSURE_NOPARENS, true) } private void createDSL(String contents) { @@ -297,60 +299,60 @@ final class DSLNamedArgContentAssistTests extends CompletionTestSuite { @Test void testClostureOp1() { createDSL(CLOSURE_DSLD) - checkProposalApplicationNonType(closureContents, closureContents + '1 {', closureContents.length(), 'test1') + checkProposalApplicationNonType(closureContents, closureContents + '1 { }', closureContents.length(), 'test1') } @Test void testClostureOp2() { createDSL(CLOSURE_DSLD) - checkProposalApplicationNonType(closureContents, closureContents + '2("") {', closureContents.length(), 'test2') + checkProposalApplicationNonType(closureContents, closureContents + '2("") { }', closureContents.length(), 'test2') } @Test void testClostureOp3() { createDSL(CLOSURE_DSLD) - checkProposalApplicationNonType(closureContents, closureContents + '3(op:{ })', closureContents.length(), 'test3') + checkProposalApplicationNonType(closureContents, closureContents + '3(op: { })', closureContents.length(), 'test3') } @Test void testClostureOp4() { createDSL(CLOSURE_DSLD) - checkProposalApplicationNonType(closureContents, closureContents + '4(first:"", op:{ })', closureContents.length(), 'test4') + checkProposalApplicationNonType(closureContents, closureContents + '4(first: "", op: { })', closureContents.length(), 'test4') } @Test void testClostureOp5() { createDSL(CLOSURE_DSLD) - checkProposalApplicationNonType(closureContents, closureContents + '5("", op:{ })', closureContents.length(), 'test5') + checkProposalApplicationNonType(closureContents, closureContents + '5(op: { }, "")', closureContents.length(), 'test5') } @Test void testClostureOp6() { createDSL(CLOSURE_DSLD) - checkProposalApplicationNonType(closureContents, closureContents + '6(first:"") {', closureContents.length(), 'test6') + checkProposalApplicationNonType(closureContents, closureContents + '6(first: "") { }', closureContents.length(), 'test6') } @Test void testClostureOp7() { createDSL(CLOSURE_DSLD) - checkProposalApplicationNonType(closureContents, closureContents + '7(first:"", other:"") {', closureContents.length(), 'test7') + checkProposalApplicationNonType(closureContents, closureContents + '7(first: "", other: "") { }', closureContents.length(), 'test7') } @Test void testClostureOp8() { createDSL(CLOSURE_DSLD) - checkProposalApplicationNonType(closureContents, closureContents + '8("", first:"") {', closureContents.length(), 'test8') + checkProposalApplicationNonType(closureContents, closureContents + '8(first: "", "") { }', closureContents.length(), 'test8') } @Test void testClostureOp9() { createDSL(CLOSURE_DSLD) - checkProposalApplicationNonType(closureContents, closureContents + '9("", { }, "", first:"")', closureContents.length(), 'test9') + checkProposalApplicationNonType(closureContents, closureContents + '9(first: "", "", { }, "")', closureContents.length(), 'test9') } @Test void testClostureOp0() { createDSL(CLOSURE_DSLD) - checkProposalApplicationNonType(closureContents, closureContents + '0("", { }, "", first:"") {', closureContents.length(), 'test0') + checkProposalApplicationNonType(closureContents, closureContents + '0(first: "", "", { }, "") { }', closureContents.length(), 'test0') } } diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/GroovyContentAssist.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/GroovyContentAssist.java index 50451eb5f7..33078ef88e 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/GroovyContentAssist.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/GroovyContentAssist.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,15 +27,13 @@ public class GroovyContentAssist extends AbstractUIPlugin { public static final String PLUGIN_ID = "org.codehaus.groovy.eclipse.codeassist.completion"; - // comma-separated list of method names to hide + /** Comma-separated list of method names to hide. */ public static final String FILTERED_DGMS = PLUGIN_ID + ".filtereddgms"; - // if true use named arguments for method calls + /** If {@code true}, named arguments are used in method calls. */ public static final String NAMED_ARGUMENTS = PLUGIN_ID + ".arguments.named"; - // if true use parameter guessing proposals - public static final String PARAMETER_GUESSING = PLUGIN_ID + ".arguments.guess"; - // if true use brackets for closure args + /** If {@code true}, literals are used for closure args in method calls. */ public static final String CLOSURE_BRACKETS = PLUGIN_ID + ".closures.literals"; - // if true do not use parens around methods + /** If {@code true}, trailing closures are placed after method call parens. */ public static final String CLOSURE_NOPARENS = PLUGIN_ID + ".closures.noparens"; private static GroovyContentAssist plugin; diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/GroovyJavaFieldCompletionProposal.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/GroovyJavaFieldCompletionProposal.java index d4bb76a9e2..3cd9c0ae8a 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/GroovyJavaFieldCompletionProposal.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/GroovyJavaFieldCompletionProposal.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,16 +24,17 @@ public class GroovyJavaFieldCompletionProposal extends JavaCompletionProposal { - private final CompletionProposal proposal; - public GroovyJavaFieldCompletionProposal(CompletionProposal proposal, StyledString displayString, JavaContentAssistInvocationContext javaContext) { - super(String.valueOf(proposal.getName()), proposal.getReplaceStart(), proposal.getReplaceEnd() - proposal.getReplaceStart(), - ProposalUtils.getImage(proposal), displayString, proposal.getRelevance(), false, javaContext); - this.setProposalInfo(new FieldProposalInfo(javaContext.getProject(), (this.proposal = proposal))); - this.setTriggerCharacters(ProposalUtils.VAR_TRIGGER); - } + super(String.valueOf(proposal.getName()), // replacementString + proposal.getReplaceStart(), // replacementOffset + proposal.getReplaceEnd() - proposal.getReplaceStart(), // replacementLength + ProposalUtils.getImage(proposal), + displayString, + proposal.getRelevance(), + false, // inJavadoc + javaContext); - public CompletionProposal getProposal() { - return proposal; + setProposalInfo(new FieldProposalInfo(javaContext.getProject(), proposal)); + setTriggerCharacters(ProposalUtils.VAR_TRIGGER); } } diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/GroovyJavaGuessingCompletionProposal.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/GroovyJavaGuessingCompletionProposal.java deleted file mode 100644 index 95d3afc820..0000000000 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/GroovyJavaGuessingCompletionProposal.java +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Copyright 2009-2018 the original author or authors. - * - * 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.codehaus.groovy.eclipse.codeassist.completions; - -import java.util.Arrays; - -import org.codehaus.groovy.eclipse.codeassist.GroovyContentAssist; -import org.codehaus.groovy.eclipse.codeassist.ProposalUtils; -import org.codehaus.groovy.eclipse.codeassist.processors.GroovyCompletionProposal; -import org.codehaus.groovy.eclipse.codeassist.proposals.ProposalFormattingOptions; -import org.eclipse.jdt.core.CompletionContext; -import org.eclipse.jdt.core.CompletionProposal; -import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.Signature; -import org.eclipse.jdt.core.compiler.CharOperation; -import org.eclipse.jdt.internal.core.ImportContainer; -import org.eclipse.jdt.internal.corext.template.java.SignatureUtil; -import org.eclipse.jdt.internal.ui.JavaPlugin; -import org.eclipse.jdt.internal.ui.javaeditor.EditorHighlightingSynchronizer; -import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; -import org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal; -import org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposal; -import org.eclipse.jdt.internal.ui.text.java.JavaMethodCompletionProposal; -import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.jface.text.BadPositionCategoryException; -import org.eclipse.jface.text.IDocument; -import org.eclipse.jface.text.IPositionUpdater; -import org.eclipse.jface.text.IRegion; -import org.eclipse.jface.text.Position; -import org.eclipse.jface.text.Region; -import org.eclipse.jface.text.contentassist.ICompletionProposal; -import org.eclipse.jface.text.link.ILinkedModeListener; -import org.eclipse.jface.text.link.InclusivePositionUpdater; -import org.eclipse.jface.text.link.LinkedModeModel; -import org.eclipse.jface.text.link.LinkedModeUI; -import org.eclipse.jface.text.link.LinkedPosition; -import org.eclipse.jface.text.link.LinkedPositionGroup; -import org.eclipse.jface.text.link.ProposalPosition; -import org.eclipse.jface.viewers.StyledString; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.texteditor.link.EditorLinkedModeUI; - -/** - * An adaptation of {@link ParameterGuessingProposal} for Groovy code. - */ -public class GroovyJavaGuessingCompletionProposal extends JavaMethodCompletionProposal { - - /** - * Creates a {@link ParameterGuessingProposal} or null if the - * core context isn't available or extended. - * - * @param proposal the original completion proposal - * @param context the current context - * @param fillBestGuess if set, the best guess will be filled in - * - * @return a proposal or null - */ - public static GroovyJavaGuessingCompletionProposal createProposal(CompletionProposal proposal, - JavaContentAssistInvocationContext context, boolean fillBestGuess, String contributor, ProposalFormattingOptions options) { - CompletionContext coreContext = context.getCoreContext(); - if (coreContext != null && coreContext.isExtended()) { - return new GroovyJavaGuessingCompletionProposal(proposal, context, coreContext, fillBestGuess, contributor, options); - } - return null; - } - - private ICompletionProposal[][] fChoices; // initialized by guessParameters() - - private Position[] fPositions; // initialized by guessParameters() - - private IRegion fSelectedRegion; // initialized by apply() - - private IPositionUpdater fUpdater; - - private final boolean fFillBestGuess; - - private final CompletionContext fCoreContext; - - private final ProposalFormattingOptions options; - - private final String contributor; - - private boolean methodPointer; - - private GroovyJavaGuessingCompletionProposal(CompletionProposal proposal, JavaContentAssistInvocationContext context, - CompletionContext coreContext, boolean fillBestGuess, String contributor, ProposalFormattingOptions proposalOptions) { - super(proposal, context); - fCoreContext = coreContext; - fFillBestGuess = fillBestGuess; - this.contributor = contributor; - this.options = proposalOptions; - } - - @Override - protected int computeRelevance() { - // precomputed - return fProposal.getRelevance(); - } - - private IJavaElement getEnclosingElement() { - return fCoreContext.getEnclosingElement(); - } - - // includes the regular and the named parameters - private String[] cachedVisibleParameterTypes = null; - - private String[] getParameterTypes() { - if (cachedVisibleParameterTypes == null) { - char[] signature = SignatureUtil.fix83600(fProposal.getSignature()); - char[][] types = Signature.getParameterTypes(signature); - - String[] ret = new String[types.length]; - for (int i = 0; i < types.length; i++) { - ret[i] = new String(Signature.toCharArray(types[i])); - } - cachedVisibleParameterTypes = ret; - } - return cachedVisibleParameterTypes; - } - - private IJavaElement[][] getAssignableElements() { - return Arrays.stream(getParameterTypes()) - .map(t -> Signature.createTypeSignature(t, true)) - .map(fCoreContext::getVisibleElements) - .toArray(IJavaElement[][]::new); - } - - @Override - public void apply(IDocument document, char trigger, int offset) { - methodPointer = ProposalUtils.isMethodPointerCompletion(document, getReplacementOffset()); - try { - super.apply(document, trigger, offset); - - int baseOffset = getReplacementOffset(); - String replacement = getReplacementString(); - - if (fPositions != null && getTextViewer() != null) { - LinkedModeModel model = new LinkedModeModel(); - - for (int i = 0; i < fPositions.length; i += 1) { - LinkedPositionGroup group = new LinkedPositionGroup(); - int positionOffset = fPositions[i].getOffset(); - int positionLength = fPositions[i].getLength(); - - if (fChoices[i].length < 2) { - group.addPosition(new LinkedPosition(document, positionOffset, positionLength, LinkedPositionGroup.NO_STOP)); - } else { - ensurePositionCategoryInstalled(document, model); - document.addPosition(getCategory(), fPositions[i]); - group.addPosition(new ProposalPosition(document, positionOffset, positionLength, LinkedPositionGroup.NO_STOP, fChoices[i])); - } - model.addGroup(group); - } - - model.forceInstall(); - JavaEditor editor = getJavaEditor(); - if (editor != null) { - model.addLinkingListener(new EditorHighlightingSynchronizer(editor)); - } - - LinkedModeUI ui = new EditorLinkedModeUI(model, getTextViewer()); - ui.setExitPosition(getTextViewer(), baseOffset + replacement.length(), 0, Integer.MAX_VALUE); - ui.setExitPolicy(new ExitPolicy(')', document)); - ui.setCyclingMode(LinkedModeUI.CYCLE_WHEN_NO_PARENT); - ui.setDoContextInfo(true); - ui.enter(); - fSelectedRegion = ui.getSelectedRegion(); - } else { - fSelectedRegion = new Region(baseOffset + replacement.length(), 0); - } - - } catch (BadLocationException e) { - ensurePositionCategoryRemoved(document); - GroovyContentAssist.logError(e); - openErrorDialog(e); - } catch (BadPositionCategoryException e) { - ensurePositionCategoryRemoved(document); - GroovyContentAssist.logError(e); - openErrorDialog(e); - } - } - - @Override - protected boolean needsLinkedMode() { - return false; // we handle it ourselves - } - - @Override - protected StyledString computeDisplayString() { - StyledString displayString = super.computeDisplayString(); - if (contributor != null && contributor.trim().length() > 0) { - displayString.append(new StyledString(" (" + contributor.trim() + ")", StyledString.DECORATIONS_STYLER)); - } - return displayString; - } - - @Override - protected String computeReplacementString() { - char[] proposalName = fProposal.getName(); - boolean hasWhitespace = ProposalUtils.hasWhitespace(proposalName); - - if (methodPointer) { - // complete the name only for a method pointer expression - return String.valueOf(!hasWhitespace ? proposalName : CharOperation.concat('"', proposalName, '"')); - } - - if (!hasParameters() || !hasArgumentList()) { - if (options.noParens) { - return String.valueOf(!hasWhitespace ? proposalName : CharOperation.concat('"', proposalName, '"')); - } else { - String replacementString = super.computeReplacementString(); - if (replacementString.endsWith(");")) { - replacementString = replacementString.substring(0, replacementString.length() - 1); - } - return replacementString; - } - } - - try { - return computeGuessingCompletion(); - } catch (JavaModelException e) { - fPositions = null; - fChoices = null; - GroovyContentAssist.logError(e); - openErrorDialog(e); - } - - return super.computeReplacementString(); - } - - /** - * Creates the completion string. Offsets and Lengths are set to the offsets - * and lengths of the - * parameters. - * - * @return the completion string - * @throws JavaModelException if parameter guessing failed - */ - private String computeGuessingCompletion() throws JavaModelException { - StringBuffer buffer = new StringBuffer(); - char[] proposalName = fProposal.getName(); - boolean hasWhitespace = ProposalUtils.hasWhitespace(proposalName); - char[] newProposalName = !hasWhitespace ? proposalName : CharOperation.concat('"', proposalName, '"'); - - fProposal.setName(newProposalName); - appendMethodNameReplacement(buffer); - fProposal.setName(proposalName); - - FormatterPrefs prefs = getFormatterPrefs(); - if (options.noParens) { - // eat the opening paren replace with a space if there isn't one already - buffer.replace(buffer.length() - 1, buffer.length(), prefs.beforeOpeningParen ? "" : " "); - } - - setCursorPosition(buffer.length()); - - // groovy doesn't require parens around closures if it is the last argument - // If the option is set, then we follow that heuristic - char[][] regularParameterTypes = ((GroovyCompletionProposal) fProposal).getRegularParameterTypeNames(); - - // check if the last regular parameter is a closure. If so, it must be - // moved to the end - boolean lastArgIsClosure = lastArgIsClosure(regularParameterTypes); - int indexOfLastClosure = lastArgIsClosure ? regularParameterTypes.length - 1 : -1; - - char[][] namedParameterNames = ((GroovyCompletionProposal) fProposal).getNamedParameterNames(); - char[][] regularParameterNames = ((GroovyCompletionProposal) fProposal).getRegularParameterNames(); - int namedCount = namedParameterNames.length; - int argCount = regularParameterNames.length; - int allCount = argCount + namedCount; - - if (options.noParensAroundClosures) { - // remove the opening paren only if there is a single closure parameter - if (indexOfLastClosure == 0 && namedCount == 0) { - buffer.replace(buffer.length() - 1, buffer.length(), ""); - // add space if not already there - // would be added by call to appendMethodNameReplacement - if (!prefs.beforeOpeningParen) { - buffer.append(SPACE); - } - } else { - if (prefs.afterOpeningParen) - buffer.append(SPACE); - } - } else { - if (prefs.afterOpeningParen) - buffer.append(SPACE); - } - - // we don't want parameters for static import declarations - if (fCoreContext.getEnclosingElement() != null - && !(fCoreContext.getEnclosingElement().getParent() instanceof ImportContainer)) { - // now add the parameters - int replacementOffset = getReplacementOffset(); - fChoices = guessParameters(namedParameterNames, regularParameterNames); - - for (int i = 0; i < allCount; i++) { - if (i == indexOfLastClosure) { - // handle the last closure separately - continue; - } - - char[] nextName; - if (i < argCount) { - nextName = regularParameterNames[i]; - } else { - nextName = namedParameterNames[i - argCount]; - } - if (i >= argCount || options.useNamedArguments) { - buffer.append(nextName).append(":"); - } - - // handle the value - ICompletionProposal proposal = fChoices[i][0]; - String argument = proposal.getDisplayString(); - - Position position = fPositions[i]; - position.setOffset(replacementOffset + buffer.length()); - position.setLength(argument.length()); - - // handle the "unknown" case where we only insert a proposal - if (proposal instanceof AbstractJavaCompletionProposal) { - ((AbstractJavaCompletionProposal) proposal).setReplacementOffset(position.getOffset()); - ((AbstractJavaCompletionProposal) proposal).setReplacementLength(position.getLength()); - } - - buffer.append(argument); - - // check what to add after argument - if (i == allCount - 1 || (i == allCount - 2 && i == indexOfLastClosure - 1)) { - if (prefs.beforeClosingParen || options.noParens) { - buffer.append(SPACE); - } - if (!options.noParens) { - buffer.append(RPAREN); - } - } else if (i < allCount - 1) { - if (prefs.beforeComma) - buffer.append(SPACE); - buffer.append(COMMA); - if (prefs.afterComma) - buffer.append(SPACE); - } - } - - // closures at the end are added in an idiomatic groovy way - if (indexOfLastClosure >= 0) { - if (allCount > 1) { - if (!options.noParensAroundClosures) { - buffer.append(COMMA); - } - buffer.append(SPACE); - } - Position position = fPositions[indexOfLastClosure]; - position.setOffset(replacementOffset + buffer.length()); - position.setLength(1); - buffer.append("{"); - - if (!options.noParensAroundClosures) { - buffer.append(" }"); - buffer.append(RPAREN); - } - } - } - return buffer.toString(); - } - - private boolean lastArgIsClosure(char[][] regularParameterTypes) { - if (regularParameterTypes != null && regularParameterTypes.length > 0) { - char[] lastArgType = regularParameterTypes[regularParameterTypes.length - 1]; - // we should be comparing against a fully qualified type name, but - // it is not always available - // so a simple name is close enough - return CharOperation.equals("Closure".toCharArray(), lastArgType); - } - // no args - return false; - } - - /** - * Returns the currently active java editor, or null if it - * cannot be determined. - * - * @return the currently active java editor, or null - */ - private JavaEditor getJavaEditor() { - IEditorPart part = JavaPlugin.getActivePage().getActiveEditor(); - if (part instanceof JavaEditor) { - return (JavaEditor) part; - } - return null; - } - - private ICompletionProposal[][] guessParameters(char[][] firstParameterNames, char[][] secondParameterNames) throws JavaModelException { - int count = firstParameterNames.length + secondParameterNames.length; - - fPositions = new Position[count]; - fChoices = new ICompletionProposal[count][]; - String[] parameterTypes = getParameterTypes(); - IJavaElement[][] assignableElements = getAssignableElements(); - - char[][] parameterNames = new char[count][]; - System.arraycopy(firstParameterNames, 0, parameterNames, 0, firstParameterNames.length); - System.arraycopy(secondParameterNames, 0, parameterNames, firstParameterNames.length, secondParameterNames.length); - - // Find matches in reverse order. Do this because people tend to declare - // the variable meant for the last parameter last. That is, local variables - // for the last parameter in the method completion are more likely to be - // closer to the point of code completion. As an example consider: - // - // public void myMethod(int param1, int param2, int param3) { - // someOtherObject.yourMethod(param1, param2, param3); - // } - // - // The other consideration is giving preference to variables that have - // not previously been used in this code completion, which avoids - // "someOtherObject.yourMethod(param1, param1, param1)". - - for (int i = count - 1; i >= 0; i -= 1) { - Position position = new Position(0, 0); - String paramName = String.valueOf(parameterNames[i]); - ICompletionProposal[] argumentProposals = new ParameterGuesserDelegate(getEnclosingElement(), fInvocationContext). - parameterProposals(parameterTypes[i], paramName, position, assignableElements[i], fFillBestGuess); - - if (argumentProposals.length == 0) { - argumentProposals = new ICompletionProposal[] { - new JavaCompletionProposal(paramName, 0, paramName.length(), null, paramName, 0) - }; - } - - fPositions[i] = position; - fChoices[i] = argumentProposals; - } - - return fChoices; - } - - @Override - public Point getSelection(IDocument document) { - if (fSelectedRegion == null) - return new Point(getReplacementOffset(), 0); - - return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength()); - } - - private void openErrorDialog(Exception e) { - Shell shell = getTextViewer().getTextWidget().getShell(); - MessageDialog.openError(shell, "Error guessing parameters for content assist proposal", e.getMessage()); - } - - private void ensurePositionCategoryInstalled(final IDocument document, LinkedModeModel model) { - if (!document.containsPositionCategory(getCategory())) { - document.addPositionCategory(getCategory()); - fUpdater = new InclusivePositionUpdater(getCategory()); - document.addPositionUpdater(fUpdater); - - model.addLinkingListener(new ILinkedModeListener() { - @Override - public void left(LinkedModeModel environment, int flags) { - ensurePositionCategoryRemoved(document); - } - - @Override - public void suspend(LinkedModeModel environment) { - } - - @Override - public void resume(LinkedModeModel environment, int flags) { - } - }); - } - } - - private void ensurePositionCategoryRemoved(IDocument document) { - if (document.containsPositionCategory(getCategory())) { - try { - document.removePositionCategory(getCategory()); - } catch (BadPositionCategoryException e) { - // ignore - } - document.removePositionUpdater(fUpdater); - } - } - - private String getCategory() { - return "ParameterGuessingProposal_" + toString(); - } - - /** - * Not API, for testing only. - * - * @return the guessed parameter proposals - */ - public ICompletionProposal[][] getChoices() { - return fChoices; - } -} diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/GroovyJavaMethodCompletionProposal.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/GroovyJavaMethodCompletionProposal.java index 7fdd87c1aa..26c2a6dd8b 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/GroovyJavaMethodCompletionProposal.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/GroovyJavaMethodCompletionProposal.java @@ -16,18 +16,24 @@ package org.codehaus.groovy.eclipse.codeassist.completions; import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.eclipse.codeassist.GroovyContentAssist; import org.codehaus.groovy.eclipse.codeassist.ProposalUtils; import org.codehaus.groovy.eclipse.codeassist.processors.GroovyCompletionProposal; import org.codehaus.groovy.eclipse.codeassist.proposals.ProposalFormattingOptions; -import org.eclipse.core.runtime.Assert; import org.eclipse.jdt.core.CompletionProposal; import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CharOperation; @@ -35,26 +41,37 @@ import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.jdt.groovy.core.util.ReflectionUtils; import org.eclipse.jdt.internal.codeassist.InternalCompletionContext; +import org.eclipse.jdt.internal.corext.template.java.SignatureUtil; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.javaeditor.EditorHighlightingSynchronizer; import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; +import org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposal; import org.eclipse.jdt.internal.ui.text.java.JavaMethodCompletionProposal; import org.eclipse.jdt.internal.ui.text.java.LazyGenericTypeProposal; import org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal; import org.eclipse.jdt.internal.ui.text.java.LazyJavaTypeCompletionProposal; import org.eclipse.jdt.internal.ui.text.java.MethodProposalInfo; import org.eclipse.jdt.internal.ui.text.java.ProposalContextInformation; +import org.eclipse.jdt.internal.ui.text.java.ProposalInfo; +import org.eclipse.jdt.ui.PreferenceConstants; import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext; +import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IPositionUpdater; import org.eclipse.jface.text.IRegion; -import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.text.link.ILinkedModeListener; +import org.eclipse.jface.text.link.InclusivePositionUpdater; import org.eclipse.jface.text.link.LinkedModeModel; import org.eclipse.jface.text.link.LinkedModeUI; import org.eclipse.jface.text.link.LinkedPosition; import org.eclipse.jface.text.link.LinkedPositionGroup; +import org.eclipse.jface.text.link.ProposalPosition; import org.eclipse.jface.viewers.StyledString; import org.eclipse.swt.graphics.Point; import org.eclipse.ui.IEditorPart; @@ -62,92 +79,64 @@ public class GroovyJavaMethodCompletionProposal extends JavaMethodCompletionProposal { - protected final String contributor; - - protected final ReplacementPreferences preferences; - - public GroovyJavaMethodCompletionProposal(GroovyCompletionProposal proposal, JavaContentAssistInvocationContext context, ProposalFormattingOptions options) { - this(proposal, context, options, null); - } - - public GroovyJavaMethodCompletionProposal(GroovyCompletionProposal proposal, JavaContentAssistInvocationContext context, ProposalFormattingOptions options, String contributor) { + public GroovyJavaMethodCompletionProposal(CompletionProposal proposal, ProposalFormattingOptions options, JavaContentAssistInvocationContext context, String contributor) { super(proposal, context); - - this.contributor = contributor; - this.preferences = new ReplacementPreferences(context.getProject(), getFormatterPrefs(), options); - - this.setRelevance(proposal.getRelevance()); - this.setProposalInfo(new MethodProposalInfo(context.getProject(), proposal)); - this.setTriggerCharacters(!proposal.hasParameters() ? ProposalUtils.METHOD_TRIGGERS : ProposalUtils.METHOD_WITH_ARGUMENTS_TRIGGERS); + fContributor = (contributor == null ? "" : contributor.trim()); + fPreferences = new ReplacementPreferences(options, getFormatterPrefs(), context.getProject()); } - protected ImportRewrite fImportRewite; + protected final String fContributor; + protected final ReplacementPreferences fPreferences; - public void setImportRewite(ImportRewrite importRewite) { + public final void setImportRewite(ImportRewrite importRewite) { fImportRewite = importRewite; } + protected ImportRewrite fImportRewite; - /** If {@code true}, shows the context only and does not perform completion. */ - protected boolean fContextOnly; - - public void contextOnly() { - fContextOnly = true; - } - - // initialized during application - protected boolean fMethodPointer; - protected int[] fArgumentOffsets; - protected int[] fArgumentLengths; + // initialized during application: protected IRegion fSelectedRegion; + protected String fPositionCategory; + protected List fPositions; + private IPositionUpdater fUpdater; + protected List fProposals; //-------------------------------------------------------------------------- @Override public void apply(IDocument document, char trigger, int offset) { - fMethodPointer = ProposalUtils.isMethodPointerCompletion(document, getReplacementOffset()); - ReflectionUtils.setPrivateField(LazyJavaCompletionProposal.class, "fReplacementStringComputed", this, Boolean.FALSE); - - if (trigger == '{' && !fContextOnly) { - String replacement = getReplacementString(); - if (replacement.endsWith("}") || fMethodPointer) { - trigger = 0; // disable insertion of trailing '{' - } else if (fProposal.getKind() == CompletionProposal.CONSTRUCTOR_INVOCATION || - !lastParamAcceptsClosure(Signature.getParameterTypes(fProposal.getSignature()), fInvocationContext)) { - // prepare for insertion of new block after replacement - if (preferences.insertSpaceBeforeOpeningBraceInBlock) { - setReplacementString(replacement + SPACE); - } - } else { - trigger = 0; // disable insertion of trailing '{' - - if (!preferences.useClosureLiteral || !lastParamIsClosure(Signature.getParameterTypes(fProposal.getSignature()))) { + try { + if (trigger == '{' && (fProposal.getCompletion() != null && fProposal.getCompletion().length > 0)) { + String replacement = getReplacementString(); + if (replacement.endsWith("}") || fProposal.getKind() == CompletionProposal.METHOD_NAME_REFERENCE) { + trigger = 0; // disable insertion of trailing '{' + + } else if (fProposal.getKind() == CompletionProposal.CONSTRUCTOR_INVOCATION || + !fPreferences.isEnabled(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES) || + !lastParamAcceptsClosure(Signature.getParameterTypes(fProposal.getSignature()))) { + // prepare for insertion of new block after replacement + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_BRACE_IN_BLOCK)) { + setReplacementString(replacement + SPACE); + } + } else { // replace the last argument with a closure literal setReplacementString(recomputeReplacementString()); + trigger = 0; // disable insertion of trailing '{' } } - } - - super.apply(document, trigger, offset); - if (fArgumentOffsets != null && fArgumentOffsets.length > 0 && fArgumentLengths != null && fArgumentLengths.length > 0) { - fSelectedRegion = new Region(getReplacementOffset() + fArgumentOffsets[0], fArgumentLengths[0]); - } else { - fSelectedRegion = new Region(getReplacementOffset() + getReplacementString().length(), 0); - } - } + super.apply(document, trigger, offset); - @Override - public Point getSelection(IDocument document) { - return (fSelectedRegion == null ? null : new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength())); - } - - @Override - protected StyledString computeDisplayString() { - StyledString displayString = super.computeDisplayString(); - if (contributor != null && !contributor.trim().isEmpty()) { - displayString.append(new StyledString(" (" + contributor.trim() + ")", StyledString.DECORATIONS_STYLER)); + if (getContextInformationPosition() > 0) { + // change offset from relative to absolute + setContextInformationPosition(getContextInformationPosition() + getReplacementOffset()); + } + if (fPositions != null && !fPositions.isEmpty()) { + fSelectedRegion = new Region(fPositions.get(0).getOffset(), fPositions.get(0).getLength()); + } + } catch (Exception e) { + GroovyContentAssist.logError(e); + ensurePositionCategoryRemoved(document); } - return displayString; } @Override @@ -163,42 +152,40 @@ protected IContextInformation computeContextInformation() { } @Override - protected LazyJavaCompletionProposal createRequiredTypeCompletionProposal(CompletionProposal completionProposal, JavaContentAssistInvocationContext invocationContext) { - LazyJavaCompletionProposal requiredProposal = super.createRequiredTypeCompletionProposal(completionProposal, invocationContext); - if (fProposal.getKind() == CompletionProposal.CONSTRUCTOR_INVOCATION && requiredProposal instanceof LazyJavaTypeCompletionProposal) { - if (fImportRewite != null) { - ReflectionUtils.setPrivateField(LazyJavaTypeCompletionProposal.class, "fImportRewrite", requiredProposal, fImportRewite); - } - if (requiredProposal instanceof LazyGenericTypeProposal) { - // disable generics completion for constructors (i.e. complete expression as "new ArrayList()" instead of "new ArrayList()" - try { - //requiredProposal.fTypeArgumentProposals = new LazyGenericTypeProposal.TypeArgumentProposal[0]; - ReflectionUtils.setPrivateField(LazyGenericTypeProposal.class, "fTypeArgumentProposals", requiredProposal, - Array.newInstance(Class.forName("org.eclipse.jdt.internal.ui.text.java.LazyGenericTypeProposal$TypeArgumentProposal"), 0)); - } catch (Exception e) { - GroovyContentAssist.logError(e); - } - } + protected final StyledString computeDisplayString() { + StyledString displayString = super.computeDisplayString(); + if (!fContributor.isEmpty()) { + displayString.append(new StyledString(" (" + fContributor + ")", StyledString.DECORATIONS_STYLER)); } - return requiredProposal; + return displayString; + } + + @Override + protected ProposalInfo computeProposalInfo() { + return new MethodProposalInfo(fInvocationContext.getProject(), fProposal); + } + + @Override + protected int computeRelevance() { + return fProposal.getRelevance(); } @Override protected String computeReplacementString() { - if (fContextOnly) { + if (fProposal.getCompletion() == null || fProposal.getCompletion().length == 0) { return ""; } char[] proposalName = fProposal.getName(); boolean hasWhitespace = ProposalUtils.hasWhitespace(proposalName); - if (fMethodPointer) { - // complete the name only for a method pointer expression + if (fProposal.getKind() == CompletionProposal.METHOD_NAME_REFERENCE) { + // complete the name only for a method pointer or static import expression return String.valueOf(!hasWhitespace ? proposalName : CharOperation.concat('"', proposalName, '"')); } - // with no arguments there is nothing groovy to do - if ((!hasParameters() || !hasArgumentList()) && !hasWhitespace) { + // if no whitespace in the method name and no arguments, there is nothing groovy to do + if (!hasWhitespace && (!hasParameters() || !hasArgumentList())) { String replacementString = super.computeReplacementString(); if (replacementString.endsWith(");")) { replacementString = replacementString.substring(0, replacementString.length() - 1); @@ -206,24 +193,54 @@ protected String computeReplacementString() { return replacementString; } + // StringBuffer buffer = new StringBuffer(); + fProposal.setName(!hasWhitespace ? proposalName : CharOperation.concat('"', proposalName, '"')); appendMethodNameReplacement(buffer); fProposal.setName(proposalName); if (!hasParameters()) { - if (preferences.insertSpaceBetweenEmptyParensInMethodCall) { + while (Character.isWhitespace(buffer.charAt(buffer.length() - 1))) { + buffer.deleteCharAt(buffer.length() - 1); + } + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BETWEEN_EMPTY_PARENS_IN_METHOD_INVOCATION)) { buffer.append(SPACE); } buffer.append(RPAREN); + + } else if (!fPreferences.isEnabled(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES)) { + if (fPreferences.bCommandChaining) { + int i = buffer.lastIndexOf(LPAREN); + while (Character.isWhitespace(buffer.charAt(i - 1))) { + i -= 1; + } + buffer.replace(i, buffer.length(), SPACE); + } else if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_PAREN_IN_METHOD_INVOCATION)) { + buffer.append(SPACE); + } + + setCursorPosition(buffer.length()); // position cursor inside parentheses + setContextInformationPosition(getCursorPosition()); + + if (!fPreferences.bCommandChaining) + buffer.append(RPAREN); + } else { int indexOfLastClosure = -1; - char[][] regularParameterTypes = ((GroovyCompletionProposal) fProposal).getRegularParameterTypeNames(); char[][] namedParameterTypes = ((GroovyCompletionProposal) fProposal).getNamedParameterTypeNames(); - if (preferences.insertClosureAfterClosingParenInMethodCall) { + char[][] regularParameterTypes = ((GroovyCompletionProposal) fProposal).getRegularParameterTypeNames(); + + if (fPreferences.bCommandChaining) { + int i = buffer.lastIndexOf(LPAREN); + while (Character.isWhitespace(buffer.charAt(i - 1))) { + i -= 1; + } + buffer.replace(i, buffer.length(), SPACE); + } else if (fPreferences.isEnabled(GroovyContentAssist.CLOSURE_NOPARENS)) { // need to check both regular and named parameters for closure - if (lastParamIsClosure(regularParameterTypes, namedParameterTypes)) { - indexOfLastClosure = regularParameterTypes.length + namedParameterTypes.length - 1; + if (lastParamIsClosure(regularParameterTypes, CharOperation.NO_CHAR_CHAR)) { + indexOfLastClosure = namedParameterTypes.length + regularParameterTypes.length - 1; } // remove the opening paren only if there is a single closure parameter @@ -231,25 +248,27 @@ protected String computeReplacementString() { buffer.deleteCharAt(buffer.length() - 1); // add space if not already there would be added by call to appendMethodNameReplacement - if (!preferences.insertSpaceBeforeOpeningParenInMethodCall) { + if (!fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_PAREN_IN_METHOD_INVOCATION)) { buffer.append(SPACE); } } - } else if (preferences.insertSpaceAfterOpeningParenInMethodCall) { + } else if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_PAREN_IN_METHOD_INVOCATION)) { buffer.append(SPACE); } + setContextInformationPosition(buffer.length()); + // now add the parameters; named parameters go first char[][] namedParameterNames = ((GroovyCompletionProposal) fProposal).getNamedParameterNames(); char[][] regularParameterNames = ((GroovyCompletionProposal) fProposal).getRegularParameterNames(); int namedCount = namedParameterNames.length, totalCount = regularParameterNames.length + namedCount; - fArgumentOffsets = new int[totalCount]; - fArgumentLengths = new int[totalCount]; + // initialize fProposals and fPositions + computeReplacementProposals(namedParameterNames, regularParameterNames, indexOfLastClosure); for (int i = 0; i < totalCount; i += 1) { + @SuppressWarnings("unused") char[] nextName, nextType; - // check for named args (either all of them, or the explicitly named ones) if (i < namedCount) { nextName = namedParameterNames[i]; nextType = namedParameterTypes[i]; @@ -257,53 +276,38 @@ protected String computeReplacementString() { nextName = regularParameterNames[i - namedCount]; nextType = regularParameterTypes[i - namedCount]; } + String nextValue = fProposals.get(i)[0].getDisplayString(); - if ((preferences.useNamedArguments || i < namedCount) && i != indexOfLastClosure) { + if ((fPreferences.isEnabled(GroovyContentAssist.NAMED_ARGUMENTS) || i < namedCount) && i != indexOfLastClosure) { buffer.append(nextName); - if (preferences.insertSpaceBeforeColonInNamedArgument) { + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COLON_IN_LABELED_STATEMENT)) { buffer.append(SPACE); } buffer.append(":"); - if (preferences.insertSpaceAfterColonInNamedArgument) { + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COLON_IN_LABELED_STATEMENT)) { buffer.append(SPACE); } } - if (preferences.useClosureLiteral && CharOperation.equals(nextType, CLOSURE_TYPE_NAME)) { - buffer.append("{"); - if (preferences.insertSpaceAfterOpeningBraceInClosure) { - buffer.append(SPACE); - } - - fArgumentOffsets[i] = buffer.length(); - fArgumentLengths[i] = 2; // select "it" - buffer.append("it"); - - if (preferences.insertSpaceBeforeClosingBraceInClosure) { - buffer.append(SPACE); - } - buffer.append("}"); - } else { - fArgumentOffsets[i] = buffer.length(); - fArgumentLengths[i] = nextName.length; - buffer.append(nextName); - } + fPositions.get(i).setLength(nextValue.length()); + fPositions.get(i).setOffset(buffer.length()); + buffer.append(nextValue); - if (i == (indexOfLastClosure - 1) || (i != indexOfLastClosure && i == (totalCount - 1))) { - if (preferences.insertSpaceBeforeClosingParenInMethodCall) { + if (i == (indexOfLastClosure - 1) || (i != indexOfLastClosure && i == (totalCount - 1) && !fPreferences.bCommandChaining)) { + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_PAREN_IN_METHOD_INVOCATION)) { buffer.append(SPACE); } buffer.append(RPAREN); - if (i == (indexOfLastClosure - 1) && (!preferences.useClosureLiteral || - preferences.insertSpaceBeforeOpeningBraceInBlock)) { + if (i == (indexOfLastClosure - 1) && (!fPreferences.isEnabled(GroovyContentAssist.CLOSURE_BRACKETS) || + fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_BRACE_IN_BLOCK))) { buffer.append(SPACE); } } else if (i < (totalCount - 1)) { - if (preferences.insertSpaceBeforeCommaInMethodCallArgs) { + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_METHOD_INVOCATION_ARGUMENTS)) { buffer.append(SPACE); } buffer.append(COMMA); - if (preferences.insertSpaceAfterCommaInMethodCallArgs) { + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_METHOD_INVOCATION_ARGUMENTS)) { buffer.append(SPACE); } } @@ -313,12 +317,15 @@ protected String computeReplacementString() { return buffer.toString(); } - protected String recomputeReplacementString() { - int last = (fArgumentOffsets.length - 1); - String head = getReplacementString().substring(0, fArgumentOffsets[last]); - String tail = getReplacementString().substring(fArgumentOffsets[last] + fArgumentLengths[last]); + protected String recomputeReplacementString() throws JavaModelException { + int last = (fPositions.size() - 1); + // disable guessing for literal + fProposals.remove(last); + + String head = getReplacementString().substring(0, fPositions.get(last).getOffset()); + String tail = getReplacementString().substring((fPositions.get(last).getOffset()) + fPositions.get(last).getLength()); - if (preferences.insertClosureAfterClosingParenInMethodCall) { + if (fPreferences.isEnabled(GroovyContentAssist.CLOSURE_NOPARENS)) { if (!tail.isEmpty()) { // remove opening paren if there is one param if (last == 0) { @@ -326,62 +333,57 @@ protected String recomputeReplacementString() { } else { head = head.substring(0, head.lastIndexOf(',')) + tail; } - if (!head.endsWith(SPACE) && preferences.insertSpaceBeforeOpeningBraceInBlock) { + if (!head.endsWith(SPACE) && fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_BRACE_IN_BLOCK)) { head += SPACE; } tail = ""; - } else if (head.endsWith(SPACE) && !preferences.insertSpaceBeforeOpeningBraceInBlock) { + } else if (head.endsWith(SPACE) && !fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_BRACE_IN_BLOCK)) { head = head.substring(head.length() - 1); } } StringBuffer buffer = new StringBuffer(head); buffer.append("{"); - if (preferences.insertSpaceAfterOpeningBraceInClosure) { + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_BRACE_IN_ARRAY_INITIALIZER)) { buffer.append(SPACE); } char[][] paramTypes = Signature.getParameterTypes(fProposal.getSignature()); if (lastParamIsClosure(paramTypes)) { - fArgumentOffsets[last] = buffer.length(); - fArgumentLengths[last] = 2; // select "it" + fPositions.get(last).setLength(2); // select "it" + fPositions.get(last).setOffset(buffer.length()); buffer.append("it"); - } else /* SAM type */ { + } else { // insert closure params for the abstract method - try { - IMethod sam = findSingleAbstractMethod(paramTypes[paramTypes.length - 1]); - String[] names = sam.getParameterNames(); - - int n = names.length; - for (int i = 0; i < n; i += 1) { - if (i > 0) { - if (preferences.insertSpaceBeforeCommaInClosureParams) { - buffer.append(SPACE); - } - buffer.append(COMMA); - if (preferences.insertSpaceAfterCommaInClosureParams) { - buffer.append(SPACE); - } + IMethod sam = findSingleAbstractMethod(paramTypes[paramTypes.length - 1]); + String[] names = sam.getParameterNames(); + + int n = names.length; + for (int i = 0; i < n; i += 1) { + if (i > 0) { + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_ARRAY_INITIALIZER)) { + buffer.append(SPACE); + } + buffer.append(COMMA); + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_ARRAY_INITIALIZER)) { + buffer.append(SPACE); } - // add position information for each closure argument to enhance linked mode - fArgumentLengths = insert(fArgumentLengths, last, names[i].length()); - fArgumentOffsets = insert(fArgumentOffsets, last, buffer.length()); - buffer.append(names[i]); - last += 1; - } - if (n > 0) { - buffer.append(SPACE); } - } catch (Exception e) { - GroovyContentAssist.logError(e); + // add position information for each closure argument to enhance linked mode + fPositions.add(last, new Position(buffer.length(), names[i].length())); + buffer.append(names[i]); + last += 1; + } + if (n > 0) { + buffer.append(SPACE); } buffer.append("->"); - fArgumentLengths[last] = 0; - fArgumentOffsets[last] = buffer.length(); + fPositions.get(last).setLength(0); + fPositions.get(last).setOffset(buffer.length()); } - if (preferences.insertSpaceBeforeClosingBraceInClosure) { + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_BRACE_IN_ARRAY_INITIALIZER)) { buffer.append(SPACE); } buffer.append("}"); @@ -390,47 +392,186 @@ protected String recomputeReplacementString() { return buffer.toString(); } + @Override + protected LazyJavaCompletionProposal createRequiredTypeCompletionProposal( + CompletionProposal completionProposal, JavaContentAssistInvocationContext invocationContext) { + LazyJavaCompletionProposal requiredProposal = super.createRequiredTypeCompletionProposal(completionProposal, invocationContext); + if (fProposal.getKind() == CompletionProposal.CONSTRUCTOR_INVOCATION && requiredProposal instanceof LazyJavaTypeCompletionProposal) { + if (fImportRewite != null) { + ReflectionUtils.setPrivateField(LazyJavaTypeCompletionProposal.class, "fImportRewrite", requiredProposal, fImportRewite); + } + if (requiredProposal instanceof LazyGenericTypeProposal) { + // disable generics completion for constructors (i.e. complete expression as "new ArrayList()" instead of "new ArrayList()" + try { + //requiredProposal.fTypeArgumentProposals = new LazyGenericTypeProposal.TypeArgumentProposal[0]; + ReflectionUtils.setPrivateField(LazyGenericTypeProposal.class, "fTypeArgumentProposals", requiredProposal, + Array.newInstance(Class.forName("org.eclipse.jdt.internal.ui.text.java.LazyGenericTypeProposal$TypeArgumentProposal"), 0)); + } catch (Exception e) { + GroovyContentAssist.logError(e); + } + } + } + return requiredProposal; + } + + @Override + protected char[] computeTriggerCharacters() { + if (fProposal instanceof GroovyCompletionProposal) { + boolean hasParameters = ((GroovyCompletionProposal) fProposal).hasParameters(); + return (!hasParameters ? ProposalUtils.METHOD_TRIGGERS : ProposalUtils.METHOD_WITH_ARGUMENTS_TRIGGERS); + } + return super.computeTriggerCharacters(); + } + + @Override + public int getContextInformationPosition() { + return fContextInformationPosition; + } + + @Override + public Point getSelection(IDocument document) { + if (fSelectedRegion != null) { + return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength()); + } + return super.getSelection(document); + } + @Override protected boolean needsLinkedMode() { - return super.needsLinkedMode(); + return (fPositions != null); } @Override protected void setUpLinkedMode(IDocument document, char closingCharacter) { - ITextViewer textViewer = getTextViewer(); - if (textViewer != null && fArgumentOffsets != null) { - int baseOffset = getReplacementOffset(); - String replacement = getReplacementString(); + if (getTextViewer() != null) { try { + int baseOffset = getReplacementOffset(); LinkedModeModel model = new LinkedModeModel(); - for (int i = 0, n = fArgumentOffsets.length; i < n; i += 1) { + for (int i = 0; i < fPositions.size(); i += 1) { + Position position = fPositions.get(i); + // change offset from relative to absolute + position.setOffset(baseOffset + position.getOffset()); LinkedPositionGroup group = new LinkedPositionGroup(); - group.addPosition(new LinkedPosition(document, baseOffset + fArgumentOffsets[i], fArgumentLengths[i], LinkedPositionGroup.NO_STOP)); + if (fProposals.size() <= i || fProposals.get(i).length < 2) { + group.addPosition(new LinkedPosition(document, position.getOffset(), position.getLength(), LinkedPositionGroup.NO_STOP)); + } else { + ensurePositionCategoryInstalled(document, model); + document.addPosition(fPositionCategory, position); + group.addPosition(new ProposalPosition(document, position.getOffset(), position.getLength(), LinkedPositionGroup.NO_STOP, fProposals.get(i))); + } model.addGroup(group); } - model.forceInstall(); - JavaEditor editor = getJavaEditor(); if (editor != null) { model.addLinkingListener(new EditorHighlightingSynchronizer(editor)); } + model.forceInstall(); - LinkedModeUI ui = new EditorLinkedModeUI(model, textViewer); - ui.setExitPosition(textViewer, baseOffset + replacement.length(), 0, Integer.MAX_VALUE); + LinkedModeUI ui = new EditorLinkedModeUI(model, getTextViewer()); + ui.setCyclingMode(LinkedModeUI.CYCLE_NEVER/*WHEN_NO_PARENT*/); + ui.setDoContextInfo(true); // displays above the argument list ui.setExitPolicy(new ExitPolicy(closingCharacter, document)); - ui.setDoContextInfo(true); - ui.setCyclingMode(LinkedModeUI.CYCLE_WHEN_NO_PARENT); + ui.setExitPosition(getTextViewer(), baseOffset + getCursorPosition(), 0, Integer.MAX_VALUE); ui.enter(); - fSelectedRegion = ui.getSelectedRegion(); - - } catch (BadLocationException e) { + } catch (BadLocationException | BadPositionCategoryException e) { GroovyContentAssist.logError(e); } } } - protected IMethod findSingleAbstractMethod(char[] typeSignature) throws JavaModelException { + //-------------------------------------------------------------------------- + + protected void computeReplacementProposals(char[][] namedParameterNames, char[][] positionalParameterNames, int indexOfLastClosure) { + boolean guess = (fInvocationContext.getCoreContext().isExtended() && + fPreferences.isEnabled(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS)); + char[][] parameterTypes = Signature.getParameterTypes(SignatureUtil.fix83600(fProposal.getSignature())); + + int npc = namedParameterNames.length, n = npc + positionalParameterNames.length; + + fPositions = new ArrayList<>(n); + fProposals = new ArrayList<>(n); + + for (int i = 0; i < n; i += 1) { + fPositions.add(new Position(0)); + + char[] name = (i < npc ? namedParameterNames[i] : positionalParameterNames[i - npc]); + // NOTE: named parameters come after positional parameters in the parameterTypes array + char[] type = (i < npc ? parameterTypes[i + positionalParameterNames.length] : parameterTypes[i - npc]); + + ICompletionProposal[] vals; + if (guess) { + boolean fillBestGuess = true; + String typeSignature = String.valueOf(type); + IJavaElement[] visibleElements = fInvocationContext.getCoreContext().getVisibleElements(typeSignature); + + vals = new ParameterGuesserDelegate(getEnclosingElement(), fInvocationContext).parameterProposals( + Signature.toString(typeSignature), String.valueOf(name), fPositions.get(i), visibleElements, fillBestGuess); + } else { + StringBuilder buffer = new StringBuilder(); + + if (fPreferences.isEnabled(GroovyContentAssist.CLOSURE_BRACKETS) && CharOperation.equals(type, CLOSURE_TYPE_SIGNATURE)) { + buffer.append("{"); + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_BRACE_IN_ARRAY_INITIALIZER)) { + buffer.append(SPACE); + } + + buffer.append("it"); + + if (fPreferences.isEnabled(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_BRACE_IN_ARRAY_INITIALIZER)) { + buffer.append(SPACE); + } + buffer.append("}"); + } else { + buffer.append(name); + } + + vals = new ICompletionProposal[] { + new JavaCompletionProposal(buffer.toString(), 0, buffer.length(), null, buffer.toString(), 1) + }; + } + + fProposals.add(vals); + } + } + + private void ensurePositionCategoryInstalled(final IDocument document, LinkedModeModel model) { + fPositionCategory = "ParameterGuessingProposal_" + toString(); + if (!document.containsPositionCategory(fPositionCategory)) { + fUpdater = new InclusivePositionUpdater(fPositionCategory); + document.addPositionCategory(fPositionCategory); + document.addPositionUpdater(fUpdater); + + model.addLinkingListener(new ILinkedModeListener() { + @Override + public void left(LinkedModeModel environment, int flags) { + ensurePositionCategoryRemoved(document); + } + + @Override + public void suspend(LinkedModeModel environment) { + } + + @Override + public void resume(LinkedModeModel environment, int flags) { + } + }); + } + } + + private void ensurePositionCategoryRemoved(IDocument document) { + if (document.containsPositionCategory(fPositionCategory)) { + try { + document.removePositionCategory(fPositionCategory); + } catch (BadPositionCategoryException e) { + // ignore + } + document.removePositionUpdater(fUpdater); + } + fUpdater = null; + } + + protected final IMethod findSingleAbstractMethod(char[] typeSignature) throws JavaModelException { char[] name = CharOperation.concat(Signature.getSignatureQualifier(typeSignature), Signature.getSignatureSimpleName(typeSignature), '.'); IType type = getJavaElement().getJavaProject().findType(String.valueOf(name)); if (type.exists()) { @@ -443,13 +584,17 @@ protected IMethod findSingleAbstractMethod(char[] typeSignature) throws JavaMode return null; } - //-------------------------------------------------------------------------- + protected final IJavaElement getEnclosingElement() { + if (fInvocationContext.getCoreContext().isExtended()) { + return fInvocationContext.getCoreContext().getEnclosingElement(); + } + return null; + } /** - * Returns the currently active Java editor, or null if it - * cannot be determined. + * @returns currently active Java editor, or {@code null} if not available */ - protected static JavaEditor getJavaEditor() { + protected final JavaEditor getJavaEditor() { IEditorPart part = JavaPlugin.getActivePage().getActiveEditor(); if (part instanceof JavaEditor) { return (JavaEditor) part; @@ -457,28 +602,16 @@ protected static JavaEditor getJavaEditor() { return null; } - protected static int[] insert(int[] array, int index, int value) { - final int length = array.length; - int[] result = new int[length + 1]; - Assert.isTrue(0 <= index && index <= length); - System.arraycopy(array, 0, result, 0, index); - result[index] = value; - if (length > index) { - System.arraycopy(array, index, result, index + 1, length - index); - } - return result; - } - - protected static boolean lastParamAcceptsClosure(char[][] parameterSignatures, JavaContentAssistInvocationContext context) { + protected final boolean lastParamAcceptsClosure(char[][] parameterSignatures) { if (lastParamIsClosure(parameterSignatures)) { return true; } int n = parameterSignatures.length; - if (n > 0 && context.getCoreContext().isExtended()) { + if (n > 0 && fInvocationContext.getCoreContext().isExtended()) { char[] lastType = Signature.getTypeErasure(parameterSignatures[n - 1]); if (Signature.getArrayCount(lastType) == 0) { GroovyExtendedCompletionContext groovyContext = (GroovyExtendedCompletionContext) - ReflectionUtils.getPrivateField(InternalCompletionContext.class, "extendedContext", context.getCoreContext()); + ReflectionUtils.getPrivateField(InternalCompletionContext.class, "extendedContext", fInvocationContext.getCoreContext()); return ClassHelper.isSAMType(groovyContext.toClassNode(lastType)); } @@ -486,7 +619,7 @@ protected static boolean lastParamAcceptsClosure(char[][] parameterSignatures, J return false; } - protected static boolean lastParamIsClosure(char[][] parameterSignatures) { + protected final boolean lastParamIsClosure(char[][] parameterSignatures) { int n = parameterSignatures.length; if (n > 0) { char[] lastType = Signature.getTypeErasure(parameterSignatures[n - 1]); @@ -497,13 +630,14 @@ protected static boolean lastParamIsClosure(char[][] parameterSignatures) { return false; } - protected static boolean lastParamIsClosure(char[][] parameterTypeNames, char[][] namedParameterTypeNames) { + protected final boolean lastParamIsClosure(char[][] parameterTypeNames, + char[][] namedParameterTypeNames) { char[] lastTypeName; - if (namedParameterTypeNames != null && namedParameterTypeNames.length > 0) { - lastTypeName = namedParameterTypeNames[namedParameterTypeNames.length - 1]; - } else if (parameterTypeNames != null && parameterTypeNames.length > 0) { + if (parameterTypeNames != null && parameterTypeNames.length > 0) { lastTypeName = parameterTypeNames[parameterTypeNames.length - 1]; - } else { // no args + }/* else if (namedParameterTypeNames != null && namedParameterTypeNames.length > 0) { + lastTypeName = namedParameterTypeNames[namedParameterTypeNames.length - 1]; + }*/ else { return false; } // we should be comparing against a fully qualified type name, but it is not always available so a simple name is close enough @@ -521,54 +655,62 @@ protected static boolean lastParamIsClosure(char[][] parameterTypeNames, char[][ */ protected static class ReplacementPreferences { - public ReplacementPreferences(IJavaProject project, FormatterPrefs prefs, ProposalFormattingOptions opts) { - insertSpaceBetweenEmptyParensInMethodCall = prefs.inEmptyList; - insertSpaceBeforeOpeningParenInMethodCall = prefs.beforeOpeningParen; - insertSpaceAfterOpeningParenInMethodCall = prefs.afterOpeningParen; - insertSpaceBeforeClosingParenInMethodCall = prefs.beforeClosingParen; - insertSpaceBeforeCommaInMethodCallArgs = prefs.beforeComma; - insertSpaceAfterCommaInMethodCallArgs = prefs.afterComma; - - useNamedArguments = opts.useNamedArguments; - // TODO: Add formatter preferences for Named Argument labels. - insertSpaceBeforeColonInNamedArgument = getCoreOption(project, prefs, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COLON_IN_LABELED_STATEMENT, false); - insertSpaceAfterColonInNamedArgument = getCoreOption(project, prefs, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COLON_IN_LABELED_STATEMENT, true); - - useClosureLiteral = opts.useBracketsForClosures; - insertClosureAfterClosingParenInMethodCall = opts.noParensAroundClosures; - // TODO: Add formatter preferences for Closure literals, or switch to Lambda prefs if/when they become available. - // TODO: Add formatter preferences for inserting space before and after Closure arrow, or switch to Lambda prefs if/when they become available. - insertSpaceAfterOpeningBraceInClosure = getCoreOption(project, prefs, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_BRACE_IN_ARRAY_INITIALIZER, true); - insertSpaceBeforeClosingBraceInClosure = getCoreOption(project, prefs, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_BRACE_IN_ARRAY_INITIALIZER, true); - insertSpaceBeforeOpeningBraceInBlock = getCoreOption(project, prefs, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_BRACE_IN_BLOCK, true); - insertSpaceBeforeCommaInClosureParams = getCoreOption(project, prefs, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_ARRAY_INITIALIZER, false); - insertSpaceAfterCommaInClosureParams = getCoreOption(project, prefs, DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_ARRAY_INITIALIZER, true); + public ReplacementPreferences(ProposalFormattingOptions opts, FormatterPrefs prefs, IJavaProject project) { + + bCommandChaining = opts.noParens; // no preference exists for this + cache.put(GroovyContentAssist.NAMED_ARGUMENTS, opts.useNamedArguments); + cache.put(GroovyContentAssist.CLOSURE_BRACKETS, opts.useBracketsForClosures); + cache.put(GroovyContentAssist.CLOSURE_NOPARENS, opts.noParensAroundClosures); + + cache.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_PAREN_IN_METHOD_INVOCATION, prefs.beforeOpeningParen); + cache.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_PAREN_IN_METHOD_INVOCATION, prefs.afterOpeningParen); + cache.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_METHOD_INVOCATION_ARGUMENTS, prefs.beforeComma); + cache.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_METHOD_INVOCATION_ARGUMENTS, prefs.afterComma); + cache.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_PAREN_IN_METHOD_INVOCATION, prefs.beforeClosingParen); + cache.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BETWEEN_EMPTY_PARENS_IN_METHOD_INVOCATION, prefs.inEmptyList); + + cache.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_COMMA_IN_PARAMETERIZED_TYPE_REFERENCE, prefs.beforeTypeArgumentComma); + cache.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_PARAMETERIZED_TYPE_REFERENCE, prefs.afterTypeArgumentComma); + cache.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_OPENING_ANGLE_BRACKET_IN_PARAMETERIZED_TYPE_REFERENCE, prefs.beforeOpeningBracket); + cache.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_OPENING_ANGLE_BRACKET_IN_PARAMETERIZED_TYPE_REFERENCE, prefs.afterOpeningBracket); + cache.put(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_CLOSING_ANGLE_BRACKET_IN_PARAMETERIZED_TYPE_REFERENCE, prefs.beforeClosingBracket); + + IPreferenceStore uiPrefs = JavaPlugin.getDefault().getPreferenceStore(); boolean fillArgs; + cache.put(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES, (fillArgs = uiPrefs.getBoolean(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES))); + cache.put(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, (fillArgs && uiPrefs.getBoolean(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS))); + + computer = key -> { + boolean def = getDefaultOptions().get(key).matches(JavaCore.ENABLED + "|" + JavaCore.INSERT); + Boolean val = (Boolean) ReflectionUtils.executePrivateMethod(FormatterPrefs.class, "getCoreOption", + new Class[] {IJavaProject.class, String.class, boolean.class}, prefs, new Object[] {project, key, def}); + return val; + }; } - // lift from org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal.FormatterPrefs - protected boolean getCoreOption(IJavaProject prj, FormatterPrefs ref, String key, boolean def) { - Boolean val = (Boolean) ReflectionUtils.executePrivateMethod(FormatterPrefs.class, "getCoreOption", - new Class[] {IJavaProject.class, String.class, boolean.class}, ref, new Object[] {prj, key, def}); - return val.booleanValue(); + public final boolean bCommandChaining; + + private final Map cache = new HashMap<>(32); + private final Function computer; + private Map defaults; + + private Map getDefaultOptions() { + if (defaults == null) { + defaults = JavaCore.getDefaultOptions(); + } + return defaults; } - public final boolean insertSpaceBetweenEmptyParensInMethodCall; - public final boolean insertSpaceBeforeOpeningParenInMethodCall; - public final boolean insertSpaceAfterOpeningParenInMethodCall ; - public final boolean insertSpaceBeforeClosingParenInMethodCall; - public final boolean insertSpaceBeforeCommaInMethodCallArgs; - public final boolean insertSpaceAfterCommaInMethodCallArgs; - - public final boolean useNamedArguments; - public final boolean insertSpaceBeforeColonInNamedArgument; - public final boolean insertSpaceAfterColonInNamedArgument; - - public final boolean useClosureLiteral; - public final boolean insertClosureAfterClosingParenInMethodCall; - public final boolean insertSpaceAfterOpeningBraceInClosure; - public final boolean insertSpaceBeforeClosingBraceInClosure; - public final boolean insertSpaceBeforeOpeningBraceInBlock; - public final boolean insertSpaceBeforeCommaInClosureParams; - public final boolean insertSpaceAfterCommaInClosureParams; + public final boolean isEnabled(String key) { + return cache.computeIfAbsent(key, computer).booleanValue(); + } + } + + /** + * Not API; for testing only! + * + * @return the guessed parameter proposals + */ + public List getChoices() { + return fProposals; } } diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/NamedParameterProposal.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/NamedParameterProposal.java index 89b12c7e7c..da0bc5c481 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/NamedParameterProposal.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/NamedParameterProposal.java @@ -19,7 +19,6 @@ import org.codehaus.groovy.eclipse.codeassist.GroovyContentAssist; import org.codehaus.groovy.eclipse.codeassist.ProposalUtils; import org.eclipse.core.runtime.Adapters; -import org.eclipse.jdt.core.CompletionContext; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.JavaModelException; @@ -28,8 +27,10 @@ import org.eclipse.jdt.internal.ui.javaeditor.EditorHighlightingSynchronizer; import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; import org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposal; +import org.eclipse.jdt.ui.PreferenceConstants; import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext; import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.IDocument; @@ -62,10 +63,6 @@ public class NamedParameterProposal extends JavaCompletionProposal { private IRegion selectedRegion; - private final CompletionContext coreContext; - - private final boolean tryParamGuessing; - private ICompletionProposal[] choices; private Position paramNamePosition; @@ -85,14 +82,11 @@ public NamedParameterProposal( StyledString displayString, int relevance, boolean inJavadoc, - JavaContentAssistInvocationContext invocationContext, - boolean tryParamGuessing) { + JavaContentAssistInvocationContext javaContext) { - super(computeReplacementString(paramName), replacementOffset, replacementLength, image, displayString, relevance, inJavadoc, invocationContext); + super(computeReplacementString(paramName), replacementOffset, replacementLength, image, displayString, relevance, inJavadoc, javaContext); this.paramName = paramName; this.paramSignature = paramSignature; - this.tryParamGuessing = tryParamGuessing; - coreContext = invocationContext.getCoreContext(); this.setTriggerCharacters(ProposalUtils.VAR_TRIGGER); } @@ -131,32 +125,37 @@ private IRegion calculateArgumentRegion() { * @return {@code true} iff parameter guessing should be performed */ private boolean shouldDoGuessing() { - return tryParamGuessing && coreContext.isExtended(); + if (fInvocationContext.getCoreContext().isExtended()) { + IPreferenceStore prefs = JavaPlugin.getDefault().getPreferenceStore(); + return (prefs.getBoolean(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES) && + prefs.getBoolean(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS)); + } + return false; } private ICompletionProposal[] guessParameters(char[] parameterName) throws JavaModelException { if (paramSignature == null) { return NO_COMPLETIONS; } + String type = Signature.toString(paramSignature); IJavaElement[] assignableElements = getAssignableElements(); Position position = new Position(selectedRegion.getOffset(), selectedRegion.getLength()); - ICompletionProposal[] argumentProposals = new ParameterGuesserDelegate(getEnclosingElement(), fInvocationContext).parameterProposals(type, paramName, position, assignableElements, tryParamGuessing); - if (argumentProposals.length == 0) { - argumentProposals = new ICompletionProposal[] { + + ParameterGuesserDelegate guesser = new ParameterGuesserDelegate( + fInvocationContext.getCoreContext().getEnclosingElement(), fInvocationContext); + ICompletionProposal[] guesses = guesser.parameterProposals(type, paramName, position, assignableElements, true); + if (guesses.length == 0) { + guesses = new ICompletionProposal[] { new JavaCompletionProposal(paramName, 0, paramName.length(), null, paramName, 0) }; } paramNamePosition = position; - return choices = argumentProposals; - } - - private IJavaElement getEnclosingElement() { - return coreContext.getEnclosingElement(); + return (choices = guesses); } private IJavaElement[] getAssignableElements() { - return coreContext.getVisibleElements(paramSignature); + return fInvocationContext.getCoreContext().getVisibleElements(paramSignature); } @Override diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/ParameterGuesserDelegate.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/ParameterGuesserDelegate.java index 413f61a021..d5142d39f3 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/ParameterGuesserDelegate.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/completions/ParameterGuesserDelegate.java @@ -132,7 +132,15 @@ private ICompletionProposal[] addExtras(ICompletionProposal[] parameterProposals if (proposal != null) { if (parameterProposals[parameterProposals.length - 1].getDisplayString().equals("null")) { // replace 'null' proposal with simple value proposal - parameterProposals[parameterProposals.length - 1] = proposal; + if (VariableScope.CLOSURE_CLASS_NODE.getName().equals(expectedType) && + GroovyContentAssist.getDefault().getPreferenceStore().getBoolean(GroovyContentAssist.CLOSURE_BRACKETS)) { + for (int i = (parameterProposals.length - 1); i > 0; i -=1) { + parameterProposals[i] = parameterProposals[i - 1]; + } + parameterProposals[0] = proposal; + } else { + parameterProposals[parameterProposals.length - 1] = proposal; + } } else { parameterProposals = (ICompletionProposal[]) ArrayUtils.add(parameterProposals, proposal); } diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/factories/AnnotationMemberValueCompletionProcessorFactory.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/factories/AnnotationMemberValueCompletionProcessorFactory.java index 2007055403..d021df0925 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/factories/AnnotationMemberValueCompletionProcessorFactory.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/factories/AnnotationMemberValueCompletionProcessorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,6 @@ import org.codehaus.groovy.eclipse.codeassist.creators.FieldProposalCreator; import org.codehaus.groovy.eclipse.codeassist.creators.MethodProposalCreator; import org.codehaus.groovy.eclipse.codeassist.processors.AbstractGroovyCompletionProcessor; -import org.codehaus.groovy.eclipse.codeassist.processors.GroovyCompletionProposal; import org.codehaus.groovy.eclipse.codeassist.processors.IGroovyCompletionProcessor; import org.codehaus.groovy.eclipse.codeassist.processors.PackageCompletionProcessor; import org.codehaus.groovy.eclipse.codeassist.processors.TypeCompletionProcessor; @@ -255,7 +254,7 @@ protected final boolean isNotStaticImported(FieldNode fieldNode) { protected final ICompletionProposal newEnumTypeProposal(ClassNode enumType) { String signature = GroovyUtils.getTypeSignatureWithoutGenerics(enumType, true, true); - GroovyCompletionProposal proposal = new GroovyCompletionProposal(CompletionProposal.TYPE_REF, 0); + CompletionProposal proposal = CompletionProposal.create(CompletionProposal.TYPE_REF, 0); proposal.setSignature(signature.toCharArray()); proposal.setFlags(enumType.getModifiers()); diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/preferences/ContentAssistPreferencesPage.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/preferences/ContentAssistPreferencesPage.java index d9d6ce9ba8..e7a4105220 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/preferences/ContentAssistPreferencesPage.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/preferences/ContentAssistPreferencesPage.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IInputValidator; import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.ListEditor; import org.eclipse.jface.window.Window; @@ -40,6 +41,8 @@ import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.dialogs.PreferenceLinkArea; +import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; public class ContentAssistPreferencesPage extends FieldEditorOverlayPage implements IWorkbenchPreferencePage { @@ -203,8 +206,6 @@ protected void createFieldEditors() { addField(new BooleanFieldEditor(GroovyContentAssist.NAMED_ARGUMENTS, "Use named arguments for method calls", fieldGroup)); - addField(new BooleanFieldEditor(GroovyContentAssist.PARAMETER_GUESSING, - "Use guessed arguments for method calls", fieldGroup)); addField(new BooleanFieldEditor(GroovyContentAssist.CLOSURE_BRACKETS, "Use closure literals for closure arguments", fieldGroup)); addField(new BooleanFieldEditor(GroovyContentAssist.CLOSURE_NOPARENS, @@ -215,12 +216,15 @@ protected void createFieldEditors() { addField(new CompletionFilterListEditor("Filtered DGMs", "Default Groovy Methods that will be filtered from content assist", fieldGroup)); + + // + insertPageLink("org.eclipse.jdt.ui.preferences.CodeAssistPreferencePage", + "Additional preferences are inherited from Java Content Assist"); } private Composite createFieldGroup(String label) { Group group = new Group(getFieldEditorParent(), SWT.SHADOW_NONE); - group.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); - ((GridData) group.getLayoutData()).horizontalSpan = 2; + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP).grab(true, false).span(2, 1).applyTo(group); group.setFont(group.getParent().getFont()); group.setLayout(new GridLayout()); group.setText(label); @@ -230,4 +234,10 @@ private Composite createFieldGroup(String label) { panel.setLayoutData(new GridData(GridData.FILL_BOTH)); return panel; } + + private void insertPageLink(String page, String text) { + PreferenceLinkArea linkArea = new PreferenceLinkArea(getFieldEditorParent(), + SWT.WRAP, page, text, (IWorkbenchPreferenceContainer) getContainer(), null); + GridDataFactory.fillDefaults().indent(0, 8).applyTo(linkArea.getControl()); + } } diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/AbstractGroovyCompletionProcessor.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/AbstractGroovyCompletionProcessor.java index 496b9880cd..e87e9d2ae7 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/AbstractGroovyCompletionProcessor.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/AbstractGroovyCompletionProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,7 +57,7 @@ protected IProposalCreator[] getAllProposalCreators() { protected final GroovyCompletionProposal createProposal(int kind, int completionOffset) { GroovyCompletionProposal proposal = new GroovyCompletionProposal(kind, completionOffset); - proposal.setNameLookup(this.nameEnvironment.nameLookup); + proposal.setNameLookup(nameEnvironment.nameLookup); return proposal; } } diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/GroovyProposalTypeSearchRequestor.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/GroovyProposalTypeSearchRequestor.java index 801858414a..6d8cad899d 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/GroovyProposalTypeSearchRequestor.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/GroovyProposalTypeSearchRequestor.java @@ -614,16 +614,8 @@ private ICompletionProposal proposeConstructor(char[] simpleTypeName, int parame relevanceMultiplier += computeRelevanceForCaseMatching(completionExpressionChars, simpleTypeName); proposal.setRelevance(Relevance.MEDIUM_HIGH.getRelevance(relevanceMultiplier)); - GroovyJavaMethodCompletionProposal lazyProposal = new GroovyJavaMethodCompletionProposal(proposal, javaContext, getProposalOptions()); + GroovyJavaMethodCompletionProposal lazyProposal = new GroovyJavaMethodCompletionProposal(proposal, getProposalOptions(), javaContext, null); lazyProposal.setImportRewite(groovyRewriter.getImportRewrite(monitor)); - if (contextOnly) { - lazyProposal.contextOnly(); - } - if (proposal.hasParameters()) { - lazyProposal.setTriggerCharacters(ProposalUtils.METHOD_WITH_ARGUMENTS_TRIGGERS); - } else { - lazyProposal.setTriggerCharacters(ProposalUtils.METHOD_TRIGGERS); - } return lazyProposal; } diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/StatementAndExpressionCompletionProcessor.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/StatementAndExpressionCompletionProcessor.java index 0ee77f2e7d..91d9cb0ce6 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/StatementAndExpressionCompletionProcessor.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/processors/StatementAndExpressionCompletionProcessor.java @@ -568,6 +568,6 @@ private IProposalCreator[] chooseProposalCreators() { return getAllProposalCreators(); } - private static final Pattern FIELD_ACCESS_COMPLETION = Pattern.compile(".+\\.@\\s*(?:\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)?", Pattern.DOTALL); - private static final Pattern METHOD_POINTER_COMPLETION = Pattern.compile(".+\\.&\\s*(?:\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)?", Pattern.DOTALL); + public static final Pattern FIELD_ACCESS_COMPLETION = Pattern.compile(".+\\.@\\s*(?:\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)?", Pattern.DOTALL); + public static final Pattern METHOD_POINTER_COMPLETION = Pattern.compile(".+\\.&\\s*(?:\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)?", Pattern.DOTALL); } diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/proposals/GroovyMethodProposal.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/proposals/GroovyMethodProposal.java index f0d645b2b9..57ecf66eaa 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/proposals/GroovyMethodProposal.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/proposals/GroovyMethodProposal.java @@ -16,13 +16,13 @@ package org.codehaus.groovy.eclipse.codeassist.proposals; import static org.codehaus.groovy.eclipse.codeassist.ProposalUtils.createTypeSignature; +import static org.codehaus.groovy.eclipse.codeassist.processors.StatementAndExpressionCompletionProcessor.METHOD_POINTER_COMPLETION; import org.codehaus.groovy.ast.AnnotatedNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.eclipse.codeassist.GroovyContentAssist; import org.codehaus.groovy.eclipse.codeassist.ProposalUtils; -import org.codehaus.groovy.eclipse.codeassist.completions.GroovyJavaGuessingCompletionProposal; import org.codehaus.groovy.eclipse.codeassist.completions.GroovyJavaMethodCompletionProposal; import org.codehaus.groovy.eclipse.codeassist.completions.NamedArgsMethodNode; import org.codehaus.groovy.eclipse.codeassist.processors.GroovyCompletionProposal; @@ -84,9 +84,27 @@ public void setProposalFormattingOptions(ProposalFormattingOptions options) { @Override public IJavaCompletionProposal createJavaProposal(ContentAssistContext context, JavaContentAssistInvocationContext javaContext) { - int kind = (context.location == ContentAssistLocation.ANNOTATION_BODY ? CompletionProposal.ANNOTATION_ATTRIBUTE_REF : CompletionProposal.METHOD_REF); + int kind; + switch (context.location) { + case ANNOTATION_BODY: + kind = CompletionProposal.ANNOTATION_ATTRIBUTE_REF; + break; + case IMPORT: + kind = CompletionProposal.METHOD_NAME_REFERENCE; + break; + case EXPRESSION: + if (METHOD_POINTER_COMPLETION.matcher(context.fullCompletionExpression).matches()) { + kind = CompletionProposal.METHOD_NAME_REFERENCE; + break; + } + default: + kind = CompletionProposal.METHOD_REF; + } + GroovyCompletionProposal proposal = new GroovyCompletionProposal(kind, context.completionLocation); + // if location is METHOD_CONTEXT, then the type must be MethodInfoContentAssistContext + // ...but there are other times when the type is MethodInfoContentAssistContext as well if (context.location == ContentAssistLocation.METHOD_CONTEXT) { // only show context information and only for methods that exactly match the name // this happens when we are at the start of an argument or an open paren @@ -94,8 +112,8 @@ public IJavaCompletionProposal createJavaProposal(ContentAssistContext context, if (!methodContext.methodName.equals(method.getName())) { return null; } - proposal.setReplaceRange(context.completionLocation, context.completionLocation); proposal.setCompletion(CharOperation.NO_CHAR); + proposal.setReplaceRange(context.completionLocation, context.completionLocation); } else { // this is a normal method proposal boolean parens = (kind == CompletionProposal.ANNOTATION_ATTRIBUTE_REF ? false : !isParens(context, javaContext)); proposal.setCompletion(completionName(parens)); @@ -155,23 +173,7 @@ public IJavaCompletionProposal createJavaProposal(ContentAssistContext context, lazyProposal = new LazyJavaCompletionProposal(proposal, javaContext); lazyProposal.setProposalInfo(new AnnotationAtttributeProposalInfo(javaContext.getProject(), proposal)); } else { - if (getProposalFormattingOptions().doParameterGuessing) { - lazyProposal = GroovyJavaGuessingCompletionProposal.createProposal(proposal, javaContext, true, contributor, getProposalFormattingOptions()); - } - if (lazyProposal == null) { - lazyProposal = new GroovyJavaMethodCompletionProposal(proposal, javaContext, getProposalFormattingOptions(), contributor); - // if location is METHOD_CONTEXT, then the type must be MethodInfoContentAssistContext - // ...but there are other times when the type is MethodInfoContentAssistContext as well - if (context.location == ContentAssistLocation.METHOD_CONTEXT) { - ((GroovyJavaMethodCompletionProposal) lazyProposal).contextOnly(); - } - } - if (context.location == ContentAssistLocation.METHOD_CONTEXT) { // NOTE: I've seen STATEMENT for 'assertNu|' and EXPRESSION for 'Assert.assertNu|' - // attempt to find the location immediately after the opening paren - // if this is wrong, no big deal, but the context information will not be properly highlighted - // assume that there is the method name, and then an opening paren (or a space) and then the arguments - lazyProposal.setContextInformationPosition(((MethodInfoContentAssistContext) context).methodNameEnd + 1); - } + lazyProposal = new GroovyJavaMethodCompletionProposal(proposal, getProposalFormattingOptions(), javaContext, contributor); } return lazyProposal; } diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/proposals/GroovyNamedArgumentProposal.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/proposals/GroovyNamedArgumentProposal.java index a10b9db62f..76c279cb14 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/proposals/GroovyNamedArgumentProposal.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/proposals/GroovyNamedArgumentProposal.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,9 +59,8 @@ public IJavaCompletionProposal createJavaProposal(ContentAssistContext context, StyledString displayString = createDisplayString(); int relevance = Relevance.VERY_HIGH.getRelevance(); boolean inJavadoc = false; - boolean tryParameterGuessing = ProposalFormattingOptions.newFromOptions().doParameterGuessing; - return new NamedParameterProposal(paramName, paramSignature, offset, length, image, displayString, relevance, inJavadoc, javaContext, tryParameterGuessing); + return new NamedParameterProposal(paramName, paramSignature, offset, length, image, displayString, relevance, inJavadoc, javaContext); } protected StyledString createDisplayString() { diff --git a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/proposals/ProposalFormattingOptions.java b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/proposals/ProposalFormattingOptions.java index 56dadffa89..c3965c2896 100644 --- a/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/proposals/ProposalFormattingOptions.java +++ b/ide/org.codehaus.groovy.eclipse.codeassist.completion/src/org/codehaus/groovy/eclipse/codeassist/proposals/ProposalFormattingOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2017 the original author or authors. + * Copyright 2009-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,11 +27,10 @@ public static ProposalFormattingOptions newFromOptions() { final boolean noParensAroundClosures = prefs.getBoolean(GroovyContentAssist.CLOSURE_NOPARENS); final boolean useBracketsForClosures = prefs.getBoolean(GroovyContentAssist.CLOSURE_BRACKETS); - final boolean doParameterGuessing = prefs.getBoolean(GroovyContentAssist.PARAMETER_GUESSING); final boolean useNamedArguments = prefs.getBoolean(GroovyContentAssist.NAMED_ARGUMENTS); final boolean noParensInChains = false; - return new ProposalFormattingOptions(noParensAroundClosures, useBracketsForClosures, useNamedArguments, doParameterGuessing, noParensInChains); + return new ProposalFormattingOptions(noParensAroundClosures, useBracketsForClosures, useNamedArguments, noParensInChains); } public final boolean noParensAroundClosures; @@ -40,21 +39,17 @@ public static ProposalFormattingOptions newFromOptions() { public final boolean useNamedArguments; - public final boolean doParameterGuessing; - // used for DSL command expressions public final boolean noParens; - public ProposalFormattingOptions( + private ProposalFormattingOptions( boolean noParensAroundArgs, boolean useBracketsForClosures, boolean useNamedArguments, - boolean doParameterGuessing, boolean noParens) { this.noParensAroundClosures = noParensAroundArgs; this.useBracketsForClosures = useBracketsForClosures; this.useNamedArguments = useNamedArguments; - this.doParameterGuessing = doParameterGuessing; this.noParens = noParens; } @@ -62,9 +57,9 @@ public ProposalFormattingOptions newFromExisting(boolean overrideUseNamedArgs, b // For named args if overridden, always use named args // if not a constructor and not overridden, never use named args if (overrideUseNamedArgs || overrideNoParens) { - return new ProposalFormattingOptions(noParensAroundClosures, useBracketsForClosures, overrideUseNamedArgs, doParameterGuessing, overrideNoParens); + return new ProposalFormattingOptions(noParensAroundClosures, useBracketsForClosures, overrideUseNamedArgs, overrideNoParens); } else if (useNamedArguments && !(method instanceof ConstructorNode)) { - return new ProposalFormattingOptions(noParensAroundClosures, useBracketsForClosures, false, doParameterGuessing, overrideNoParens); + return new ProposalFormattingOptions(noParensAroundClosures, useBracketsForClosures, false, overrideNoParens); } else { return this; }