/*******************************************************************************
* Copyright (c) 2011 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.mylyn.docs.intent.client.ui.editor.completion;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.templates.TemplateProposal;
import org.eclipse.mylyn.docs.intent.client.ui.editor.IntentPairMatcher;
import org.eclipse.mylyn.docs.intent.client.ui.editor.scanner.IntentPartitionScanner;
import org.eclipse.mylyn.docs.intent.client.ui.logger.IntentUiLogger;
import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.RepositoryAdapter;
/**
* Computes the completion proposals.
*
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
*/
public class IntentCompletionProcessor extends AbstractIntentCompletionProcessor {
// Patterns by contexts.
/**
* All patterns allowing to detect the current context.
*/
public static final Pattern[] PATTERNS_BY_CONTEXT = new Pattern[] {
Pattern.compile("Document\\s+[^{\r\n]*\\{"), Pattern.compile("Chapter\\s+[^{\r\n]*\\{"),
Pattern.compile("Section\\s+[^{\r\n]*\\{"),
};
// Keywords by contexts.
// CHECKSTYLE:OFF Keywords have the same name than templates names & templates description, but merging
// their declaration have no sense.
private static final String[][] KEYWORDS_BY_CONTEXT = new String[][] {
// Document-level keywords
new String[] {"Chapter",
},
// Chapter-level keywords
new String[] {"Section",
},
// Section-level keywords
new String[] {"Section", "@M",
},
};
// CHECKSTYLE:ON
// Accurate contexts.
/**
* Constant representing default context.
*/
private static final int NULL_CONTEXT = -1;
/**
* Constant representing document context.
*/
private static final int DOCUMENT_CONTEXT = 0;
/**
* Constant representing chapter context.
*/
private static final int CHAPTER_CONTEXT = 1;
/**
* Constant representing section context.
*/
private static final int SECTION_CONTEXT = 2;
/**
* Current context ID.
*/
private int accurateContext;
/**
* Block matcher used to determine contexts.
*/
private IntentPairMatcher blockMatcher;
/**
* Completion processor for pure documentation zones.
*/
private MarkupCompletionProcessor markupCompletionProcessor;
/**
* Completion processor for description units.
*/
private DescriptionUnitCompletionProcessor descriptionUnitCompletionProcessor;
/**
* Creates a new {@link IntentCompletionProcessor} with the given {@link IntentPairMatcher}.
*
* @param matcher
* the block matcher
* @param repositoryAdapter
* the {@link RepositoryAdapter} used to interact with Intent repository
*/
public IntentCompletionProcessor(IntentPairMatcher matcher, RepositoryAdapter repositoryAdapter) {
super(repositoryAdapter);
this.blockMatcher = matcher;
this.markupCompletionProcessor = new MarkupCompletionProcessor(repositoryAdapter);
this.descriptionUnitCompletionProcessor = new DescriptionUnitCompletionProcessor(repositoryAdapter);
}
/**
* Computes the context at the current offset.
*
* @throws BadLocationException
* if the current offset is incorrect
*/
private void computeAccurateContext() throws BadLocationException {
int[] offsetsByContextType = new int[3];
final String startText = document.get(0, offset);
for (int i = 0; i < PATTERNS_BY_CONTEXT.length; i++) {
offsetsByContextType[i] = getLastIndexOf(startText, PATTERNS_BY_CONTEXT[i]);
}
int res = NULL_CONTEXT;
int maxValue = -1;
for (int i = 0; i < offsetsByContextType.length; i++) {
if (offsetsByContextType[i] > maxValue) {
IRegion region = blockMatcher.match(document, offsetsByContextType[i]);
if (region != null && region.getOffset() + region.getLength() > offset) {
maxValue = offsetsByContextType[i];
res = i;
}
}
}
accurateContext = res;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.mylyn.docs.intent.client.ui.editor.completion.AbstractIntentCompletionProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer,
* int)
*/
@Override
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int currentOffset) {
ArrayList<ICompletionProposal> proposals = Lists.newArrayList();
proposals.addAll(Lists.newArrayList(super.computeCompletionProposals(viewer, currentOffset)));
proposals.addAll(Lists.newArrayList(descriptionUnitCompletionProcessor.computeCompletionProposals(
viewer, currentOffset)));
proposals.addAll(Lists.newArrayList(markupCompletionProcessor.computeCompletionProposals(viewer,
currentOffset)));
return proposals.toArray(new ICompletionProposal[proposals.size()]);
}
/**
* Computes the completion proposals.
*
* @return the completion proposals
*/
protected ICompletionProposal[] computeCompletionProposals() {
try {
computeAccurateContext();
} catch (BadLocationException e) {
IntentUiLogger.logError(e);
}
List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
proposals.addAll(createKeyWordsProposals());
proposals.addAll(createTemplatesProposals());
return proposals.toArray(new ICompletionProposal[proposals.size()]);
}
/**
* Creates the keywords proposals.
*
* @return the keywords proposals
*/
protected Collection<ICompletionProposal> createKeyWordsProposals() {
List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
if (accurateContext >= 0) {
for (String keyword : KEYWORDS_BY_CONTEXT[accurateContext]) {
if (!"".equals(start) && keyword.startsWith(start)) {
proposals.add(createKeyWordProposal(keyword));
}
}
}
return proposals;
}
/**
* Creates the templates proposals.
*
* @return the templates proposals
*/
protected List<TemplateProposal> createTemplatesProposals() {
TemplateProposal chapterProposal = createTemplateProposal("Chapter", "Chapter",
"Chapter ${Title} {\n\t${Text}\n}\n", "icon/outline/chapter.gif");
TemplateProposal sectionProposal = createTemplateProposal("Section", "Section",
"Section ${Title} {\n\t${Text}\n}\n", "icon/outline/section.gif");
TemplateProposal modelingUnitProposal = createTemplateProposal("Modeling Unit", "Modeling Unit",
"@M\n${Code}\nM@\n", "icon/outline/modelingunit.png");
List<TemplateProposal> proposals = new ArrayList<TemplateProposal>();
switch (accurateContext) {
case DOCUMENT_CONTEXT:
proposals.add(chapterProposal);
break;
case CHAPTER_CONTEXT:
proposals.add(sectionProposal);
break;
case SECTION_CONTEXT:
proposals.add(sectionProposal);
proposals.add(modelingUnitProposal);
break;
default:
break;
}
return proposals;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.mylyn.docs.intent.client.ui.editor.completion.AbstractIntentCompletionProcessor#getContextType()
*/
@Override
public String getContextType() {
return IntentPartitionScanner.INTENT_DESCRIPTIONUNIT;
}
}