blob: ac21923439cad6db84700fcb08ef7fb419a94591 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2012 Obeo.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.acceleo.internal.ide.ui.editors.template;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.acceleo.common.IAcceleoConstants;
import org.eclipse.acceleo.common.internal.utils.AcceleoPackageRegistry;
import org.eclipse.acceleo.common.internal.utils.LazyEPackageDescriptor;
import org.eclipse.acceleo.common.internal.utils.compatibility.AcceleoCompatibilityEclipseHelper;
import org.eclipse.acceleo.common.internal.utils.compatibility.AcceleoOCLReflection;
import org.eclipse.acceleo.common.internal.utils.compatibility.OCLVersion;
import org.eclipse.acceleo.common.utils.CompactHashSet;
import org.eclipse.acceleo.common.utils.CompactLinkedHashSet;
import org.eclipse.acceleo.common.utils.ModelUtils;
import org.eclipse.acceleo.ide.ui.AcceleoUIActivator;
import org.eclipse.acceleo.internal.ide.ui.editors.template.scanner.AcceleoPartitionScanner;
import org.eclipse.acceleo.internal.ide.ui.editors.template.utils.AcceleoUIDocumentationUtils;
import org.eclipse.acceleo.internal.ide.ui.editors.template.utils.IAcceleoContantsImage;
import org.eclipse.acceleo.internal.ide.ui.views.overrides.OverridesBrowser;
import org.eclipse.acceleo.internal.ide.ui.views.proposals.ProposalsBrowser;
import org.eclipse.acceleo.internal.parser.ast.ocl.environment.AcceleoEnvironment;
import org.eclipse.acceleo.internal.parser.cst.utils.Sequence;
import org.eclipse.acceleo.internal.parser.cst.utils.SequenceBlock;
import org.eclipse.acceleo.parser.cst.Block;
import org.eclipse.acceleo.parser.cst.CSTNode;
import org.eclipse.acceleo.parser.cst.CstPackage;
import org.eclipse.acceleo.parser.cst.IfBlock;
import org.eclipse.acceleo.parser.cst.InitSection;
import org.eclipse.acceleo.parser.cst.LetBlock;
import org.eclipse.acceleo.parser.cst.ModelExpression;
import org.eclipse.acceleo.parser.cst.Module;
import org.eclipse.acceleo.parser.cst.ModuleElement;
import org.eclipse.acceleo.parser.cst.ModuleExtendsValue;
import org.eclipse.acceleo.parser.cst.ModuleImportsValue;
import org.eclipse.acceleo.parser.cst.Query;
import org.eclipse.acceleo.parser.cst.Template;
import org.eclipse.acceleo.parser.cst.TemplateOverridesValue;
import org.eclipse.acceleo.parser.cst.TextExpression;
import org.eclipse.acceleo.parser.cst.TypedModel;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jface.text.templates.DocumentTemplateContext;
import org.eclipse.jface.text.templates.TemplateContext;
import org.eclipse.jface.text.templates.TemplateContextType;
import org.eclipse.ocl.expressions.CollectionKind;
import org.eclipse.ocl.helper.Choice;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
/**
* A completion processor for the Acceleo template editor.
*
* @author <a href="mailto:jonathan.musset@obeo.fr">Jonathan Musset</a>
*/
public class AcceleoCompletionProcessor implements IContentAssistProcessor {
/** The auto activation characters for completion proposal. */
private static final char[] AUTO_ACTIVATION_CHARACTERS;
/**
* The current text viewer.
*/
protected ITextViewer textViewer;
/**
* The source content.
*/
protected AcceleoSourceContent content;
/**
* The text used to compute the proposals.
*/
protected String text;
/**
* The default start for the completion proposal.
*/
private final String defaultStart = " ${"; //$NON-NLS-1$
/**
* An offset within the text for which completions should be computed.
*/
private int offset;
/**
* The CST node at the current offset.
*/
private CSTNode cstNode;
/**
* The default type of the variables.
*/
private String defaultVariableType;
static {
/*
* Assume the version of Eclipse is tied to the associated OCL Version. If this assumption is false,
* the user built his own version of OCL and he'll have to cope with content assist problems. We have
* to have the "space" char in the auto activation triggers as explicit calls to "ctrl+space" wouldn't
* trigger the completion popup otherwise before Galileo (3.5). In general though, having the content
* assistant triggered on whitespaces is useless and, in most cases, stupid. We then only activate
* this under Ganymede when it is mandatory.
*/
if (AcceleoCompatibilityEclipseHelper.getCurrentOCLVersion() == OCLVersion.GANYMEDE) {
AUTO_ACTIVATION_CHARACTERS = new char[] {' ', '.', '[', '-', '>', ':' };
} else {
AUTO_ACTIVATION_CHARACTERS = new char[] {'.', '[', '-', '>', ':' };
}
}
/**
* Constructor.
*
* @param content
* is the content
*/
public AcceleoCompletionProcessor(AcceleoSourceContent content) {
super();
this.content = content;
this.defaultVariableType = "Type"; //$NON-NLS-1$
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer,
* int)
*/
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int pos) {
textViewer = viewer;
setText(viewer);
if (viewer != null) {
ITextSelection selection = (ITextSelection)viewer.getSelectionProvider().getSelection();
if (selection != null && selection.getOffset() == pos) {
setCompletionOffset(selection.getOffset() + selection.getLength());
} else {
setCompletionOffset(pos);
}
} else {
setCompletionOffset(pos);
}
cstNode = content.getCSTNode(offset, offset);
if (cstNode instanceof InitSection && offset == cstNode.getStartPosition()
&& cstNode.eContainer() != null) {
// We change the CSTNode if we are on the first index of an InitSection
cstNode = (CSTNode)cstNode.eContainer();
}
try {
return computeCompletionProposals();
} finally {
textViewer = null;
text = null;
setCompletionOffset(0);
cstNode = null;
}
}
/**
* Returns a list of completion proposals based on the specified location within the text.
*
* @return an array of completion proposals or null if no proposals are possible
*/
private ICompletionProposal[] computeCompletionProposals() {
List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
if (textViewer != null && cstNode instanceof TextExpression) {
ITextSelection selection = (ITextSelection)textViewer.getSelectionProvider().getSelection();
if (selection != null && selection.getLength() > 0) {
Template template = (Template)content.getCSTParent(cstNode, Template.class);
computeSelectionReplacementProposals(proposals, template, selection.getOffset(), selection
.getOffset()
+ selection.getLength());
}
}
if (cstNode == null
|| (cstNode instanceof Module && cstNode.getEndPosition() == 0 && cstNode.getStartPosition() == 0)) {
computeModulePatternProposals(proposals);
} else if (cstNode instanceof TypedModel) {
computeEPackageProposals(proposals);
} else if (cstNode instanceof ModuleImportsValue || cstNode instanceof ModuleExtendsValue) {
computeImportProposals(proposals);
} else {
if (offset > 0 && text.charAt(offset - 1) != '/') {
computeEClassifierProposals(proposals);
boolean addPatterns = false;
if (proposals.size() == 0) {
computeKeywordsProposals(proposals);
if (!isNextSignificantChar(']')) {
addPatterns = true;
}
}
if (text.charAt(offset - 1) != ']') {
if (cstNode instanceof ModelExpression || cstNode instanceof InitSection) {
computeOCLProposals(proposals);
} else if (cstNode instanceof Template
&& text.substring(cstNode.getStartPosition(), offset).contains("]")) { //$NON-NLS-1$
computeOCLProposals(proposals);
} else if (cstNode instanceof Block && !(cstNode instanceof Template)) {
computeOCLProposals(proposals);
} else if (cstNode instanceof Query && cstNode.getStartPosition() > -1
&& offset > cstNode.getStartPosition()
&& isHeaderAfterParenthesis(text.substring(cstNode.getStartPosition(), offset))) {
computeOCLProposals(proposals);
} else if (cstNode instanceof TemplateOverridesValue) {
computeTemplatesProposals(proposals);
}
}
if (addPatterns) {
computePatternsProposals(proposals);
}
}
}
computeProposalsBrowserView(proposals);
if (cstNode instanceof Module) {
computeOverridesBrowserView(proposals);
}
if (proposals.size() == 0 && textViewer != null && textViewer.getTextWidget() != null) {
if (offset < text.length() && text.charAt(offset) == ']' && text.charAt(offset - 1) == '[') {
textViewer.getTextWidget().replaceTextRange(offset, 0,
IAcceleoConstants.DEFAULT_END_BODY_CHAR);
}
}
return proposals.toArray(new ICompletionProposal[proposals.size()]);
}
/**
* This method is only called if the current text selection isn't empty. It creates a new completion
* proposal that contains the ending text of the current template, it means from the current offset to the
* ending index of the template. The text is modified by replacing everywhere the current selected text.
* An OCL expression can be created for each occurrence.
*
* @param proposals
* are the proposals, it's an input/output parameter
* @param template
* is the current template, to delimit the modifications
* @param begin
* is the beginning index of the selection
* @param end
* is the ending index of the selection
*/
private void computeSelectionReplacementProposals(List<ICompletionProposal> proposals, Template template,
int begin, int end) {
if (template != null && template.getEndPosition() > -1 && template.getEndPosition() <= text.length()) {
String stringToReplace = text.substring(begin, end);
String stringToReplaceLower = stringToReplace.toLowerCase();
String endingTextS = text.substring(begin, template.getEndPosition());
StringBuffer endingText = new StringBuffer(endingTextS);
StringBuffer endingTextLower = new StringBuffer(endingTextS.toLowerCase());
int count = 0;
int i = endingTextLower.indexOf(stringToReplaceLower);
int shift = 0;
while (i > -1 && i < endingText.length()) {
int b = i;
int e = b + stringToReplace.length();
CSTNode newNode = content.getCSTNode(begin + b - shift, begin + b - shift);
if (newNode instanceof TextExpression) {
count++;
String stringFound = endingText.substring(b, e);
String replacementString;
if (stringFound.length() > 1 && stringFound.equals(stringFound.toUpperCase())) {
replacementString = "[${name}.toUpper()/]"; //$NON-NLS-1$
} else if (Character.isUpperCase(stringFound.charAt(0))) {
replacementString = "[${name}.toUpperFirst()/]"; //$NON-NLS-1$
} else {
replacementString = "[${name}/]"; //$NON-NLS-1$
}
endingText.replace(b, e, replacementString);
endingTextLower.replace(b, e, replacementString);
shift += replacementString.length() - (e - b);
i = endingTextLower.indexOf(stringToReplaceLower, b + replacementString.length());
} else if (newNode == null) {
i = -1;
} else {
i = endingTextLower.indexOf(stringToReplaceLower, e);
}
}
Image image = AcceleoUIActivator.getDefault().getImage(
"icons/template-editor/completion/RefactorTextSelection.gif"); //$NON-NLS-1$
String label;
if (count > 1) {
label = "Replacing by [ ] - " + count + " times"; //$NON-NLS-1$ //$NON-NLS-2$
} else {
label = "Replacing by [ ]"; //$NON-NLS-1$
}
int last = endingText.length() - 1;
for (i = last; i >= 0; i--) {
char c = endingText.charAt(i);
if (c == '$' && (i == last || endingText.charAt(i + 1) != '{')) {
endingText.insert(i, '$');
}
}
proposals.add(createTemplateProposal(endingText.toString(), begin, template.getEndPosition()
- begin, begin, image, label, null, stringToReplace + " -> [name/]")); //$NON-NLS-1$
}
}
/**
* Computes the proposals specified in the ProposalsBrowser view.
*
* @param proposals
* are the completion proposals (in out parameter)
*/
private void computeProposalsBrowserView(List<ICompletionProposal> proposals) {
IWorkbenchWindow activeWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (activeWindow == null || activeWindow.getActivePage() == null) {
return;
}
IWorkbenchPage page = activeWindow.getActivePage();
IViewReference[] references = page.getViewReferences();
for (int i = 0; i < references.length; i++) {
IViewReference viewReference = references[i];
IViewPart view = viewReference.getView(false);
if (view instanceof ProposalsBrowser && page.isPartVisible(view) && textViewer != null) {
List<ICompletionProposal> advancedCompletionProposals = ((ProposalsBrowser)view)
.getPatternCompletionProposals(textViewer.getDocument(), text, offset, cstNode);
if (advancedCompletionProposals.size() > 0) {
proposals.addAll(0, advancedCompletionProposals);
}
}
}
}
/**
* Computes the 'overrides' proposals specified in the OverridesBrowser view.
*
* @param proposals
* are the completion proposals (in out parameter)
*/
private void computeOverridesBrowserView(List<ICompletionProposal> proposals) {
IWorkbenchWindow activeWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (activeWindow == null || activeWindow.getActivePage() == null) {
return;
}
IWorkbenchPage page = activeWindow.getActivePage();
IViewReference[] references = page.getViewReferences();
for (int i = 0; i < references.length; i++) {
IViewReference viewReference = references[i];
IViewPart view = viewReference.getView(false);
if (view instanceof OverridesBrowser && page.isPartVisible(view) && textViewer != null) {
List<ICompletionProposal> advancedCompletionProposals = ((OverridesBrowser)view)
.getExtendCompletionProposals(textViewer.getDocument(), text, offset);
if (advancedCompletionProposals.size() > 0) {
proposals.addAll(0, advancedCompletionProposals);
}
}
}
}
/**
* Indicates if the next significant character matches with the given one.
*
* @param ref
* is the character to match
* @return true if 'ref' is the next significant character
*/
private boolean isNextSignificantChar(char ref) {
if (offset < text.length()) {
int i = offset;
char c;
do {
c = text.charAt(i);
i++;
} while (Character.isWhitespace(c) && i < text.length());
return c == ref;
}
return false;
}
/**
* Computes a list of templates completion proposals based on the specified location within the text.
*
* @param proposals
* are the completion proposals (in out parameter)
*/
private void computeTemplatesProposals(List<ICompletionProposal> proposals) {
int i = offset;
while (i > 0 && Character.isJavaIdentifierPart(text.charAt(i - 1))) {
i--;
}
String start = text.substring(i, offset);
int startPosition = cstNode.getStartPosition();
if (startPosition < 0) {
startPosition = 0;
}
if (startPosition > offset) {
startPosition = offset;
}
String textOCL = text.substring(startPosition, offset);
Collection<Choice> choices = content.getSyntaxHelp(textOCL, offset);
Set<String> duplicated = new CompactHashSet<String>();
for (Choice next : choices) {
String replacementString = next.getName();
if (replacementString.toLowerCase().startsWith(start.toLowerCase())) {
switch (next.getKind()) {
case OPERATION:
if (next.getElement() instanceof EOperation) {
Image image = AcceleoUIActivator.getDefault().getImage(
IAcceleoContantsImage.TemplateEditor.Completion.TEMPLATE_PUBLIC);
EOperation eOperation = (EOperation)next.getElement();
String description;
if (eOperation.getEContainingClass() != null) {
description = eOperation.getEContainingClass().getName() + "." //$NON-NLS-1$
+ next.getDescription();
} else {
description = next.getDescription();
}
if (!duplicated.contains(next.getDescription())) {
duplicated.add(next.getDescription());
proposals.add(createCompletionProposal(replacementString, offset
- start.length(), start.length(), replacementString.length(), image,
next.getDescription(), null, description));
}
}
break;
default:
break;
}
}
}
}
/**
* Computes a list of OCL completion proposals based on the specified location within the text.
*
* @param proposals
* are the completion proposals (in out parameter)
*/
private void computeOCLProposals(List<ICompletionProposal> proposals) {
int i = offset;
while (i > 0 && Character.isJavaIdentifierPart(text.charAt(i - 1))) {
i--;
}
String start = text.substring(i, offset);
int startPosition = cstNode.getStartPosition();
if (startPosition < 0) {
startPosition = 0;
}
if (startPosition > offset) {
startPosition = offset;
}
String textOCL = text.substring(startPosition, offset);
Collection<Choice> choices = content.getSyntaxHelp(textOCL, offset);
Set<String> duplicated = new CompactHashSet<String>();
for (Choice next : choices) {
String choiceValue = next.getName();
String replacement = getReplacementStringFor(choiceValue);
if (choiceValue.toLowerCase().startsWith(start.toLowerCase())
|| replacement.toLowerCase().startsWith(start.toLowerCase())) {
switch (next.getKind()) {
case OPERATION:
addOCLOperationChoice(proposals, next, start, duplicated);
break;
case SIGNAL:
case PROPERTY:
String displayProperty = next.getDescription();
String descriptionProperty;
if (next.getElement() instanceof EStructuralFeature) {
displayProperty = getPropertyDisplay((EStructuralFeature)next.getElement());
StringBuilder descriptionPropertyBuilder = new StringBuilder(displayProperty);
// TODO NLS This should probably be externalized
descriptionPropertyBuilder.append("\n\t defined on "); //$NON-NLS-1$
descriptionPropertyBuilder.append(((EStructuralFeature)next.getElement())
.getEContainingClass().getName());
descriptionProperty = descriptionPropertyBuilder.toString();
} else {
descriptionProperty = displayProperty;
}
if (!duplicated.contains(displayProperty)) {
duplicated.add(displayProperty);
proposals.add(createCompletionProposal(replacement, offset - start.length(),
start.length(), replacement.length(), AcceleoUIActivator.getDefault()
.getImage("icons/template-editor/completion/Property.gif"), //$NON-NLS-1$
displayProperty, null, descriptionProperty));
}
break;
case ENUMERATION_LITERAL:
if (!duplicated.contains(choiceValue)) {
duplicated.add(choiceValue);
proposals.add(createCompletionProposal(replacement, offset - start.length(),
start.length(), replacement.length(), AcceleoUIActivator.getDefault()
.getImage("icons/template-editor/completion/EnumLiteral.gif"), //$NON-NLS-1$
choiceValue, null, next.getDescription()));
}
break;
case VARIABLE:
String displayVariable;
String description;
if (IAcceleoConstants.SELF.equals(choiceValue) || next.getDescription() == null) {
displayVariable = choiceValue;
description = ""; //$NON-NLS-1$
} else {
displayVariable = choiceValue + ":" + next.getDescription(); //$NON-NLS-1$
description = next.getDescription();
}
if (!duplicated.contains(displayVariable)) {
duplicated.add(displayVariable);
proposals.add(createCompletionProposal(replacement, offset - start.length(),
start.length(), replacement.length(), AcceleoUIActivator.getDefault()
.getImage("icons/template-editor/completion/Variable.gif"), //$NON-NLS-1$
displayVariable, null, description));
}
break;
default:
break;
}
}
}
}
/**
* This will be in charge of disambigating conflicts between feature/operations and OCL reserved keywords.
* Specifically, we'll prefix any reserved keyword with an underscore.
*
* @param choiceValue
* String that is to be replaced.
* @return The replacement String that's to be used instead of <em>choiceValue</em> if it is a reserved
* keyword, <em>choiceValue</em> otherwise.
*/
private String getReplacementStringFor(String choiceValue) {
if (content.getOCLEnvironment() instanceof AcceleoEnvironment) {
if (AcceleoOCLReflection.getReservedKeywords().contains(choiceValue)) {
return '_' + choiceValue;
}
}
return choiceValue;
}
/**
* Adds (or not) an OCL operation in the proposals list.
*
* @param proposals
* is the output proposals list
* @param nextOperationChoice
* is the next operation to add (or not) in the proposals list
* @param start
* is the current text used to keep the completion proposals, the name of a valid proposal must
* start with this string
* @param duplicated
* are the descriptions that already appear in the completion list
*/
private void addOCLOperationChoice(List<ICompletionProposal> proposals, Choice nextOperationChoice,
String start, Set<String> duplicated) {
String description = ""; //$NON-NLS-1$
Image image;
if (nextOperationChoice instanceof AcceleoCompletionChoice) {
AcceleoCompletionChoice acceleoCompletionChoice = (AcceleoCompletionChoice)nextOperationChoice;
org.eclipse.acceleo.model.mtl.ModuleElement acceleoElement = acceleoCompletionChoice
.getAcceleoElement();
image = this.computeImage(acceleoElement);
description = this.computeDescription(acceleoElement);
} else {
image = AcceleoUIActivator.getDefault().getImage(
IAcceleoContantsImage.TemplateEditor.Completion.OPERATION);
}
if (nextOperationChoice.getElement() instanceof EOperation) {
EOperation eOperation = (EOperation)nextOperationChoice.getElement();
String replacementStringWithArgsBefore = getReplacementStringFor(eOperation.getName())
+ IAcceleoConstants.PARENTHESIS_BEGIN;
String replacementStringWithArgsAfter = ""; //$NON-NLS-1$
Iterator<EParameter> eParametersIt = eOperation.getEParameters().iterator();
while (eParametersIt.hasNext()) {
EParameter eParameter = eParametersIt.next();
replacementStringWithArgsAfter += "${" + eParameter.getName() + "}"; //$NON-NLS-1$ //$NON-NLS-2$
if (eParametersIt.hasNext()) {
replacementStringWithArgsAfter += ", "; //$NON-NLS-1$
}
}
replacementStringWithArgsAfter += ')';
if (description.length() == 0) {
description = AcceleoUIDocumentationUtils.getTextFrom(eOperation);
}
if (description.length() == 0) {
if (eOperation.getEContainingClass() != null) {
description = "\n" + eOperation.getEContainingClass().getName() + "." //$NON-NLS-1$ //$NON-NLS-2$
+ nextOperationChoice.getDescription();
} else {
description = "\n" + nextOperationChoice.getDescription(); //$NON-NLS-1$
}
}
if (!duplicated.contains(nextOperationChoice.getDescription())) {
duplicated.add(nextOperationChoice.getDescription());
proposals.add(createTemplateProposal(replacementStringWithArgsBefore
+ replacementStringWithArgsAfter, offset - start.length(), start.length(),
replacementStringWithArgsBefore.length(), image,
nextOperationChoice.getDescription(), null, description));
}
} else {
if (!duplicated.contains(nextOperationChoice.getDescription())) {
duplicated.add(nextOperationChoice.getDescription());
String replacementString = nextOperationChoice.getName();
if (replacementString.contains("(")) { //$NON-NLS-1$
replacementString = getReplacementStringFor(replacementString.substring(0,
replacementString.indexOf('(')));
}
proposals.add(createCompletionProposal(replacementString, offset - start.length(), start
.length(), replacementString.length(), image, nextOperationChoice.getDescription(),
null, nextOperationChoice.getDescription() + description));
}
}
}
/**
* Computes the description of the module element.
*
* @param acceleoElement
* The module element
* @return the description of the module element
*/
private String computeDescription(org.eclipse.acceleo.model.mtl.ModuleElement acceleoElement) {
String description = ""; //$NON-NLS-1$
if (acceleoElement instanceof org.eclipse.acceleo.model.mtl.Template) {
org.eclipse.acceleo.model.mtl.Template template = (org.eclipse.acceleo.model.mtl.Template)acceleoElement;
description = AcceleoUIDocumentationUtils.getDocumentation(template);
} else if (acceleoElement instanceof org.eclipse.acceleo.model.mtl.Query) {
org.eclipse.acceleo.model.mtl.Query query = (org.eclipse.acceleo.model.mtl.Query)acceleoElement;
description = AcceleoUIDocumentationUtils.getDocumentation(query);
} else if (acceleoElement instanceof org.eclipse.acceleo.model.mtl.Macro) {
org.eclipse.acceleo.model.mtl.Macro macro = (org.eclipse.acceleo.model.mtl.Macro)acceleoElement;
description = AcceleoUIDocumentationUtils.getDocumentation(macro);
}
return description;
}
/**
* Computes the image of the module element.
*
* @param acceleoElement
* The module element
* @return The image of the module element
*/
private Image computeImage(org.eclipse.acceleo.model.mtl.ModuleElement acceleoElement) {
Image image = null;
if (acceleoElement instanceof org.eclipse.acceleo.model.mtl.Template) {
org.eclipse.acceleo.model.mtl.Template template = (org.eclipse.acceleo.model.mtl.Template)acceleoElement;
image = AcceleoUIDocumentationUtils.getCompletionImage(template);
} else if (acceleoElement instanceof org.eclipse.acceleo.model.mtl.Query) {
org.eclipse.acceleo.model.mtl.Query query = (org.eclipse.acceleo.model.mtl.Query)acceleoElement;
image = AcceleoUIDocumentationUtils.getCompletionImage(query);
} else if (acceleoElement instanceof org.eclipse.acceleo.model.mtl.Macro) {
image = AcceleoUIActivator.getDefault().getImage(
IAcceleoContantsImage.TemplateEditor.Completion.MACRO_PUBLIC);
} else {
image = AcceleoUIActivator.getDefault().getImage(
IAcceleoContantsImage.TemplateEditor.Completion.OPERATION);
}
return image;
}
/**
* Gets the buffer to display for each structural feature.
*
* @param eStructuralFeature
* is the feature to display
* @return the string to display
*/
private String getPropertyDisplay(EStructuralFeature eStructuralFeature) {
StringBuffer displayProperty = new StringBuffer();
displayProperty.append(eStructuralFeature.getName());
if (eStructuralFeature.getEType() != null) {
displayProperty.append(':');
displayProperty.append(eStructuralFeature.getEType().getName());
}
if (eStructuralFeature.getLowerBound() == eStructuralFeature.getUpperBound()) {
displayProperty.append(' ');
displayProperty.append('[');
displayProperty.append(eStructuralFeature.getLowerBound());
displayProperty.append(']');
} else {
displayProperty.append(' ');
displayProperty.append('[');
displayProperty.append(eStructuralFeature.getLowerBound());
displayProperty.append(".."); //$NON-NLS-1$
if (eStructuralFeature.getUpperBound() == -1) {
displayProperty.append("*"); //$NON-NLS-1$
} else {
displayProperty.append(eStructuralFeature.getUpperBound());
}
displayProperty.append(']');
}
return displayProperty.toString();
}
/**
* Computes an import declaration based on the specified location within the text.
*
* @param proposals
* are the completion proposals (in out parameter)
*/
private void computeImportProposals(List<ICompletionProposal> proposals) {
int i = offset;
while (i > 0 && !Character.isWhitespace(text.charAt(i - 1))) {
i--;
}
String start = text.substring(i, offset);
Iterator<URI> dependencies = content.getAccessibleOutputFiles().iterator();
while (dependencies.hasNext()) {
URI uri = dependencies.next();
String displayString = new Path(uri.lastSegment()).removeFileExtension().lastSegment();
if (displayString.toLowerCase().startsWith(start.toLowerCase())) {
proposals.add(new AcceleoCompletionImportProposal(uri, offset - start.length(), start
.length(), AcceleoUIActivator.getDefault().getImage(
IAcceleoContantsImage.TemplateEditor.Completion.MODULE), displayString));
}
}
}
/**
* Computes a list of meta-model URIs based on the specified location within the text.
*
* @param proposals
* are the completion proposals (in out parameter)
*/
private void computeEPackageProposals(List<ICompletionProposal> proposals) {
final String uriImagePath = IAcceleoContantsImage.TemplateEditor.Completion.URI;
int i = offset;
while (i > 0 && text.charAt(i - 1) != '(' && text.charAt(i - 1) != ',' && text.charAt(i - 1) != '\'') {
i--;
}
String start = text.substring(i, offset);
Iterator<String> entries = new CompactLinkedHashSet<String>(AcceleoPackageRegistry.INSTANCE.keySet())
.iterator();
while (entries.hasNext()) {
String pURI = entries.next();
if (pURI.toLowerCase().startsWith(start.toLowerCase())) {
proposals.add(createCompletionProposal(pURI, offset - start.length(), start.length(), pURI
.length(), AcceleoUIActivator.getDefault().getImage(uriImagePath), pURI, null, pURI));
}
}
if (start.length() > 0) {
entries = new CompactLinkedHashSet<String>(AcceleoPackageRegistry.INSTANCE.keySet()).iterator();
while (entries.hasNext()) {
String pURI = entries.next();
Object obj = ModelUtils.getEPackageOrDescriptor(pURI);
String shortName = ""; //$NON-NLS-1$
if (obj instanceof LazyEPackageDescriptor) {
shortName = ((LazyEPackageDescriptor)obj).getName();
} else if (obj instanceof EPackage) {
shortName = ((EPackage)obj).getName();
} else {
EPackage resolved = ModelUtils.getEPackage(pURI);
if (resolved != null) {
shortName = resolved.getName();
}
}
if (shortName.startsWith(start.toLowerCase())) {
proposals.add(createCompletionProposal(pURI, offset - start.length(), start.length(),
pURI.length(), AcceleoUIActivator.getDefault().getImage(uriImagePath), pURI,
null, pURI));
}
}
}
List<IFile> ecoreFiles = new ArrayList<IFile>();
try {
computeEcoreFiles(ecoreFiles, ResourcesPlugin.getWorkspace().getRoot());
for (IFile ecoreFile : ecoreFiles) {
String ecorePath = ecoreFile.getFullPath().toString();
if (ecorePath.toLowerCase().startsWith(start.toLowerCase())) {
proposals.add(createCompletionProposal(ecorePath, offset - start.length(),
start.length(), ecorePath.length(), AcceleoUIActivator.getDefault().getImage(
uriImagePath), ecorePath, null, ecorePath));
}
if (start.length() > 0) {
String shortName = new Path(ecorePath).removeFileExtension().lastSegment();
if (shortName.startsWith(start.toLowerCase())) {
proposals.add(createCompletionProposal(ecorePath, offset - start.length(), start
.length(), ecorePath.length(), AcceleoUIActivator.getDefault().getImage(
uriImagePath), ecorePath, null, ecorePath));
}
}
}
} catch (CoreException e) {
AcceleoUIActivator.getDefault().getLog().log(e.getStatus());
}
Collections.sort(proposals, new Comparator<ICompletionProposal>() {
public int compare(ICompletionProposal o1, ICompletionProposal o2) {
// All completion proposal have the actual package URI as their display name
return o1.getDisplayString().compareTo(o2.getDisplayString());
}
});
}
/**
* Returns a list of existing ecore files in the workspace.
*
* @param ecoreFiles
* an output parameter to get all the ecore files
* @param container
* is the container to browse
* @throws CoreException
* contains a status object describing the cause of the exception
*/
private void computeEcoreFiles(List<IFile> ecoreFiles, IContainer container) throws CoreException {
if (container != null) {
IResource[] children;
if (container instanceof IWorkspaceRoot) {
children = ((IWorkspaceRoot)container).getProjects();
} else {
children = container.members();
}
if (children != null) {
for (int i = 0; i < children.length; ++i) {
IResource resource = children[i];
if (resource instanceof IFile && "ecore".equals(((IFile)resource).getFileExtension())) { //$NON-NLS-1$
ecoreFiles.add((IFile)resource);
} else if (resource instanceof IContainer && resource.isAccessible()) {
computeEcoreFiles(ecoreFiles, (IContainer)resource);
}
}
}
}
}
/**
* Computes a list of classifiers based on the specified location within the text.
*
* @param proposals
* are the completion proposals (in out parameter)
*/
private void computeEClassifierProposals(List<ICompletionProposal> proposals) {
if (content.getCST() == null) {
return;
}
int i = offset;
while (i > 0 && Character.isJavaIdentifierPart(text.charAt(i - 1))) {
i--;
}
int j = i;
while (j > 0 && Character.isWhitespace(text.charAt(j - 1))) {
j--;
}
if (j > 0 && (text.charAt(j - 1) == ':' || typeIsRequiredAfterParenthesis(j))) {
String start = text.substring(i, offset);
Iterator<EClassifier> eClassifierIt = content.getTypes().iterator();
while (eClassifierIt.hasNext()) {
EClassifier eClassifier = eClassifierIt.next();
if (eClassifier.getName().toLowerCase().startsWith(start.toLowerCase())) {
String name = eClassifier.getName();
if (name.endsWith(")")) { //$NON-NLS-1$
name = name.replaceAll("\\(", "(\\${"); //$NON-NLS-1$ //$NON-NLS-2$
name = name.replaceAll("\\)", "})"); //$NON-NLS-1$ //$NON-NLS-2$
proposals.add(createTemplateProposal(name, offset - start.length(), start.length(),
name.length(), AcceleoUIActivator.getDefault().getImage(
IAcceleoContantsImage.TemplateEditor.Completion.TYPE), eClassifier
.getName(), null, name));
} else {
proposals.add(createCompletionProposal(eClassifier.getName(),
offset - start.length(), start.length(), eClassifier.getName().length(),
AcceleoUIActivator.getDefault().getImage(
"icons/template-editor/completion/Type.gif"), eClassifier.getName(), //$NON-NLS-1$
null, eClassifier.getName()));
}
}
}
}
}
/**
* Indicates if one meta type is required at the given index.
*
* @param index
* is the index in the text
* @return true if the meta types are requested at the given index
*/
private boolean typeIsRequiredAfterParenthesis(int index) {
if (index <= 0 || text.charAt(index - 1) != '(') {
return false;
}
int i = index - 1;
while (i > 0 && Character.isJavaIdentifierPart(text.charAt(i - 1))) {
i--;
}
String start = text.substring(i, index - 1);
boolean result;
if (start.length() == 0) {
result = false;
} else if (CollectionKind.getByName(start) != null) {
result = true;
} else {
result = false;
int startPosition = cstNode.getStartPosition();
if (startPosition < 0) {
startPosition = 0;
}
if (startPosition > index - 1) {
startPosition = index - 1;
}
String textOCL = text.substring(startPosition, index - 1);
Iterator<Choice> choices = content.getSyntaxHelp(textOCL, index - 1).iterator();
while (!result && choices.hasNext()) {
Choice next = choices.next();
if (start.length() > 0 && next.getName().startsWith(start)
&& next.getElement() instanceof EOperation) {
EOperation eOperation = (EOperation)next.getElement();
for (EParameter eParameter : eOperation.getEParameters()) {
if (eParameter.getEType() != null
&& "OclType".equals(eParameter.getEType().getName())) { //$NON-NLS-1$
result = true;
break;
}
}
}
}
}
return result;
}
/**
* Computes a module declaration pattern based on the specified location within the text.
*
* @param proposals
* are the completion proposals (in out parameter)
*/
private void computeModulePatternProposals(List<ICompletionProposal> proposals) {
int i = offset;
while (i > 0 && Character.isJavaIdentifierPart(text.charAt(i - 1))) {
i--;
}
if (i > 0 && text.charAt(i - 1) == '[') {
i--;
}
String start = text.substring(i, offset);
StringBuffer tabBuffer = new StringBuffer();
while (i > 0 && Character.isWhitespace(text.charAt(i - 1)) && text.charAt(i - 1) != '\n') {
tabBuffer.insert(0, text.charAt(i - 1));
i--;
}
if (i == 0 || text.charAt(i - 1) == '\n') {
String fileName;
if (content.getFile() != null) {
fileName = content.getFile().getName();
} else {
fileName = ""; //$NON-NLS-1$
}
if (IAcceleoConstants.MODULE.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.MODULE).startsWith(start.toLowerCase())) {
String replacementStringBefore = '[' + IAcceleoConstants.MODULE + ' '
+ new Path(fileName).removeFileExtension().lastSegment() + "('"; //$NON-NLS-1$
String replacementStringAfter = "${ecore}') /]\n" + tabBuffer.toString(); //$NON-NLS-1$
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start
.length(), replacementStringBefore.length(), AcceleoUIActivator.getDefault()
.getImage(IAcceleoContantsImage.TemplateEditor.Completion.PATTERN),
'[' + IAcceleoConstants.MODULE + ']', null, replacementString));
}
}
}
/**
* Computes a list of patterns based on the specified location within the text.
*
* @param proposals
* are the completion proposals (in out parameter)
*/
private void computePatternsProposals(List<ICompletionProposal> proposals) {
int i = offset;
while (i > 0 && (Character.isJavaIdentifierPart(text.charAt(i - 1)) || text.charAt(i - 1) == '@')) {
i--;
}
if (i > 0 && (text.charAt(i - 1) == '[' || text.charAt(i - 1) == ']')) {
i--;
}
String start = text.substring(i, offset);
StringBuffer tabBuffer = new StringBuffer();
while (i > 0 && Character.isWhitespace(text.charAt(i - 1)) && text.charAt(i - 1) != '\n') {
tabBuffer.insert(0, text.charAt(i - 1));
i--;
}
String tab = tabBuffer.toString();
Image patternImage = AcceleoUIActivator.getDefault().getImage(
IAcceleoContantsImage.TemplateEditor.Completion.PATTERN);
if (i > 0 && text.charAt(i - 1) == '\n') {
// We are not interested by the first line
if (content.getCSTParent(cstNode, org.eclipse.acceleo.parser.cst.ModuleElement.class) == null
&& text.substring(0, i).indexOf('[' + IAcceleoConstants.TEMPLATE) == -1
&& text.substring(0, i).indexOf('[' + IAcceleoConstants.QUERY) == -1
&& text.substring(0, i).indexOf('[' + IAcceleoConstants.MACRO) == -1) {
if (IAcceleoConstants.IMPORT.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.IMPORT).startsWith(start.toLowerCase())) {
String replacementStringBefore = '[' + IAcceleoConstants.IMPORT + ' ';
String replacementStringAfter = "${common} /]" + tab; //$NON-NLS-1$
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start
.length(), replacementStringBefore.length(), patternImage,
'[' + IAcceleoConstants.IMPORT + ']', null, replacementString));
}
}
if (!(cstNode instanceof ModuleElement)
&& content.getCSTParent(cstNode, org.eclipse.acceleo.parser.cst.ModuleElement.class) == null) {
computeModuleElementsPatternsProposals(proposals, start, tab, patternImage);
}
if (cstNode instanceof IfBlock
|| (cstNode instanceof TextExpression && cstNode.eContainer() instanceof IfBlock)) {
if (IAcceleoConstants.ELSE_IF.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.ELSE_IF).startsWith(start.toLowerCase())) {
String replacementStringBefore = '[' + IAcceleoConstants.ELSE_IF + ' ' + '(';
String replacementStringAfter = "${expression})]" + '\n' + tab + '\t'; //$NON-NLS-1$
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start
.length(), replacementStringBefore.length(), patternImage,
'[' + IAcceleoConstants.ELSE_IF + ']', null, replacementString));
}
if (IAcceleoConstants.ELSE.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.ELSE).startsWith(start.toLowerCase())) {
String replacementString = '[' + IAcceleoConstants.ELSE + ']' + '\n' + tab + '\t';
proposals.add(createCompletionProposal(replacementString, offset - start.length(), start
.length(), replacementString.length(), patternImage,
'[' + IAcceleoConstants.ELSE + ']', null, replacementString));
}
}
if (cstNode instanceof LetBlock
|| (cstNode instanceof TextExpression && cstNode.eContainer() instanceof LetBlock)) {
if (IAcceleoConstants.ELSE_LET.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.ELSE_LET).startsWith(start.toLowerCase())) {
String replacementStringBefore = '[' + IAcceleoConstants.ELSE_LET + ' ';
String replacementStringAfter = "${var} " + ":" + defaultStart + defaultVariableType + "} = ${self}]" + '\n' //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ tab + '\t';
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start
.length(), replacementStringBefore.length(), patternImage,
'[' + IAcceleoConstants.ELSE_LET + ']', null, replacementString));
}
if (IAcceleoConstants.ELSE.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.ELSE).startsWith(start.toLowerCase())) {
String replacementString = '[' + IAcceleoConstants.ELSE + ']' + '\n' + tab + '\t';
proposals.add(createCompletionProposal(replacementString, offset - start.length(), start
.length(), replacementString.length(), patternImage,
'[' + IAcceleoConstants.ELSE + ']', null, replacementString));
}
}
if (!start.startsWith("@")) { //$NON-NLS-1$
computeBlocksPatternsProposals(proposals, start, tab, patternImage);
}
if (!(cstNode instanceof ModelExpression)
&& (IAcceleoConstants.COMMENT.startsWith(start.toLowerCase()) || ('[' + IAcceleoConstants.COMMENT)
.startsWith(start.toLowerCase()))) {
String replacementStringBefore = '[' + IAcceleoConstants.COMMENT + ' ';
String replacementStringAfter = " /]\n" + tab; //$NON-NLS-1$
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createCompletionProposal(replacementString, offset - start.length(), start
.length(), replacementStringBefore.length(), patternImage,
'[' + IAcceleoConstants.COMMENT + ']', null, replacementString));
}
} else {
if (!start.startsWith("@")) { //$NON-NLS-1$
computeBlocksPatternsProposals(proposals, start, tab, patternImage);
}
}
computeMainTagPatternsProposals(proposals, start, tab, patternImage);
}
/**
* Computes a list of patterns for the main annotation (@main).
*
* @param proposals
* are the completion proposals (in out parameter)
* @param start
* is the beginning text
* @param tab
* is the current indentation of the text
* @param patternImage
* is the image to show in the completion popup menu
*/
private void computeMainTagPatternsProposals(List<ICompletionProposal> proposals, String start,
String tab, Image patternImage) {
if (cstNode instanceof TextExpression) {
if (IAcceleoConstants.TAG_MAIN.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.TAG_MAIN).startsWith(start.toLowerCase())) {
String replacementStringBefore = '[' + IAcceleoConstants.COMMENT + ' '
+ IAcceleoConstants.TAG_MAIN + ' ' + "/]\n" + tab; //$NON-NLS-1$
String replacementString = replacementStringBefore;
proposals.add(createCompletionProposal(replacementString, offset - start.length(), start
.length(), replacementStringBefore.length(), patternImage,
IAcceleoConstants.TAG_MAIN, null, replacementString));
}
}
}
/**
* Computes a list of patterns for all the module elements : template, query, macro...
*
* @param proposals
* are the completion proposals (in out parameter)
* @param start
* is the beginning text
* @param tab
* is the current indentation of the text
* @param patternImage
* is the image to show in the completion popup menu
*/
private void computeModuleElementsPatternsProposals(List<ICompletionProposal> proposals, String start,
String tab, Image patternImage) {
if (IAcceleoConstants.TEMPLATE.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.TEMPLATE).startsWith(start.toLowerCase())) {
String replacementStringBefore = '[' + IAcceleoConstants.TEMPLATE + defaultStart
+ IAcceleoConstants.VISIBILITY_KIND_PUBLIC + "} "; //$NON-NLS-1$
String replacementStringAfter = "${name}(" + "${arg}" + " : ${" + defaultVariableType + "})]\n" + tab //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ '\t'
+ "${template_expression}" + '\n' + tab + '[' + '/' + IAcceleoConstants.TEMPLATE + ']'; //$NON-NLS-1$
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start.length(),
replacementStringBefore.length(), patternImage, '[' + IAcceleoConstants.TEMPLATE + ']',
null, tab + replacementString));
}
if (IAcceleoConstants.QUERY.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.QUERY).startsWith(start.toLowerCase())) {
String replacementStringBefore = '[' + IAcceleoConstants.QUERY + defaultStart
+ IAcceleoConstants.VISIBILITY_KIND_PUBLIC + "} "; //$NON-NLS-1$
String replacementStringAfter = "${name}(${arg} : ${" + defaultVariableType + "}) : ${OclAny} = ${self} /]\n"; //$NON-NLS-1$ //$NON-NLS-2$
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start.length(),
replacementStringBefore.length(), patternImage, '[' + IAcceleoConstants.QUERY + ']',
null, tab + replacementString));
}
if (IAcceleoConstants.MACRO.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.MACRO).startsWith(start.toLowerCase())) {
String replacementStringBefore = '[' + IAcceleoConstants.MACRO + ' '
+ IAcceleoConstants.VISIBILITY_KIND_PUBLIC + ' ';
String replacementStringAfter = "${name}(${arg} : ${" + defaultVariableType + "}) : ${String}" //$NON-NLS-1$ //$NON-NLS-2$
+ "]\n" + tab + '\t' + '\n' + tab + '[' + '/' + IAcceleoConstants.MACRO //$NON-NLS-1$
+ ']';
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start.length(),
replacementStringBefore.length(), patternImage, '[' + IAcceleoConstants.MACRO + ']',
null, tab + replacementString));
}
}
/**
* Checks the position and computes a list of patterns for all the blocks : for, if, file...
*
* @param proposals
* are the completion proposals (in out parameter)
* @param start
* is the beginning text
* @param tab
* is the current indentation of the text
* @param patternImage
* is the image to show in the completion popup menu
*/
private void computeBlocksPatternsProposals(List<ICompletionProposal> proposals, String start,
String tab, Image patternImage) {
if (cstNode instanceof Template) {
if (((Template)cstNode).getBody().size() == 0
|| offset >= ((Template)cstNode).getBody().get(0).getStartPosition()) {
computeBlocksPatternsProposalsSub(proposals, start, tab, patternImage);
}
} else if (cstNode instanceof Block) {
computeBlocksPatternsProposalsSub(proposals, start, tab, patternImage);
}
if (content.getCSTParent(cstNode, org.eclipse.acceleo.parser.cst.ModuleElement.class) != null) {
if (cstNode instanceof org.eclipse.acceleo.parser.cst.TextExpression) {
int size = proposals.size();
computeBlocksPatternsProposalsSub(proposals, start, tab, patternImage);
if (proposals.size() == size) {
computeBlocksPatternsProposalsSub(proposals, "", tab, patternImage); //$NON-NLS-1$
}
} else if (cstNode instanceof org.eclipse.acceleo.parser.cst.ModelExpression) {
if (start.startsWith("[") && (offset == text.length() || text.charAt(offset) != '/')) { //$NON-NLS-1$
computeBlocksPatternsProposalsSub(proposals, start, tab, patternImage);
}
}
}
}
/**
* Computes a list of patterns for all the blocks : for, if, file...
*
* @param proposals
* are the completion proposals (in out parameter)
* @param start
* is the beginning text
* @param tab
* is the current indentation of the text
* @param patternImage
* is the image to show in the completion popup menu
*/
private void computeBlocksPatternsProposalsSub(List<ICompletionProposal> proposals, String start,
String tab, Image patternImage) {
final String cursor = "${block_expression}"; //$NON-NLS-1$
if (IAcceleoConstants.DEFAULT_BEGIN.startsWith(start.toLowerCase())) {
String replacementStringBefore = "[${expression}"; //$NON-NLS-1$
String replacementStringAfter = "/]"; //$NON-NLS-1$
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start.length(),
replacementStringBefore.length(), patternImage, "[ ]", null, replacementString)); //$NON-NLS-1$
}
if (IAcceleoConstants.FOR.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.FOR).startsWith(start.toLowerCase())) {
String virtualText = getVirtualTextInBlock();
String replacementStringBefore = '[' + IAcceleoConstants.FOR + ' ' + '(';
String replacementStringAfter;
if (virtualText.length() > 0) {
replacementStringAfter = "${it} : ${" + defaultVariableType + "} | ${expression})]\n" + //$NON-NLS-1$ //$NON-NLS-2$
tab + virtualText + tab + '[' + '/' + IAcceleoConstants.FOR + ']' + '\n';
} else {
replacementStringAfter = "${it} : ${" + defaultVariableType + "} | ${expression})]\n" + tab + '\t' //$NON-NLS-1$ //$NON-NLS-2$
+ cursor + '\n' + tab + '[' + '/' + IAcceleoConstants.FOR + ']';
}
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start.length()
+ virtualText.length(), replacementStringBefore.length(), patternImage,
'[' + IAcceleoConstants.FOR + ']', null, tab + replacementString));
}
if (IAcceleoConstants.IF.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.IF).startsWith(start.toLowerCase())) {
String virtualText = getVirtualTextInBlock();
String replacementStringBefore = '[' + IAcceleoConstants.IF + ' ' + '(';
String replacementStringAfter;
if (virtualText.length() > 0) {
replacementStringAfter = "${condition})]\n" + tab + virtualText + tab + '[' //$NON-NLS-1$
+ '/' + IAcceleoConstants.IF + ']' + '\n';
} else {
replacementStringAfter = "${condition})]\n" + tab + '\t' + cursor + '\n' + tab + '[' //$NON-NLS-1$
+ '/' + IAcceleoConstants.IF + ']';
}
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start.length()
+ virtualText.length(), replacementStringBefore.length(), patternImage,
'[' + IAcceleoConstants.IF + ']', null, tab + replacementString));
}
if (IAcceleoConstants.FILE.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.FILE).startsWith(start.toLowerCase())) {
computeFileBlockPatternsProposals(proposals, start, tab, patternImage, true);
computeFileBlockPatternsProposals(proposals, start, tab, patternImage, false);
}
if (IAcceleoConstants.LET.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.LET).startsWith(start.toLowerCase())) {
String replacementStringBefore = '[' + IAcceleoConstants.LET + ' ';
String replacementStringAfter = "${var}" + " : ${" + defaultVariableType + "} = ${self}]\n" + tab + '\t' + cursor //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ '\n' + tab + '[' + '/' + IAcceleoConstants.LET + ']';
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start.length(),
replacementStringBefore.length(), patternImage, '[' + IAcceleoConstants.LET + ']', null,
tab + replacementString));
}
if (IAcceleoConstants.TRACE.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.TRACE).startsWith(start.toLowerCase())) {
String replacementStringBefore = '[' + IAcceleoConstants.TRACE + " ('"; //$NON-NLS-1$
String replacementStringAfter = "${message}')]\n" + tab + '\t' + cursor + '\n' + tab + '[' //$NON-NLS-1$
+ '/' + IAcceleoConstants.TRACE + ']';
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start.length(),
replacementStringBefore.length(), patternImage, '[' + IAcceleoConstants.TRACE + ']',
null, tab + replacementString));
}
if (IAcceleoConstants.PROTECTED_AREA.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.PROTECTED_AREA).startsWith(start.toLowerCase())) {
String replacementStringBefore = '[' + IAcceleoConstants.PROTECTED_AREA + " ('"; //$NON-NLS-1$
String replacementStringAfter = "${protected}')]\n" + tab + '\t' + cursor + '\n' + tab + '[' //$NON-NLS-1$
+ '/' + IAcceleoConstants.PROTECTED_AREA + ']';
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start.length(),
replacementStringBefore.length(), patternImage,
'[' + IAcceleoConstants.PROTECTED_AREA + ']', null, tab + replacementString));
}
if (IAcceleoConstants.SUPER.startsWith(start.toLowerCase())
|| ('[' + IAcceleoConstants.SUPER).startsWith(start.toLowerCase())) {
String replacementStringBefore = '[' + IAcceleoConstants.SUPER + "/]\n" + tab; //$NON-NLS-1$
String replacementString = replacementStringBefore;
proposals.add(createCompletionProposal(replacementString, offset - start.length(),
start.length(), replacementStringBefore.length(), patternImage,
'[' + IAcceleoConstants.SUPER + ']', null, replacementString));
}
if (IAcceleoConstants.DEFAULT_BEGIN.startsWith(start.toLowerCase())) {
String replacementString = "[ '[' /] "; //$NON-NLS-1$
proposals
.add(createCompletionProposal(replacementString, offset - start.length(), start.length(),
replacementString.length(), patternImage, "'['", null, replacementString)); //$NON-NLS-1$
}
if ("".equals(start.toLowerCase())) { //$NON-NLS-1$
String replacementString = "[ ']' /] "; //$NON-NLS-1$
proposals
.add(createCompletionProposal(replacementString, offset - start.length(), start.length(),
replacementString.length(), patternImage, "']'", null, replacementString)); //$NON-NLS-1$
}
}
/**
* Gets the example text in the block to create. If the current line contains a significant character,
* we'll try to embed the following text in the new block.
*
* @return the content of the new block
*/
private String getVirtualTextInBlock() {
boolean lineContainsTextAfter = false;
int i = offset;
while (i < text.length()) {
char c = text.charAt(i);
if (!Character.isWhitespace(c)) {
lineContainsTextAfter = true;
break;
} else if (c == '\n') {
break;
} else {
i++;
}
}
if (lineContainsTextAfter) {
i = offset;
int iEmptyLineAndNoMTL = -1;
int iBeginLine = i;
boolean currentLineIsEmpty = false;
while (i < text.length()) {
char c = text.charAt(i);
if (!Character.isWhitespace(c)) {
if (currentLineIsEmpty && c == '[') {
iEmptyLineAndNoMTL = iBeginLine;
break;
}
currentLineIsEmpty = false;
} else if (c == '\n') {
if (currentLineIsEmpty) {
iEmptyLineAndNoMTL = i + 1;
break;
}
currentLineIsEmpty = true;
iBeginLine = i + 1;
}
i++;
}
if (iEmptyLineAndNoMTL > -1) {
return text.substring(offset, iEmptyLineAndNoMTL).replace("$", "$$"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return ""; //$NON-NLS-1$
}
/**
* Computes a list of patterns for the file block.
*
* @param proposals
* are the completion proposals (in out parameter)
* @param start
* is the beginning text
* @param tab
* is the current indentation of the text
* @param patternImage
* is the image to show in the completion popup menu
* @param withMainTag
* indicates if a main annotation (@main) must be added before the file block
*/
private void computeFileBlockPatternsProposals(List<ICompletionProposal> proposals, String start,
String tab, Image patternImage, boolean withMainTag) {
String mainTagText;
if (withMainTag) {
mainTagText = '[' + IAcceleoConstants.COMMENT + ' ' + IAcceleoConstants.TAG_MAIN + ' ' + '/'
+ ']' + '\n' + tab;
} else {
mainTagText = ""; //$NON-NLS-1$
}
String replacementStringBefore = mainTagText + '[' + IAcceleoConstants.FILE + ' ' + "(${path}"; //$NON-NLS-1$
String defaultEncoding;
try {
if (content != null && content.getFile() != null) {
defaultEncoding = content.getFile().getCharset();
} else {
defaultEncoding = System.getProperty("file.encoding"); //$NON-NLS-1$
}
} catch (CoreException e) {
AcceleoUIActivator.log(e, true);
defaultEncoding = System.getProperty("file.encoding"); //$NON-NLS-1$
}
String replacementStringAfter = ", ${false}, '" + defaultEncoding //$NON-NLS-1$
+ "')]\n" + tab + '\t' + "${file_expression}" + '\n' + tab + '[' + '/' //$NON-NLS-1$ //$NON-NLS-2$
+ IAcceleoConstants.FILE + ']';
String replacementString = replacementStringBefore + replacementStringAfter;
String displayString = '[' + IAcceleoConstants.FILE + ']';
if (withMainTag) {
displayString += " - " + IAcceleoConstants.TAG_MAIN; //$NON-NLS-1$
}
proposals
.add(createTemplateProposal(replacementString, offset - start.length(), start.length(),
replacementStringBefore.length(), patternImage, displayString, null, tab
+ replacementString));
}
/**
* Computes a list of keywords based on the specified location within the text.
*
* @param proposals
* are the completion proposals (in out parameter)
*/
private void computeKeywordsProposals(List<ICompletionProposal> proposals) {
if (cstNode != null) {
int i = offset;
while (i > 0 && Character.isJavaIdentifierPart(text.charAt(i - 1))) {
i--;
}
String start = text.substring(i, offset);
int bHeader = cstNode.getStartPosition();
String bHeaderText;
if (bHeader > -1 && bHeader < i) {
bHeaderText = text.substring(bHeader, i).trim();
} else {
bHeaderText = ""; //$NON-NLS-1$
}
Image keywordImage = AcceleoUIActivator.getDefault().getImage(
IAcceleoContantsImage.TemplateEditor.Completion.KEYWORD);
if (cstNode instanceof org.eclipse.acceleo.parser.cst.Module) {
if (isHeaderAfterParenthesis(bHeaderText)
&& ((org.eclipse.acceleo.parser.cst.Module)cstNode).getExtends().size() == 0) {
computeKeywordProposal(proposals, start, IAcceleoConstants.EXTENDS + ' ',
"", keywordImage); //$NON-NLS-1$
}
}
if (cstNode instanceof org.eclipse.acceleo.parser.cst.ModelExpression
&& cstNode.eContainingFeature() == CstPackage.eINSTANCE.getBlock_Body()) {
computeKeywordsProposalsInTemplateInvocationHeader(proposals, start, bHeaderText,
keywordImage);
}
if (cstNode instanceof org.eclipse.acceleo.parser.cst.Template) {
computeKeywordsProposalsInTemplateHeader(proposals, start, bHeaderText, keywordImage);
}
if (cstNode instanceof org.eclipse.acceleo.parser.cst.Macro) {
computeKeywordsProposalsInMacroHeader(proposals, start, bHeaderText, keywordImage);
}
if (cstNode instanceof org.eclipse.acceleo.parser.cst.ForBlock) {
computeKeywordsProposalsInForBlockHeader(proposals, start, bHeaderText, keywordImage);
}
}
}
/**
* Computes a list of keywords for a 'Template' header.
*
* @param proposals
* are the completion proposals (in out parameter)
* @param start
* is the beginning text of the candidates
* @param bHeaderText
* is the current beginning text of the header, from the beginning index to the current offset
* @param keywordImage
* is the image to show in the completion popup menu
*/
private void computeKeywordsProposalsInTemplateHeader(List<ICompletionProposal> proposals, String start,
String bHeaderText, Image keywordImage) {
if (bHeaderText.equals(IAcceleoConstants.TEMPLATE)) {
computeKeywordProposal(proposals, start, IAcceleoConstants.VISIBILITY_KIND_PUBLIC + ' ', "", //$NON-NLS-1$
keywordImage);
computeKeywordProposal(proposals, start, IAcceleoConstants.VISIBILITY_KIND_PROTECTED + ' ', "", //$NON-NLS-1$
keywordImage);
computeKeywordProposal(proposals, start, IAcceleoConstants.VISIBILITY_KIND_PRIVATE + ' ', "", //$NON-NLS-1$
keywordImage);
}
if (isHeaderAfterParenthesis(bHeaderText)) {
// Remark : The completion proposals have to be ordered : overrides -> guard -> post...
StringBuffer bHeaderBuffer = new StringBuffer(bHeaderText);
Sequence pGuard = new Sequence(IAcceleoConstants.GUARD, IAcceleoConstants.PARENTHESIS_BEGIN);
Sequence pPost = new Sequence(IAcceleoConstants.POST, IAcceleoConstants.PARENTHESIS_BEGIN);
Sequence pInit = new Sequence(IAcceleoConstants.BRACKETS_BEGIN);
org.eclipse.acceleo.parser.cst.Template cstTemplate = (org.eclipse.acceleo.parser.cst.Template)cstNode;
if (cstTemplate.getOverrides().size() == 0 && pGuard.search(bHeaderBuffer).b() == -1
&& pPost.search(bHeaderBuffer).b() == -1 && pInit.search(bHeaderBuffer).b() == -1) {
computeKeywordProposal(proposals, start, IAcceleoConstants.OVERRIDES + ' ', "", keywordImage); //$NON-NLS-1$
}
boolean isAfterOverrides = cstTemplate.getOverrides().size() == 0
|| cstTemplate.getOverrides().get(cstTemplate.getOverrides().size() - 1).getEndPosition() < offset;
if (cstTemplate.getGuard() == null && pPost.search(bHeaderBuffer).b() == -1
&& pInit.search(bHeaderBuffer).b() == -1) {
if (isAfterOverrides) {
computeKeywordProposal(proposals, start, IAcceleoConstants.GUARD + ' '
+ IAcceleoConstants.PARENTHESIS_BEGIN, IAcceleoConstants.PARENTHESIS_END,
keywordImage);
}
}
boolean isAfterGuard = cstTemplate.getGuard() == null
|| cstTemplate.getGuard().getEndPosition() < offset;
if (cstTemplate.getPost() == null && pInit.search(bHeaderBuffer).b() == -1) {
if (isAfterOverrides && isAfterGuard) {
computeKeywordProposal(proposals, start, IAcceleoConstants.POST + ' '
+ IAcceleoConstants.PARENTHESIS_BEGIN, IAcceleoConstants.PARENTHESIS_END,
keywordImage);
}
}
boolean isAfterPost = cstTemplate.getPost() == null
|| cstTemplate.getPost().getEndPosition() < offset;
if (cstTemplate.getInit() == null && "{".startsWith(start.toLowerCase())) { //$NON-NLS-1$
if (isAfterOverrides && isAfterGuard && isAfterPost) {
String replacementStringBefore = "{ "; //$NON-NLS-1$
String replacementStringAfter = "${var} : ${" + defaultVariableType + "}; }"; //$NON-NLS-1$ //$NON-NLS-2$
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start
.length(), replacementStringBefore.length(), keywordImage, "{ }", null, //$NON-NLS-1$
replacementString));
}
}
}
}
/**
* Computes a list of keywords for a 'Macro' header.
*
* @param proposals
* are the completion proposals (in out parameter)
* @param start
* is the beginning text of the candidates
* @param bHeaderText
* is the current beginning text of the header, from the beginning index to the current offset
* @param keywordImage
* is the image to show in the completion popup menu
*/
private void computeKeywordsProposalsInMacroHeader(List<ICompletionProposal> proposals, String start,
String bHeaderText, Image keywordImage) {
if (bHeaderText.equals(IAcceleoConstants.MACRO)) {
computeKeywordProposal(proposals, start, IAcceleoConstants.VISIBILITY_KIND_PUBLIC + ' ', "", //$NON-NLS-1$
keywordImage);
computeKeywordProposal(proposals, start, IAcceleoConstants.VISIBILITY_KIND_PROTECTED + ' ', "", //$NON-NLS-1$
keywordImage);
computeKeywordProposal(proposals, start, IAcceleoConstants.VISIBILITY_KIND_PRIVATE + ' ', "", //$NON-NLS-1$
keywordImage);
}
}
/**
* Computes a list of keywords for a 'TemplateInvocation'.
*
* @param proposals
* are the completion proposals (in out parameter)
* @param start
* is the beginning text of the candidates
* @param bHeaderText
* is the current beginning text of the header, from the beginning index to the current offset
* @param keywordImage
* is the image to show in the completion popup menu
*/
private void computeKeywordsProposalsInTemplateInvocationHeader(List<ICompletionProposal> proposals,
String start, String bHeaderText, Image keywordImage) {
if (isHeaderAfterParenthesis(bHeaderText) || bHeaderText.indexOf(IAcceleoConstants.SUPER) > -1) {
StringBuffer bHeaderBuffer = new StringBuffer(bHeaderText);
Sequence pSeparator = new Sequence(IAcceleoConstants.SEPARATOR,
IAcceleoConstants.PARENTHESIS_BEGIN);
Sequence pAfter = new Sequence(IAcceleoConstants.AFTER, IAcceleoConstants.PARENTHESIS_BEGIN);
Sequence pGuard = new Sequence(IAcceleoConstants.GUARD, IAcceleoConstants.PARENTHESIS_BEGIN);
if (((org.eclipse.acceleo.parser.cst.ModelExpression)cstNode).getBefore() == null) {
if (pSeparator.search(bHeaderBuffer).b() == -1 && pAfter.search(bHeaderBuffer).b() == -1
&& pGuard.search(bHeaderBuffer).b() == -1) {
computeKeywordProposal(proposals, start, IAcceleoConstants.BEFORE + ' '
+ IAcceleoConstants.PARENTHESIS_BEGIN, IAcceleoConstants.PARENTHESIS_END,
keywordImage);
}
}
if (((org.eclipse.acceleo.parser.cst.ModelExpression)cstNode).getEach() == null
&& pAfter.search(bHeaderBuffer).b() == -1 && pGuard.search(bHeaderBuffer).b() == -1) {
computeKeywordProposal(proposals, start, IAcceleoConstants.SEPARATOR + ' '
+ IAcceleoConstants.PARENTHESIS_BEGIN, IAcceleoConstants.PARENTHESIS_END,
keywordImage);
}
if (((org.eclipse.acceleo.parser.cst.ModelExpression)cstNode).getAfter() == null
&& pGuard.search(bHeaderBuffer).b() == -1) {
computeKeywordProposal(proposals, start, IAcceleoConstants.AFTER + ' '
+ IAcceleoConstants.PARENTHESIS_BEGIN, IAcceleoConstants.PARENTHESIS_END,
keywordImage);
}
}
}
/**
* Computes a list of keywords for a 'ForBlock' header.
*
* @param proposals
* are the completion proposals (in out parameter)
* @param start
* is the beginning text of the candidates
* @param bHeaderText
* is the current beginning text of the header, from the beginning index to the current offset
* @param keywordImage
* is the image to show in the completion popup menu
*/
private void computeKeywordsProposalsInForBlockHeader(List<ICompletionProposal> proposals, String start,
String bHeaderText, Image keywordImage) {
if (isHeaderAfterParenthesis(bHeaderText)) {
StringBuffer bHeaderBuffer = new StringBuffer(bHeaderText);
Sequence pSeparator = new Sequence(IAcceleoConstants.SEPARATOR,
IAcceleoConstants.PARENTHESIS_BEGIN);
Sequence pAfter = new Sequence(IAcceleoConstants.AFTER, IAcceleoConstants.PARENTHESIS_BEGIN);
Sequence pGuard = new Sequence(IAcceleoConstants.GUARD, IAcceleoConstants.PARENTHESIS_BEGIN);
Sequence pInit = new Sequence(IAcceleoConstants.BRACKETS_BEGIN);
if (((org.eclipse.acceleo.parser.cst.ForBlock)cstNode).getBefore() == null) {
if (pSeparator.search(bHeaderBuffer).b() == -1 && pAfter.search(bHeaderBuffer).b() == -1
&& pGuard.search(bHeaderBuffer).b() == -1 && pInit.search(bHeaderBuffer).b() == -1) {
computeKeywordProposal(proposals, start, IAcceleoConstants.BEFORE + ' '
+ IAcceleoConstants.PARENTHESIS_BEGIN, IAcceleoConstants.PARENTHESIS_END,
keywordImage);
}
}
if (((org.eclipse.acceleo.parser.cst.ForBlock)cstNode).getEach() == null
&& pAfter.search(bHeaderBuffer).b() == -1 && pGuard.search(bHeaderBuffer).b() == -1
&& pInit.search(bHeaderBuffer).b() == -1) {
computeKeywordProposal(proposals, start, IAcceleoConstants.SEPARATOR + ' '
+ IAcceleoConstants.PARENTHESIS_BEGIN, IAcceleoConstants.PARENTHESIS_END,
keywordImage);
}
if (((org.eclipse.acceleo.parser.cst.ForBlock)cstNode).getAfter() == null
&& pGuard.search(bHeaderBuffer).b() == -1 && pInit.search(bHeaderBuffer).b() == -1) {
computeKeywordProposal(proposals, start, IAcceleoConstants.AFTER + ' '
+ IAcceleoConstants.PARENTHESIS_BEGIN, IAcceleoConstants.PARENTHESIS_END,
keywordImage);
}
if (((org.eclipse.acceleo.parser.cst.ForBlock)cstNode).getGuard() == null
&& pInit.search(bHeaderBuffer).b() == -1) {
computeKeywordProposal(proposals, start, IAcceleoConstants.GUARD + ' '
+ IAcceleoConstants.PARENTHESIS_BEGIN, IAcceleoConstants.PARENTHESIS_END,
keywordImage);
}
if (((org.eclipse.acceleo.parser.cst.ForBlock)cstNode).getInit() == null && "{".startsWith(start)) { //$NON-NLS-1$
String replacementStringBefore = "{ "; //$NON-NLS-1$
String replacementStringAfter = "${var} : ${" + defaultVariableType + "}; }"; //$NON-NLS-1$ //$NON-NLS-2$
String replacementString = replacementStringBefore + replacementStringAfter;
proposals.add(createTemplateProposal(replacementString, offset - start.length(), start
.length(), replacementStringBefore.length(), keywordImage, "{ }", null, //$NON-NLS-1$
replacementString));
}
}
}
/**
* Indicates if there are parenthesis in the text, and if there are finished. It works only if we are
* before ']'
*
* @param aText
* is the text to parse
* @return true if there are parenthesis in the text, and if there are finished
*/
private boolean isHeaderAfterParenthesis(String aText) {
final String tag = " ---TAG--- "; //$NON-NLS-1$
StringBuffer buffer = new StringBuffer();
buffer.append(aText);
buffer.append(tag);
Sequence literalEscape = new Sequence(IAcceleoConstants.LITERAL_ESCAPE);
SequenceBlock pLiteral = new SequenceBlock(new Sequence(IAcceleoConstants.LITERAL_BEGIN),
new Sequence(IAcceleoConstants.LITERAL_END), literalEscape, false, null);
SequenceBlock pParenthesis = new SequenceBlock(new Sequence(IAcceleoConstants.PARENTHESIS_BEGIN),
new Sequence(IAcceleoConstants.PARENTHESIS_END), null, true, new SequenceBlock[] {pLiteral });
Sequence pHeaderEnd = new Sequence(IAcceleoConstants.DEFAULT_END);
Sequence pTag = new Sequence(tag);
return (pHeaderEnd.search(buffer, 0, buffer.length(), null, new SequenceBlock[] {pLiteral, }).b() == -1)
&& (pParenthesis.search(buffer, 0, buffer.length()).b() > -1)
&& (pTag.search(buffer, 0, buffer.length(), null,
new SequenceBlock[] {pLiteral, pParenthesis, }).b() > -1);
}
/**
* Creates a keyword proposal.
*
* @param proposals
* are the completion proposals (in out parameter)
* @param start
* is the beginning text
* @param replacementStringBefore
* is the replacement string before the new offset
* @param replacementStringAfter
* is the replacement string after the new offset
* @param keywordImage
* is the image to show in the completion popup menu
*/
private void computeKeywordProposal(List<ICompletionProposal> proposals, String start,
String replacementStringBefore, String replacementStringAfter, Image keywordImage) {
String replacementString = replacementStringBefore + replacementStringAfter;
if (replacementString.toLowerCase().startsWith(start.toLowerCase())) {
proposals.add(createCompletionProposal(replacementString, offset - start.length(),
start.length(), replacementStringBefore.length(), keywordImage, replacementString, null,
replacementString));
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer,
* int)
*/
public IContextInformation[] computeContextInformation(ITextViewer viewer, int documentOffset) {
return null;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
*/
public char[] getCompletionProposalAutoActivationCharacters() {
// TODO JMU should return a copy of the array to avoid accidental modifications.
return AUTO_ACTIVATION_CHARACTERS;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters()
*/
public char[] getContextInformationAutoActivationCharacters() {
return null;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator()
*/
public IContextInformationValidator getContextInformationValidator() {
return null;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage()
*/
public String getErrorMessage() {
return null;
}
/**
* Creates a new template completion proposal. All fields are initialized based on the provided
* information. The replacement string contains the variables like in the following example : this.${name}
* = ${value}.
*
* @param replacementString
* the actual string to be inserted into the document, it contains the variables ${name}
* @param replacementOffset
* the offset of the text to be replaced
* @param replacementLength
* the length of the text to be replaced
* @param cursorPosition
* the position of the cursor following the insert relative to replacementOffset
* @param image
* the image to display for this proposal
* @param displayString
* the string to be displayed for the proposal
* @param contextInformation
* the context information associated with this proposal
* @param additionalProposalInfo
* the additional information associated with this proposal
*/
// CHECKSTYLE:OFF (8 parameters)
protected ICompletionProposal createTemplateProposal(String replacementString, int replacementOffset,
int replacementLength, int cursorPosition, Image image, String displayString,
IContextInformation contextInformation, String additionalProposalInfo) {
String info = additionalProposalInfo;
if (textViewer != null && textViewer.getDocument() != null) {
org.eclipse.jface.text.templates.Template template = new org.eclipse.jface.text.templates.Template(
displayString, displayString, AcceleoPartitionScanner.ACCELEO_BLOCK, replacementString,
true);
TemplateContextType type = new TemplateContextType(AcceleoPartitionScanner.ACCELEO_BLOCK,
AcceleoPartitionScanner.ACCELEO_BLOCK);
TemplateContext context = new DocumentTemplateContext(type, textViewer.getDocument(),
replacementOffset, replacementLength);
Region region = new Region(replacementOffset, replacementLength);
return new AcceleoCompletionTemplateProposal(template, context, region, image, info);
}
return createCompletionProposal(replacementString, replacementOffset, replacementLength,
cursorPosition, image, displayString, contextInformation, info);
}
/**
* Creates an instance of {@link CompletionProposal} given all of the mandatory information.
*
* @param replacementString
* the actual string to be inserted into the document.
* @param replacementOffset
* the offset of the text to be replaced.
* @param replacementLength
* the length of the text to be replaced.
* @param cursorPosition
* the position of the cursor following the insert relative to replacementOffset.
* @param image
* the image to display for this proposal.
* @param displayString
* the string to be displayed for the proposal.
* @param contextInformation
* the context information associated with this proposal.
* @param additionalProposalInfo
* the additional information associated with this proposal.
* @return The created completion proposal instance.
*/
protected ICompletionProposal createCompletionProposal(String replacementString, int replacementOffset,
int replacementLength, int cursorPosition, Image image, String displayString,
IContextInformation contextInformation, String additionalProposalInfo) {
return new AcceleoCompletionProposal(replacementString, replacementOffset, replacementLength,
cursorPosition, image, displayString, contextInformation, additionalProposalInfo);
}
// CHECKSTYLE:ON
/**
* This will be used to write the "offset" field in order to alter the point in the text where completion
* is demanded.
*
* @param newOffset
* The new offset.
*/
protected void setCompletionOffset(int newOffset) {
offset = newOffset;
}
/**
* This will be used to write the "text" field with the whole text for which completion is needed.
*
* @param viewer
* Viewer from which to retrieve the text.
*/
protected void setText(ITextViewer viewer) {
if (viewer != null) {
text = viewer.getDocument().get();
} else {
text = content.getText();
}
}
}