/*******************************************************************************
* Copyright (c) 2007, 2008 QNX Software Systems and others.
* 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:
* Bryan Wilkinson (QNX) - Initial API and implementation
* Anton Leherbauer (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.text.contentassist;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.swt.graphics.Image;
import org.eclipse.cdt.core.dom.ast.IASTCompletionNode;
import org.eclipse.cdt.core.dom.ast.IASTFieldReference;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.model.ICLanguageKeywords;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.cdt.internal.ui.viewsupport.CElementImageProvider;
public class KeywordCompletionProposalComputer extends ParsingBasedProposalComputer {
private static final int MIN_KEYWORD_LENGTH = 5;
@Override
protected List<ICompletionProposal> computeCompletionProposals(
CContentAssistInvocationContext context,
IASTCompletionNode completionNode, String prefix)
throws CoreException {
if (prefix.length() == 0) {
try {
prefix= context.computeIdentifierPrefix().toString();
} catch (BadLocationException exc) {
CUIPlugin.log(exc);
}
}
final int prefixLength = prefix.length();
// No prefix, no completions
if (prefixLength == 0 || context.isContextInformationStyle())
return Collections.emptyList();
// keywords are matched case-sensitive
final int relevance = RelevanceConstants.CASE_MATCH_RELEVANCE + RelevanceConstants.KEYWORD_TYPE_RELEVANCE;
List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
ICLanguageKeywords languageKeywords = null;
ITranslationUnit tu = context.getTranslationUnit();
if(tu != null) {
ILanguage language = tu.getLanguage();
if(language != null)
languageKeywords = (ICLanguageKeywords) language.getAdapter(ICLanguageKeywords.class);
}
if(languageKeywords == null)
return Collections.emptyList();
if (inPreprocessorDirective(context)) {
// TODO split this into a separate proposal computer?
boolean needDirectiveKeyword= inPreprocessorKeyword(context);
// add matching preprocessor keyword proposals
ImageDescriptor imagedesc = CElementImageProvider.getKeywordImageDescriptor();
Image image = imagedesc != null ? CUIPlugin.getImageDescriptorRegistry().get(imagedesc) : null;
for(String keyword : languageKeywords.getPreprocessorKeywords()) {
if (keyword.startsWith(prefix) && keyword.length() > prefixLength) {
String repString = keyword + ' ';
int repLength = prefixLength;
int repOffset = context.getInvocationOffset() - repLength;
if (prefix.charAt(0) == '#') {
// strip leading '#' from replacement
repLength--;
repOffset++;
repString= repString.substring(1);
} else if (needDirectiveKeyword) {
continue;
}
proposals.add(new CCompletionProposal(repString, repOffset,
repLength, image, keyword, relevance, context.getViewer()));
}
}
} else {
if (!isValidContext(completionNode))
return Collections.emptyList();
// add matching keyword proposals
ImageDescriptor imagedesc = CElementImageProvider.getKeywordImageDescriptor();
Image image = imagedesc != null ? CUIPlugin.getImageDescriptorRegistry().get(imagedesc) : null;
for(String keyword : languageKeywords.getKeywords()) {
if (keyword.startsWith(prefix) && keyword.length() > prefixLength && keyword.length() >= MIN_KEYWORD_LENGTH) {
int repLength = prefixLength;
int repOffset = context.getInvocationOffset() - repLength;
proposals.add(new CCompletionProposal(keyword, repOffset,
repLength, image, keyword, relevance, context.getViewer()));
}
}
}
return proposals;
}
/**
* Checks whether the given invocation context looks valid for template completion.
*
* @param context the content assist invocation context
* @return <code>false</code> if the given invocation context looks like a field reference
*/
private boolean isValidContext(IASTCompletionNode completionNode) {
IASTName[] names = completionNode.getNames();
for (int i = 0; i < names.length; ++i) {
IASTName name = names[i];
// ignore if not connected
if (name.getTranslationUnit() == null)
continue;
// ignore if this is a member access
if (name.getParent() instanceof IASTFieldReference)
continue;
return true;
}
return false;
}
/**
* Test whether the invocation offset is inside or before the preprocessor directive keyword.
*
* @param context the invocation context
* @return <code>true</code> if the invocation offset is inside or before the directive keyword
*/
private boolean inPreprocessorKeyword(CContentAssistInvocationContext context) {
IDocument doc = context.getDocument();
int offset = context.getInvocationOffset();
try {
final ITypedRegion partition= TextUtilities.getPartition(doc, ICPartitions.C_PARTITIONING, offset, true);
if (ICPartitions.C_PREPROCESSOR.equals(partition.getType())) {
String ppPrefix= doc.get(partition.getOffset(), offset - partition.getOffset());
if (ppPrefix.matches("\\s*#\\s*\\w*")) { //$NON-NLS-1$
// we are inside the directive keyword
return true;
}
}
} catch (BadLocationException exc) {
}
return false;
}
/**
* Check if the invocation offset is inside a preprocessor directive.
*
* @param context the content asist invocation context
* @return <code>true</code> if invocation offset is inside a preprocessor directive
*/
private boolean inPreprocessorDirective(CContentAssistInvocationContext context) {
IDocument doc = context.getDocument();
int offset = context.getInvocationOffset();
try {
final ITypedRegion partition= TextUtilities.getPartition(doc, ICPartitions.C_PARTITIONING, offset, true);
if (ICPartitions.C_PREPROCESSOR.equals(partition.getType())) {
return true;
}
} catch (BadLocationException exc) {
}
return false;
}
}