| /* |
| * generated by Xtext |
| */ |
| package org.eclipse.osbp.xtext.oxtype.ui.contentassist |
| |
| import com.google.common.base.Function |
| import com.google.common.base.Predicate |
| import com.google.inject.Inject |
| import java.util.List |
| import org.apache.log4j.Logger |
| import org.eclipse.core.runtime.Assert |
| import org.eclipse.emf.ecore.EObject |
| import org.eclipse.emf.ecore.EReference |
| import org.eclipse.emf.ecore.resource.Resource |
| import org.eclipse.emf.ecore.util.EcoreUtil |
| import org.eclipse.jface.text.BadLocationException |
| import org.eclipse.jface.text.DocumentRewriteSession |
| import org.eclipse.jface.text.DocumentRewriteSessionType |
| import org.eclipse.jface.text.IDocument |
| import org.eclipse.jface.text.IDocumentExtension4 |
| import org.eclipse.jface.text.ITextViewer |
| import org.eclipse.jface.text.ITextViewerExtension |
| import org.eclipse.jface.text.contentassist.ICompletionProposal |
| import org.eclipse.osbp.xtext.oxtype.imports.IShouldImportProvider |
| import org.eclipse.swt.custom.StyledText |
| import org.eclipse.text.edits.MultiTextEdit |
| import org.eclipse.text.edits.ReplaceEdit |
| import org.eclipse.text.edits.TextEdit |
| import org.eclipse.xtend.lib.annotations.Data |
| import org.eclipse.xtext.CrossReference |
| import org.eclipse.xtext.EcoreUtil2 |
| import org.eclipse.xtext.GrammarUtil |
| import org.eclipse.xtext.ParserRule |
| import org.eclipse.xtext.RuleCall |
| import org.eclipse.xtext.common.types.JvmDeclaredType |
| import org.eclipse.xtext.common.types.TypesPackage |
| import org.eclipse.xtext.conversion.IValueConverter |
| import org.eclipse.xtext.naming.IQualifiedNameConverter |
| import org.eclipse.xtext.naming.QualifiedName |
| import org.eclipse.xtext.resource.IEObjectDescription |
| import org.eclipse.xtext.resource.XtextResource |
| import org.eclipse.xtext.resource.impl.AliasedEObjectDescription |
| import org.eclipse.xtext.scoping.IScope |
| 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.ReplacementTextApplier |
| import org.eclipse.xtext.util.ReplaceRegion |
| 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.ui.contentassist.XbaseReferenceProposalCreator |
| import org.eclipse.xtext.xbase.ui.imports.ReplaceConverter |
| import org.eclipse.xtext.xtype.XImportSection |
| |
| import static org.eclipse.xtext.util.Strings.notNull |
| import org.eclipse.osbp.xtext.oxtype.imports.OXTypeRewritableImportSection |
| |
| @SuppressWarnings("restriction") |
| class OXtypeProposalProvider extends AbstractOXtypeProposalProvider { |
| |
| @Inject IShouldImportProvider importProvider |
| |
| override void lookupCrossReference(CrossReference crossReference, EReference reference, |
| ContentAssistContext contentAssistContext, ICompletionProposalAcceptor acceptor, |
| Predicate<IEObjectDescription> filter) { |
| var String ruleName = null; |
| if (crossReference.getTerminal() instanceof RuleCall) { |
| ruleName = (crossReference.getTerminal() as RuleCall).getRule().getName(); |
| } |
| |
| if (importProvider.shouldAutoImport(contentAssistContext.getCurrentModel(), reference)) { |
| // if the type should be imported, pass a custom acceptor to pass additional information to the wrapped proposal factory |
| val customAcceptor = new ImportAwareCompletionProposalAcceptor(acceptor, contentAssistContext); |
| lookupCrossReference(contentAssistContext.getCurrentModel(), reference, customAcceptor, filter, |
| getProposalFactory(ruleName, contentAssistContext)); |
| } else { |
| lookupCrossReference(contentAssistContext.getCurrentModel(), reference, acceptor, filter, |
| getProposalFactory(ruleName, contentAssistContext)); |
| } |
| } |
| |
| override void lookupCrossReference(CrossReference crossReference, ContentAssistContext contentAssistContext, |
| ICompletionProposalAcceptor acceptor, Predicate<IEObjectDescription> filter, |
| Function<IEObjectDescription, ICompletionProposal> proposalFactory) { |
| val ParserRule containingParserRule = GrammarUtil.containingParserRule(crossReference); |
| if (!GrammarUtil.isDatatypeRule(containingParserRule)) { |
| val EReference ref = GrammarUtil.getReference(crossReference); |
| if (importProvider.shouldAutoImport(contentAssistContext.getCurrentModel(), ref)) { |
| // if the type should be imported, pass a custom acceptor to pass additional information to the wrapped proposal factory |
| val customAcceptor = new ImportAwareCompletionProposalAcceptor(acceptor, contentAssistContext); |
| lookupCrossReference(contentAssistContext.getCurrentModel(), ref, customAcceptor, filter, |
| proposalFactory); |
| } else { |
| lookupCrossReference(contentAssistContext.getCurrentModel(), ref, acceptor, filter, |
| proposalFactory); |
| } |
| } |
| } |
| |
| /** |
| * Is responsible to create the completion proposals |
| */ |
| static class CustomReferenceProposalCreator extends XbaseReferenceProposalCreator { |
| |
| @Inject IShouldImportProvider importProvider |
| @Inject IQualifiedNameConverter nameConverter |
| |
| @Inject |
| private RewritableImportSection.Factory importSectionFactory; |
| |
| @Inject |
| private ReplaceConverter replaceConverter; |
| |
| @Inject |
| XbaseQualifiedNameValueConverter valueConverter; |
| |
| override void lookupCrossReference(IScope scope, EObject model, EReference reference, |
| ICompletionProposalAcceptor acceptor, Predicate<IEObjectDescription> filter, |
| Function<IEObjectDescription, ICompletionProposal> proposalFactory) { |
| if (TypesPackage.Literals.JVM_TYPE.isSuperTypeOf(getEReferenceType(model, reference)) || |
| reference === XbasePackage.Literals.XABSTRACT_FEATURE_CALL__FEATURE) { |
| super.lookupCrossReference(scope, model, reference, acceptor, filter, proposalFactory) |
| return |
| } |
| |
| val Function<IEObjectDescription, ICompletionProposal> wrappedFactory = getWrappedFactory(scope, model, |
| reference, proposalFactory, acceptor); |
| if (importProvider.shouldProposeAllElements(model, reference)) { |
| for (IEObjectDescription candidate : scope.allElements) { |
| if (!acceptor.canAcceptMoreProposals()) |
| return; |
| |
| var transformed = candidate |
| val name = candidate.name; |
| if (name.segmentCount > 1) { |
| transformed = new AliasedEObjectDescription(QualifiedName.create(name.lastSegment), |
| candidate) |
| } |
| |
| if (filter.apply(transformed)) { |
| acceptor.accept(wrappedFactory.apply(transformed)); |
| } |
| } |
| } else { |
| val Iterable<IEObjectDescription> candidates = queryScope(scope, model, reference, filter); |
| for (IEObjectDescription candidate : candidates) { |
| if (!acceptor.canAcceptMoreProposals()) |
| return; |
| if (filter.apply(candidate)) { |
| acceptor.accept(wrappedFactory.apply(candidate)); |
| } |
| } |
| } |
| |
| } |
| |
| def Function<IEObjectDescription, ICompletionProposal> getWrappedFactory(IScope scope, EObject model, |
| EReference reference, Function<IEObjectDescription, ICompletionProposal> proposalFactory, |
| ICompletionProposalAcceptor acceptor) { |
| if (!TypesPackage.Literals.JVM_TYPE.isSuperTypeOf(getEReferenceType(model, reference)) && |
| acceptor instanceof ImportAwareCompletionProposalAcceptor) { |
| |
| // create the default factory |
| val factory = super.getWrappedFactory(model, reference, proposalFactory) |
| |
| // wrap the factory and initialize the text applier for import stuff |
| val customAcceptor = acceptor as ImportAwareCompletionProposalAcceptor |
| val complContext = customAcceptor.context |
| return new Function<IEObjectDescription, ICompletionProposal>() { |
| override ICompletionProposal apply(IEObjectDescription from) { |
| val ICompletionProposal result = factory.apply(from); |
| if (result instanceof ConfigurableCompletionProposal) { |
| result.textApplier = createTextApplier(complContext, scope, nameConverter, |
| valueConverter) |
| result.setAdditionalData(IEObjectDescription.name, from) |
| } |
| return result; |
| } |
| }; |
| } else { |
| return super.getWrappedFactory(model, reference, proposalFactory); |
| } |
| } |
| |
| def IReplacementTextApplier createTextApplier(ContentAssistContext context, IScope typeScope, |
| IQualifiedNameConverter qualifiedNameConverter, IValueConverter<String> valueConverter) { |
| if (EcoreUtil2.getContainerOfType(context.getCurrentModel(), XImportSection) !== null) |
| return null; |
| return new FQNImporter(context.getResource(), context.getViewer(), typeScope, qualifiedNameConverter, |
| valueConverter, importSectionFactory, replaceConverter); |
| } |
| } |
| |
| static class FQNImporter extends ReplacementTextApplier { |
| |
| private static final Logger LOG = Logger.getLogger(FQNImporter); |
| private final ITextViewer viewer; |
| |
| RewritableImportSection.Factory importSectionFactory; |
| |
| ReplaceConverter replaceConverter; |
| |
| IValueConverter<String> valueConverter |
| |
| IQualifiedNameConverter qualifiedNameConverter |
| |
| IScope scope |
| |
| Resource context |
| |
| new(Resource context, ITextViewer viewer, IScope scope, IQualifiedNameConverter qualifiedNameConverter, |
| IValueConverter<String> valueConverter, RewritableImportSection.Factory importSectionFactory, |
| ReplaceConverter replaceConverter) { |
| this.viewer = viewer; |
| this.context = context |
| this.qualifiedNameConverter = qualifiedNameConverter; |
| this.scope = scope; |
| this.qualifiedNameConverter = qualifiedNameConverter; |
| this.valueConverter = valueConverter |
| this.importSectionFactory = importSectionFactory; |
| this.replaceConverter = replaceConverter; |
| } |
| |
| override void apply(IDocument document, |
| ConfigurableCompletionProposal proposal) throws BadLocationException { |
| |
| val IEObjectDescription desc = proposal.getAdditionalData(IEObjectDescription.name) as IEObjectDescription |
| |
| val toImportQualifiedName = desc.qualifiedName |
| val String proposalReplacementString = proposal.getReplacementString(); |
| var String typeName = proposalReplacementString; |
| if (valueConverter !== null) |
| typeName = valueConverter.toValue(toImportQualifiedName.toString, null); |
| val String replacementString = getActualReplacementString(proposal); |
| // there is an import statement - apply computed replacementString |
| if (!proposalReplacementString.equals(replacementString)) { |
| var String shortTypeName = replacementString; |
| if (valueConverter !== null) |
| shortTypeName = valueConverter.toValue(replacementString, null); |
| val QualifiedName shortQualifiedName = qualifiedNameConverter.toQualifiedName(shortTypeName); |
| if (shortQualifiedName.getSegmentCount() === 1) { |
| proposal.setCursorPosition(replacementString.length()); |
| document.replace(proposal.getReplacementOffset(), proposal.getReplacementLength(), |
| replacementString); |
| return; |
| } |
| } |
| |
| // we could create an import statement if there is no conflict |
| val 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; |
| } |
| val IEObjectDescription description = scope.getSingleElement( |
| qualifiedName.skipFirst(qualifiedName.getSegmentCount() - 1)); |
| if (description !== null) { |
| // already imported |
| proposal.setCursorPosition(proposalReplacementString.length()); |
| document.replace(proposal.getReplacementOffset(), proposal.getReplacementLength(), |
| proposalReplacementString); |
| return; |
| } |
| |
| // Import does not introduce ambiguities - add import and insert short name |
| val String shortName = qualifiedName.getLastSegment(); |
| var int topPixel = -1; |
| // store the pixel coordinates to prevent the ui from flickering |
| val StyledText widget = viewer.getTextWidget(); |
| if (widget !== null) |
| topPixel = widget.getTopPixel(); |
| var ITextViewerExtension viewerExtension = null; |
| if (viewer instanceof ITextViewerExtension) { |
| viewerExtension = viewer as ITextViewerExtension; |
| viewerExtension.setRedraw(false); |
| } |
| |
| val OXTypeRewritableImportSection importSection = importSectionFactory.parse(context as XtextResource) as OXTypeRewritableImportSection; |
| val IEObjectDescription typeToImport = scope.getSingleElement(qualifiedName); |
| if (typeToImport === null) { |
| LOG.error("Could not find unique type named '" + notNull(qualifiedName) + "' in scope"); |
| if (viewerExtension !== null) |
| viewerExtension.setRedraw(true); |
| return; |
| } |
| val EObject resolved = EcoreUtil.resolve(typeToImport.getEObjectOrProxy(), context); |
| Assert.isTrue(!resolved.eIsProxy()); |
| importSection.addFQNImport(typeName); |
| |
| var DocumentRewriteSession rewriteSession = null; |
| try { |
| if (document instanceof IDocumentExtension4) { |
| rewriteSession = (document as IDocumentExtension4).startRewriteSession( |
| DocumentRewriteSessionType.UNRESTRICTED_SMALL); |
| } |
| // apply short proposal |
| var String escapedShortname = shortName; |
| if (valueConverter !== null) { |
| escapedShortname = valueConverter.toString(shortName); |
| } |
| proposal.setCursorPosition(escapedShortname.length()); |
| val int initialCursorLine = document.getLineOfOffset(proposal.getReplacementOffset()); |
| val ReplaceEdit replaceEdit = new ReplaceEdit(proposal.getReplacementOffset(), |
| proposal.getReplacementLength(), escapedShortname); |
| |
| // add import statement |
| val List<ReplaceRegion> importChanges = importSection.rewrite(); |
| var TextEdit textEdit = replaceConverter.convertToTextEdit(importChanges); |
| if (textEdit !== null) { |
| val MultiTextEdit compound = new MultiTextEdit(); |
| compound.addChild(textEdit); |
| compound.addChild(replaceEdit); |
| textEdit = compound; |
| } else { |
| textEdit = replaceEdit; |
| } |
| textEdit.apply(document); |
| |
| val int cursorPosition = proposal.getCursorPosition() + |
| replaceConverter.getReplaceLengthDelta(importChanges, proposal.getReplacementOffset()); |
| proposal.setCursorPosition(cursorPosition); |
| val int newCursorLine = document.getLineOfOffset(cursorPosition); |
| |
| // set the pixel coordinates |
| if (widget !== null) { |
| val int additionalTopPixel = (newCursorLine - initialCursorLine) * widget.getLineHeight(); |
| widget.setTopPixel(topPixel + additionalTopPixel); |
| } |
| } finally { |
| if (rewriteSession !== null) { |
| (document as IDocumentExtension4).stopRewriteSession(rewriteSession); |
| } |
| if (viewerExtension !== null) |
| viewerExtension.setRedraw(true); |
| } |
| } |
| |
| override getActualReplacementString(ConfigurableCompletionProposal proposal) { |
| |
| var String replacementString = proposal.getReplacementString(); |
| if (scope !== null) { |
| var String qualifiedNameAsString = replacementString; |
| if (valueConverter !== null) { |
| qualifiedNameAsString = valueConverter.toValue(qualifiedNameAsString, null); |
| } |
| val IEObjectDescription element = scope.getSingleElement( |
| qualifiedNameConverter.toQualifiedName(qualifiedNameAsString)); |
| if (element !== null) { |
| val EObject resolved = EcoreUtil.resolve(element.getEObjectOrProxy(), context); |
| if (!resolved.eIsProxy()) { |
| val IEObjectDescription shortendElement = scope.getSingleElement(resolved); |
| if (shortendElement !== null) |
| replacementString = applyValueConverter(shortendElement.getName()); |
| } |
| } |
| } |
| return replacementString; |
| |
| } |
| |
| def protected String applyValueConverter(QualifiedName qualifiedName) { |
| var String result = qualifiedNameConverter.toString(qualifiedName); |
| if(valueConverter !== null) result = valueConverter.toString(result); |
| return result; |
| } |
| } |
| |
| @Data |
| static class ImportAwareCompletionProposalAcceptor extends ICompletionProposalAcceptor.Delegate { |
| final ContentAssistContext context |
| |
| new(ICompletionProposalAcceptor delegate, ContentAssistContext context) { |
| super(delegate) |
| this.context = context |
| } |
| |
| } |
| } |
| |