From cd9fe2dad2ed1383bda8c2d6dd0b73653c27e20e Mon Sep 17 00:00:00 2001 From: Holger Schill Date: Wed, 16 Jan 2019 14:29:46 +0100 Subject: [PATCH 1/3] [#677] Eclipse Xtend favorites --- .../ConfigurableCompletionProposal.java | 9 ++ .../ISmartContextInformation.java | 5 + .../ImportingTypesProposalProvider.java | 60 ++++++++---- .../ParameterContextInformation.java | 11 ++- .../contentassist/XbaseProposalProvider.java | 93 +++++++++++++++++++ 5 files changed, 155 insertions(+), 23 deletions(-) diff --git a/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/contentassist/ConfigurableCompletionProposal.java b/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/contentassist/ConfigurableCompletionProposal.java index 293b008468..be9e5f7318 100644 --- a/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/contentassist/ConfigurableCompletionProposal.java +++ b/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/contentassist/ConfigurableCompletionProposal.java @@ -347,6 +347,15 @@ public void setSelectionStart(int selectionStart) { this.selectionStart = selectionStart; } + public void shiftOffset(int deltaLength) { + this.cursorPosition = this.cursorPosition + deltaLength; + this.selectionStart = this.selectionStart + deltaLength; + if (this.contextInformation instanceof ISmartContextInformation) { + ISmartContextInformation casted = (ISmartContextInformation) contextInformation; + casted.setContextInformationPosition(casted.getContextInformationPosition() + deltaLength); + } + } + public void setSimpleLinkedMode(ITextViewer viewer, char... exitChars) { this.linkedMode = true; this.viewer = viewer; diff --git a/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/contentassist/ISmartContextInformation.java b/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/contentassist/ISmartContextInformation.java index a022cd575e..5bc079310c 100644 --- a/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/contentassist/ISmartContextInformation.java +++ b/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/contentassist/ISmartContextInformation.java @@ -34,4 +34,9 @@ public interface ISmartContextInformation extends IContextInformation, IContextI */ boolean updatePresentation(ITextViewer viewer, int offset, TextPresentation presentation); + /** + * @since 2.17 + */ + void setContextInformationPosition(int pos); + } diff --git a/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/ImportingTypesProposalProvider.java b/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/ImportingTypesProposalProvider.java index 0224c7e323..f13e31a8f2 100644 --- a/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/ImportingTypesProposalProvider.java +++ b/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/ImportingTypesProposalProvider.java @@ -29,6 +29,7 @@ import org.eclipse.text.edits.TextEdit; import org.eclipse.xtext.EcoreUtil2; import org.eclipse.xtext.common.types.JvmDeclaredType; +import org.eclipse.xtext.common.types.JvmFeature; import org.eclipse.xtext.common.types.xtext.ui.JdtTypesProposalProvider; import org.eclipse.xtext.conversion.IValueConverter; import org.eclipse.xtext.naming.IQualifiedNameConverter; @@ -108,19 +109,18 @@ public void apply(IDocument document, ConfigurableCompletionProposal proposal) t return; } } - + String typeNameInScope = typeName; + // ReplacementString might have brackets if it's a JVMExecutable + int indexOfBracket = typeName.lastIndexOf("("); + if(indexOfBracket > -1) { + typeNameInScope = typeName.substring(0, indexOfBracket); + } + QualifiedName qualifiedNameInScope = qualifiedNameConverter.toQualifiedName(typeNameInScope); // we could create an import statement if there is no conflict QualifiedName qualifiedName = qualifiedNameConverter.toQualifiedName(typeName); - if (qualifiedName.getSegmentCount() == 1) { - // type resides in default package - no need to hassle with imports - proposal.setCursorPosition(proposalReplacementString.length()); - document.replace(proposal.getReplacementOffset(), proposal.getReplacementLength(), - proposalReplacementString); - return; - } - IEObjectDescription description = scope.getSingleElement(qualifiedName.skipFirst(qualifiedName + IEObjectDescription description = scope.getSingleElement(qualifiedNameInScope.skipFirst(qualifiedNameInScope .getSegmentCount() - 1)); - IEObjectDescription typeToImport = scope.getSingleElement(qualifiedName); + IEObjectDescription typeToImport = scope.getSingleElement(qualifiedNameInScope); if (description != null) { if (typeToImport != null && !description.getEObjectURI().equals(typeToImport.getEObjectURI())) { // there exists a conflict - insert fully qualified name @@ -151,9 +151,20 @@ public void apply(IDocument document, ConfigurableCompletionProposal proposal) t return; } EObject resolved = EcoreUtil.resolve(typeToImport.getEObjectOrProxy(), context); - Assert.isTrue(!resolved.eIsProxy() && resolved instanceof JvmDeclaredType); - importSection.addImport((JvmDeclaredType) resolved); - + // In case we want to import a static JvmFeature - https://github.com/eclipse/xtext-xtend/issues/677 + if(resolved instanceof JvmFeature) { + JvmFeature operation = (JvmFeature) resolved; + if(operation.isStatic()) { + EObject container = operation.eContainer(); + if(container instanceof JvmDeclaredType) { + JvmDeclaredType type = (JvmDeclaredType) container; + importSection.addStaticImport(type.getQualifiedName(), operation.getSimpleName()); + } + } + } else { + Assert.isTrue(!resolved.eIsProxy() && resolved instanceof JvmDeclaredType); + importSection.addImport((JvmDeclaredType) resolved); + } DocumentRewriteSession rewriteSession = null; try { if (document instanceof IDocumentExtension4) { @@ -162,10 +173,18 @@ public void apply(IDocument document, ConfigurableCompletionProposal proposal) t } // apply short proposal String escapedShortname = shortName; - if (valueConverter != null) { - escapedShortname = valueConverter.toString(shortName); + + if(indexOfBracket > -1) { + if (valueConverter != null) { + escapedShortname = valueConverter.toString(typeNameInScope) + shortName.substring(indexOfBracket); + } + proposal.setCursorPosition(escapedShortname.length()-1); + } else { + if (valueConverter != null) { + escapedShortname = valueConverter.toString(shortName); + } + proposal.setCursorPosition(escapedShortname.length()); } - proposal.setCursorPosition(escapedShortname.length()); int initialCursorLine = document.getLineOfOffset(proposal.getReplacementOffset()); ReplaceEdit replaceEdit = new ReplaceEdit(proposal.getReplacementOffset(), proposal.getReplacementLength(), escapedShortname); @@ -181,10 +200,11 @@ public void apply(IDocument document, ConfigurableCompletionProposal proposal) t textEdit = replaceEdit; } textEdit.apply(document); - - int cursorPosition = proposal.getCursorPosition() + replaceConverter.getReplaceLengthDelta(importChanges, proposal.getReplacementOffset()); - proposal.setCursorPosition(cursorPosition); - int newCursorLine = document.getLineOfOffset(cursorPosition); + // delta caused by the newly introduced imports + int deltaLength = replaceConverter.getReplaceLengthDelta(importChanges, proposal.getSelectionStart()); + // shift offset by deltaLength + proposal.shiftOffset(deltaLength); + int newCursorLine = document.getLineOfOffset(proposal.getCursorPosition()); // set the pixel coordinates if (widget != null) { diff --git a/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/ParameterContextInformation.java b/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/ParameterContextInformation.java index 0c0f4256c2..a0297120c6 100644 --- a/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/ParameterContextInformation.java +++ b/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/ParameterContextInformation.java @@ -42,13 +42,13 @@ public class ParameterContextInformation implements ISmartContextInformation { private final String contextDisplayString; private int parameterListOffset; private int currentParameter = -1; - private int initialCarretOffset; + private int initialCaretOffset; public ParameterContextInformation(ParameterData data, String contextDisplayString, int parameterListOffset, int initialCarretOffset) { this.data = data; this.contextDisplayString = contextDisplayString; this.parameterListOffset = parameterListOffset; - this.initialCarretOffset = initialCarretOffset; + this.initialCaretOffset = initialCarretOffset; } @Override @@ -91,7 +91,12 @@ public String getContextDisplayString() { @Override public int getContextInformationPosition() { - return initialCarretOffset; + return initialCaretOffset; + } + + @Override + public void setContextInformationPosition(int pos) { + this.initialCaretOffset = pos; } @Override diff --git a/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/XbaseProposalProvider.java b/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/XbaseProposalProvider.java index fa899e6d9c..4ead7b9925 100644 --- a/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/XbaseProposalProvider.java +++ b/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/XbaseProposalProvider.java @@ -12,6 +12,7 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.jdt.core.search.IJavaSearchConstants; +import org.eclipse.jdt.ui.PreferenceConstants; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.contentassist.ICompletionProposal; @@ -24,12 +25,16 @@ import org.eclipse.xtext.Group; import org.eclipse.xtext.Keyword; import org.eclipse.xtext.RuleCall; +import org.eclipse.xtext.common.types.JvmConstructor; +import org.eclipse.xtext.common.types.JvmDeclaredType; import org.eclipse.xtext.common.types.JvmExecutable; import org.eclipse.xtext.common.types.JvmFeature; import org.eclipse.xtext.common.types.JvmFormalParameter; import org.eclipse.xtext.common.types.JvmIdentifiableElement; +import org.eclipse.xtext.common.types.JvmType; import org.eclipse.xtext.common.types.JvmTypeReference; import org.eclipse.xtext.common.types.TypesPackage; +import org.eclipse.xtext.common.types.util.TypeReferences; import org.eclipse.xtext.common.types.xtext.ui.ITypesProposalProvider; import org.eclipse.xtext.common.types.xtext.ui.TypeMatchFilters; import org.eclipse.xtext.conversion.IValueConverter; @@ -42,8 +47,11 @@ import org.eclipse.xtext.nodemodel.INode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.resource.IEObjectDescription; +import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.scoping.IScope; +import org.eclipse.xtext.scoping.impl.SimpleScope; import org.eclipse.xtext.ui.editor.contentassist.ConfigurableCompletionProposal; +import org.eclipse.xtext.ui.editor.contentassist.ConfigurableCompletionProposal.IReplacementTextApplier; import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext; import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor; import org.eclipse.xtext.ui.editor.contentassist.PrefixMatcher; @@ -59,18 +67,23 @@ import org.eclipse.xtext.xbase.XMemberFeatureCall; import org.eclipse.xtext.xbase.XbasePackage; import org.eclipse.xtext.xbase.conversion.XbaseQualifiedNameValueConverter; +import org.eclipse.xtext.xbase.imports.RewritableImportSection; import org.eclipse.xtext.xbase.scoping.SyntaxFilteredScopes; import org.eclipse.xtext.xbase.scoping.batch.IIdentifiableElementDescription; +import org.eclipse.xtext.xbase.scoping.batch.StaticFeatureDescription; import org.eclipse.xtext.xbase.scoping.featurecalls.OperatorMapping; import org.eclipse.xtext.xbase.typesystem.IBatchTypeResolver; import org.eclipse.xtext.xbase.typesystem.IExpressionScope; import org.eclipse.xtext.xbase.typesystem.IResolvedTypes; import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference; import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReferenceFactory; +import org.eclipse.xtext.xbase.ui.contentassist.ImportingTypesProposalProvider.FQNImporter; +import org.eclipse.xtext.xbase.ui.imports.ReplaceConverter; import org.eclipse.xtext.xtype.XtypePackage; import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.inject.Inject; @@ -128,6 +141,12 @@ public boolean apply(IEObjectDescription input) { @Inject private SyntaxFilteredScopes syntaxFilteredScopes; + @Inject + private TypeReferences typeReferences; + + @Inject + IQualifiedNameConverter qualifiedNameConverter; + protected class XbaseProposalCreator extends DefaultProposalCreator { private ContentAssistContext contentAssistContext; @@ -766,12 +785,86 @@ protected void createLocalVariableAndImplicitProposals( // TODO use the type name information proposeDeclaringTypeForStaticInvocation(context, null /* ignore */, contentAssistContext, acceptor); // System.out.printf("XbaseProposalProvider.proposeDeclaringTypeForStaticInvocation = %d\n", System.currentTimeMillis() - time); + createFavoriteStaticFeature(context, contentAssistContext, acceptor, proposalFactory); + } + + /** + * @since 2.17 + */ + protected void createFavoriteStaticFeature( + EObject context,ContentAssistContext contentAssistContext, + ICompletionProposalAcceptor acceptor, + Function proposalFactory) { + // Favorite proposals coming from JDT - https://github.com/eclipse/xtext-xtend/issues/677 + String pref= PreferenceConstants.getPreference(PreferenceConstants.CODEASSIST_FAVORITE_STATIC_MEMBERS, null); + String[] favourites= pref.split(";"); //$NON-NLS-1$ + for(String fav : favourites) { + String typeName = fav.substring(0, fav.lastIndexOf(".")); + JvmType type = typeReferences.findDeclaredType(typeName, context); + if(type != null) { + if(type instanceof JvmDeclaredType) { + JvmDeclaredType genericType = (JvmDeclaredType) type; + // All features but no Constructor + Iterable featuresToImport = Iterables.filter(Iterables.filter(genericType.getMembers(), JvmFeature.class), new Predicate() { + @Override + public boolean apply(JvmFeature input) { + return !(input instanceof JvmConstructor); + } + }); + if(context != null) { + // Make sure that already imported static features are not proposed + RewritableImportSection importSection = importSectionFactory.parse((XtextResource) context.eResource()); + featuresToImport = Iterables.filter(featuresToImport, new Predicate() { + @Override + public boolean apply(JvmFeature input) { + return !importSection.hasStaticImport(input.getSimpleName(), false); + } + }); + } + // Create StaticFeatureDescription instead of SimpleIdentifiableElementDescription since we want the Proposal to show parameters + Iterable scopedFeatures = Iterables.transform(featuresToImport, new Function() { + @Override + public IEObjectDescription apply(JvmFeature feature) { + return new StaticFeatureDescription(qualifiedNameConverter.toQualifiedName(feature.getSimpleName()), feature, 0, true); + } + }); + // Scope for all static features + IScope staticMemberScope = new SimpleScope(IScope.NULLSCOPE, scopedFeatures); + IReplacementTextApplier textApplier = new FQNImporter(contentAssistContext.getResource(), contentAssistContext.getViewer(), staticMemberScope, qualifiedNameConverter, + qualifiedNameValueConverter, importSectionFactory, replaceConverter); + Function importAddingProposalFactory = new Function() { + @Override + public ICompletionProposal apply(IEObjectDescription input) { + ICompletionProposal proposal = proposalFactory.apply(input); + if(proposal instanceof ConfigurableCompletionProposal) { + ConfigurableCompletionProposal castedProposal = (ConfigurableCompletionProposal) proposal; + // Add textApplier to introduce imports if necessary + ((ConfigurableCompletionProposal) proposal).setTextApplier(textApplier); + return castedProposal; + } + return proposal; + } + }; + getCrossReferenceProposalCreator().lookupCrossReference(staticMemberScope, context, XbasePackage.Literals.XABSTRACT_FEATURE_CALL__FEATURE, acceptor,getFeatureDescriptionPredicate(contentAssistContext),importAddingProposalFactory); + } + } + } } + @Inject + private RewritableImportSection.Factory importSectionFactory; + + @Inject + private ReplaceConverter replaceConverter; + protected String getFeatureCallRuleName() { return "IdOrSuper"; } + protected String getQualifiedNameRuleName() { + return "QualifiedName"; + } + /** * Create proposal for {@link XAbstractFeatureCall#getFeature() simple feature calls} that use an IdOrSuper * as concrete syntax. From 78e5918204eccf4a8aefc9d854d78be6a02e7e6c Mon Sep 17 00:00:00 2001 From: Holger Schill Date: Wed, 16 Jan 2019 14:36:49 +0100 Subject: [PATCH 2/3] [#677] Add missing @since --- .../editor/contentassist/ConfigurableCompletionProposal.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/contentassist/ConfigurableCompletionProposal.java b/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/contentassist/ConfigurableCompletionProposal.java index be9e5f7318..bb14b750f5 100644 --- a/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/contentassist/ConfigurableCompletionProposal.java +++ b/org.eclipse.xtext.ui/src/org/eclipse/xtext/ui/editor/contentassist/ConfigurableCompletionProposal.java @@ -347,6 +347,9 @@ public void setSelectionStart(int selectionStart) { this.selectionStart = selectionStart; } + /** + * @since 2.17 + */ public void shiftOffset(int deltaLength) { this.cursorPosition = this.cursorPosition + deltaLength; this.selectionStart = this.selectionStart + deltaLength; From 72452238b03cfd4a41331a37ae0b93781662b4c8 Mon Sep 17 00:00:00 2001 From: Holger Schill Date: Wed, 16 Jan 2019 15:05:13 +0100 Subject: [PATCH 3/3] [#677] add review comments --- .../xtext/xbase/ui/contentassist/XbaseProposalProvider.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/XbaseProposalProvider.java b/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/XbaseProposalProvider.java index 4ead7b9925..26c6936143 100644 --- a/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/XbaseProposalProvider.java +++ b/org.eclipse.xtext.xbase.ui/src/org/eclipse/xtext/xbase/ui/contentassist/XbaseProposalProvider.java @@ -145,7 +145,7 @@ public boolean apply(IEObjectDescription input) { private TypeReferences typeReferences; @Inject - IQualifiedNameConverter qualifiedNameConverter; + private IQualifiedNameConverter qualifiedNameConverter; protected class XbaseProposalCreator extends DefaultProposalCreator { @@ -825,7 +825,7 @@ public boolean apply(JvmFeature input) { Iterable scopedFeatures = Iterables.transform(featuresToImport, new Function() { @Override public IEObjectDescription apply(JvmFeature feature) { - return new StaticFeatureDescription(qualifiedNameConverter.toQualifiedName(feature.getSimpleName()), feature, 0, true); + return new StaticFeatureDescription(QualifiedName.create(feature.getSimpleName()), feature, 0, true); } }); // Scope for all static features