mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
113568: [Content Assist] proposals for include directives
This commit is contained in:
parent
2d526547f2
commit
bfd296013f
8 changed files with 508 additions and 41 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2004, 2007 IBM Corporation and others.
|
* Copyright (c) 2004, 2008 IBM Corporation and others.
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
|
@ -15,14 +15,13 @@ package org.eclipse.cdt.ui.tests.text.contentassist2;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.eclipse.core.resources.IFile;
|
import org.eclipse.core.resources.IFile;
|
||||||
import org.eclipse.core.resources.IProject;
|
import org.eclipse.core.resources.IProject;
|
||||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||||
import org.eclipse.jface.text.IDocument;
|
import org.eclipse.jface.text.IDocument;
|
||||||
|
import org.eclipse.jface.text.TextUtilities;
|
||||||
import org.eclipse.jface.text.contentassist.ContentAssistant;
|
import org.eclipse.jface.text.contentassist.ContentAssistant;
|
||||||
import org.eclipse.jface.text.contentassist.ICompletionProposal;
|
import org.eclipse.jface.text.contentassist.ICompletionProposal;
|
||||||
import org.eclipse.jface.text.contentassist.IContextInformation;
|
import org.eclipse.jface.text.contentassist.IContextInformation;
|
||||||
|
@ -34,17 +33,16 @@ import org.eclipse.ui.texteditor.ITextEditor;
|
||||||
import org.eclipse.cdt.core.CCorePlugin;
|
import org.eclipse.cdt.core.CCorePlugin;
|
||||||
import org.eclipse.cdt.core.dom.IPDOMManager;
|
import org.eclipse.cdt.core.dom.IPDOMManager;
|
||||||
import org.eclipse.cdt.core.model.ICProject;
|
import org.eclipse.cdt.core.model.ICProject;
|
||||||
import org.eclipse.cdt.core.parser.KeywordSetKey;
|
|
||||||
import org.eclipse.cdt.core.parser.ParserFactory;
|
|
||||||
import org.eclipse.cdt.core.parser.ParserLanguage;
|
|
||||||
import org.eclipse.cdt.core.testplugin.CProjectHelper;
|
import org.eclipse.cdt.core.testplugin.CProjectHelper;
|
||||||
import org.eclipse.cdt.ui.testplugin.CTestPlugin;
|
import org.eclipse.cdt.ui.testplugin.CTestPlugin;
|
||||||
import org.eclipse.cdt.ui.tests.BaseUITestCase;
|
import org.eclipse.cdt.ui.tests.BaseUITestCase;
|
||||||
import org.eclipse.cdt.ui.tests.text.EditorTestHelper;
|
import org.eclipse.cdt.ui.tests.text.EditorTestHelper;
|
||||||
import org.eclipse.cdt.ui.text.ICCompletionProposal;
|
import org.eclipse.cdt.ui.text.ICCompletionProposal;
|
||||||
|
import org.eclipse.cdt.ui.text.ICPartitions;
|
||||||
|
|
||||||
import org.eclipse.cdt.internal.ui.text.contentassist.CCompletionProposal;
|
import org.eclipse.cdt.internal.ui.text.contentassist.CCompletionProposal;
|
||||||
import org.eclipse.cdt.internal.ui.text.contentassist.CContentAssistProcessor;
|
import org.eclipse.cdt.internal.ui.text.contentassist.CContentAssistProcessor;
|
||||||
|
import org.eclipse.cdt.internal.ui.text.contentassist.RelevanceConstants;
|
||||||
|
|
||||||
public abstract class AbstractContentAssistTest extends BaseUITestCase {
|
public abstract class AbstractContentAssistTest extends BaseUITestCase {
|
||||||
|
|
||||||
|
@ -57,14 +55,6 @@ public abstract class AbstractContentAssistTest extends BaseUITestCase {
|
||||||
protected ITextEditor fEditor;
|
protected ITextEditor fEditor;
|
||||||
private boolean fIsCpp;
|
private boolean fIsCpp;
|
||||||
|
|
||||||
private final static Set fgAllKeywords= new HashSet();
|
|
||||||
|
|
||||||
static {
|
|
||||||
fgAllKeywords.addAll(ParserFactory.getKeywordSet(KeywordSetKey.KEYWORDS, ParserLanguage.C));
|
|
||||||
fgAllKeywords.addAll(ParserFactory.getKeywordSet(KeywordSetKey.TYPES, ParserLanguage.C));
|
|
||||||
fgAllKeywords.addAll(ParserFactory.getKeywordSet(KeywordSetKey.KEYWORDS, ParserLanguage.CPP));
|
|
||||||
fgAllKeywords.addAll(ParserFactory.getKeywordSet(KeywordSetKey.TYPES, ParserLanguage.CPP));
|
|
||||||
}
|
|
||||||
public AbstractContentAssistTest(String name, boolean isCpp) {
|
public AbstractContentAssistTest(String name, boolean isCpp) {
|
||||||
super(name);
|
super(name);
|
||||||
fIsCpp= isCpp;
|
fIsCpp= isCpp;
|
||||||
|
@ -111,7 +101,8 @@ public abstract class AbstractContentAssistTest extends BaseUITestCase {
|
||||||
|
|
||||||
//Call the CContentAssistProcessor
|
//Call the CContentAssistProcessor
|
||||||
ISourceViewer sourceViewer= EditorTestHelper.getSourceViewer((AbstractTextEditor)fEditor);
|
ISourceViewer sourceViewer= EditorTestHelper.getSourceViewer((AbstractTextEditor)fEditor);
|
||||||
String contentType = sourceViewer.getDocument().getContentType(offset);
|
String contentType= TextUtilities.getContentType(sourceViewer.getDocument(), ICPartitions.C_PARTITIONING, offset, true);
|
||||||
|
boolean isCode= IDocument.DEFAULT_CONTENT_TYPE.equals(contentType);
|
||||||
ContentAssistant assistant = new ContentAssistant();
|
ContentAssistant assistant = new ContentAssistant();
|
||||||
CContentAssistProcessor processor = new CContentAssistProcessor(fEditor, assistant, contentType);
|
CContentAssistProcessor processor = new CContentAssistProcessor(fEditor, assistant, contentType);
|
||||||
long startTime= System.currentTimeMillis();
|
long startTime= System.currentTimeMillis();
|
||||||
|
@ -121,7 +112,7 @@ public abstract class AbstractContentAssistTest extends BaseUITestCase {
|
||||||
long endTime= System.currentTimeMillis();
|
long endTime= System.currentTimeMillis();
|
||||||
assertTrue(results != null);
|
assertTrue(results != null);
|
||||||
|
|
||||||
results= filterResults(results);
|
results= filterResults(results, isCode);
|
||||||
String[] resultStrings= toStringArray(results, compareType);
|
String[] resultStrings= toStringArray(results, compareType);
|
||||||
Arrays.sort(expected);
|
Arrays.sort(expected);
|
||||||
Arrays.sort(resultStrings);
|
Arrays.sort(resultStrings);
|
||||||
|
@ -167,22 +158,31 @@ public abstract class AbstractContentAssistTest extends BaseUITestCase {
|
||||||
/**
|
/**
|
||||||
* Filter out template and keyword proposals.
|
* Filter out template and keyword proposals.
|
||||||
* @param results
|
* @param results
|
||||||
|
* @param isCodeCompletion completion is in code, not preprocessor, etc.
|
||||||
* @return filtered proposals
|
* @return filtered proposals
|
||||||
*/
|
*/
|
||||||
private Object[] filterResults(Object[] results) {
|
private Object[] filterResults(Object[] results, boolean isCodeCompletion) {
|
||||||
List filtered= new ArrayList();
|
List<Object> filtered= new ArrayList<Object>();
|
||||||
for (int i = 0; i < results.length; i++) {
|
for (int i = 0; i < results.length; i++) {
|
||||||
Object result = results[i];
|
Object result = results[i];
|
||||||
if (result instanceof TemplateProposal) {
|
if (result instanceof TemplateProposal) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (result instanceof ICCompletionProposal) {
|
if (result instanceof ICCompletionProposal) {
|
||||||
// check for keywords proposal
|
if (isCodeCompletion) {
|
||||||
if (fgAllKeywords.contains(((ICCompletionProposal)result).getDisplayString())) {
|
// check for keywords proposal
|
||||||
continue;
|
int relevance = ((ICCompletionProposal)result).getRelevance();
|
||||||
|
if (relevance >= RelevanceConstants.CASE_MATCH_RELEVANCE) {
|
||||||
|
relevance -= RelevanceConstants.CASE_MATCH_RELEVANCE;
|
||||||
|
}
|
||||||
|
if (relevance <= RelevanceConstants.KEYWORD_TYPE_RELEVANCE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
filtered.add(result);
|
||||||
|
} else if (result instanceof IContextInformation) {
|
||||||
|
filtered.add(result);
|
||||||
}
|
}
|
||||||
filtered.add(result);
|
|
||||||
}
|
}
|
||||||
return filtered.toArray();
|
return filtered.toArray();
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ public abstract class AbstractContentAssistTest extends BaseUITestCase {
|
||||||
String[] strings= new String[results.length];
|
String[] strings= new String[results.length];
|
||||||
for(int i=0; i< results.length; i++){
|
for(int i=0; i< results.length; i++){
|
||||||
Object result = results[i];
|
Object result = results[i];
|
||||||
if (result instanceof ICompletionProposal) {
|
if (result instanceof CCompletionProposal) {
|
||||||
if (compareType == COMPARE_ID_STRINGS) {
|
if (compareType == COMPARE_ID_STRINGS) {
|
||||||
strings[i]= ((CCompletionProposal)result).getIdString();
|
strings[i]= ((CCompletionProposal)result).getIdString();
|
||||||
} else if (compareType == COMPARE_DISP_STRINGS) {
|
} else if (compareType == COMPARE_DISP_STRINGS) {
|
||||||
|
@ -199,8 +199,20 @@ public abstract class AbstractContentAssistTest extends BaseUITestCase {
|
||||||
} else {
|
} else {
|
||||||
strings[i]= ((CCompletionProposal)result).getReplacementString();
|
strings[i]= ((CCompletionProposal)result).getReplacementString();
|
||||||
}
|
}
|
||||||
} else {
|
} else if (result instanceof ICCompletionProposal) {
|
||||||
|
if (compareType == COMPARE_ID_STRINGS) {
|
||||||
|
strings[i]= ((ICCompletionProposal)result).getIdString();
|
||||||
|
} else if (compareType == COMPARE_DISP_STRINGS) {
|
||||||
|
strings[i]= ((ICCompletionProposal)result).getDisplayString();
|
||||||
|
} else {
|
||||||
|
strings[i]= ((ICCompletionProposal)result).getDisplayString();
|
||||||
|
}
|
||||||
|
} else if (result instanceof ICompletionProposal) {
|
||||||
|
strings[i]= ((ICompletionProposal)result).getDisplayString();
|
||||||
|
} else if (result instanceof IContextInformation) {
|
||||||
strings[i]= ((IContextInformation)result).getContextDisplayString();
|
strings[i]= ((IContextInformation)result).getContextDisplayString();
|
||||||
|
} else {
|
||||||
|
strings[i]= result.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return strings;
|
return strings;
|
||||||
|
|
|
@ -13,6 +13,11 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.ui.tests.text.contentassist2;
|
package org.eclipse.cdt.ui.tests.text.contentassist2;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import junit.framework.Test;
|
import junit.framework.Test;
|
||||||
|
|
||||||
import org.eclipse.core.resources.IFile;
|
import org.eclipse.core.resources.IFile;
|
||||||
|
@ -20,6 +25,7 @@ import org.eclipse.core.resources.IProject;
|
||||||
import org.eclipse.jface.text.IDocument;
|
import org.eclipse.jface.text.IDocument;
|
||||||
|
|
||||||
import org.eclipse.cdt.core.CCorePlugin;
|
import org.eclipse.cdt.core.CCorePlugin;
|
||||||
|
import org.eclipse.cdt.core.testplugin.TestScannerProvider;
|
||||||
import org.eclipse.cdt.core.testplugin.util.BaseTestCase;
|
import org.eclipse.cdt.core.testplugin.util.BaseTestCase;
|
||||||
import org.eclipse.cdt.core.testplugin.util.TestSourceReader;
|
import org.eclipse.cdt.core.testplugin.util.TestSourceReader;
|
||||||
|
|
||||||
|
@ -951,4 +957,75 @@ public class CompletionTests extends AbstractContentAssistTest {
|
||||||
};
|
};
|
||||||
assertCompletionResults(fCursorOffset, expected, AbstractContentAssistTest.COMPARE_REP_STRINGS);
|
assertCompletionResults(fCursorOffset, expected, AbstractContentAssistTest.COMPARE_REP_STRINGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#include "/*cursor*/
|
||||||
|
public void testInclusionProposals_bug113568() throws Exception {
|
||||||
|
File tempRoot= new File(System.getProperty("java.io.tmpdir"));
|
||||||
|
File tempDir= new File(tempRoot, "cdttest_113568");
|
||||||
|
tempDir.mkdir();
|
||||||
|
try {
|
||||||
|
createIncludeFiles(tempDir, new String[] {
|
||||||
|
"h1/inc1.h",
|
||||||
|
"h1/sub1/inc11.h",
|
||||||
|
"h2/inc2.h"
|
||||||
|
});
|
||||||
|
String[] expected= {
|
||||||
|
"\"inc1.h\"",
|
||||||
|
"\"sub1/\"",
|
||||||
|
"\"inc2.h\""
|
||||||
|
};
|
||||||
|
assertCompletionResults(fCursorOffset, expected, AbstractContentAssistTest.COMPARE_REP_STRINGS);
|
||||||
|
|
||||||
|
getDocument().replace(fCursorOffset++, 0, "i");
|
||||||
|
expected= new String[] {
|
||||||
|
"\"inc1.h\"",
|
||||||
|
"\"inc2.h\""
|
||||||
|
};
|
||||||
|
assertCompletionResults(fCursorOffset, expected, AbstractContentAssistTest.COMPARE_REP_STRINGS);
|
||||||
|
|
||||||
|
getDocument().replace(fCursorOffset, 0, "\"");
|
||||||
|
expected= new String[] {
|
||||||
|
"\"inc1.h",
|
||||||
|
"\"inc2.h"
|
||||||
|
};
|
||||||
|
assertCompletionResults(fCursorOffset, expected, AbstractContentAssistTest.COMPARE_REP_STRINGS);
|
||||||
|
|
||||||
|
getDocument().replace(fCursorOffset-1, 1, "sub1/");
|
||||||
|
expected= new String[] {
|
||||||
|
"\"sub1/inc11.h"
|
||||||
|
};
|
||||||
|
assertCompletionResults(fCursorOffset+=4, expected, AbstractContentAssistTest.COMPARE_REP_STRINGS);
|
||||||
|
} finally {
|
||||||
|
deleteDir(tempDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void deleteDir(File dir) {
|
||||||
|
File[] files = dir.listFiles();
|
||||||
|
for (int i = 0; i < files.length; i++) {
|
||||||
|
if (files[i].isDirectory()) {
|
||||||
|
deleteDir(files[i]);
|
||||||
|
} else {
|
||||||
|
files[i].delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dir.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void createIncludeFiles(File dir, String[] files) throws IOException {
|
||||||
|
Set<String> includeDirs= new HashSet<String>();
|
||||||
|
for (int i = 0; i < files.length; i++) {
|
||||||
|
File file = new File(dir, files[i]);
|
||||||
|
final File parentFile= file.getParentFile();
|
||||||
|
if (parentFile.getName().startsWith("sub")) {
|
||||||
|
if (!parentFile.exists()) {
|
||||||
|
parentFile.mkdirs();
|
||||||
|
}
|
||||||
|
} else if (includeDirs.add(parentFile.getAbsolutePath())) {
|
||||||
|
parentFile.mkdirs();
|
||||||
|
}
|
||||||
|
file.createNewFile();
|
||||||
|
}
|
||||||
|
TestScannerProvider.sIncludes= (String[]) includeDirs.toArray(new String[includeDirs.size()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2184,6 +2184,15 @@
|
||||||
<partition type="__c_preprocessor"/>
|
<partition type="__c_preprocessor"/>
|
||||||
</completionProposalComputer>
|
</completionProposalComputer>
|
||||||
</extension>
|
</extension>
|
||||||
|
<extension
|
||||||
|
id="InclusionProposalComputer"
|
||||||
|
point="org.eclipse.cdt.ui.completionProposalComputer">
|
||||||
|
<completionProposalComputer
|
||||||
|
categoryId="org.eclipse.cdt.ui.parserProposalCategory"
|
||||||
|
class="org.eclipse.cdt.internal.ui.text.contentassist.InclusionProposalComputer">
|
||||||
|
<partition type="__c_preprocessor"/>
|
||||||
|
</completionProposalComputer>
|
||||||
|
</extension>
|
||||||
|
|
||||||
<extension
|
<extension
|
||||||
id="KeywordCompletionProposalComputer"
|
id="KeywordCompletionProposalComputer"
|
||||||
|
|
|
@ -169,7 +169,7 @@ public class CCompletionProposal implements ICCompletionProposal, ICompletionPro
|
||||||
Assert.isTrue(cursorPosition >= 0);
|
Assert.isTrue(cursorPosition >= 0);
|
||||||
fCursorPosition= cursorPosition;
|
fCursorPosition= cursorPosition;
|
||||||
fContextInformationPosition= (fContextInformation != null ? fCursorPosition : -1);
|
fContextInformationPosition= (fContextInformation != null ? fCursorPosition : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @see ICompletionProposalExtension#apply(IDocument, char, int)
|
* @see ICompletionProposalExtension#apply(IDocument, char, int)
|
||||||
|
@ -478,11 +478,7 @@ public class CCompletionProposal implements ICCompletionProposal, ICompletionPro
|
||||||
if (offset < fReplacementOffset)
|
if (offset < fReplacementOffset)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
boolean validated= startsWith(document, offset, fReplacementString);
|
||||||
* See http://dev.eclipse.org/bugs/show_bug.cgi?id=17667
|
|
||||||
String word= fReplacementString;
|
|
||||||
*/
|
|
||||||
boolean validated= startsWith(document, offset, fDisplayString);
|
|
||||||
|
|
||||||
if (validated && event != null) {
|
if (validated && event != null) {
|
||||||
// adapt replacement range to document change
|
// adapt replacement range to document change
|
||||||
|
@ -515,7 +511,7 @@ public class CCompletionProposal implements ICCompletionProposal, ICompletionPro
|
||||||
*/
|
*/
|
||||||
protected boolean startsWith(IDocument document, int offset, String word) {
|
protected boolean startsWith(IDocument document, int offset, String word) {
|
||||||
int wordLength= word == null ? 0 : word.length();
|
int wordLength= word == null ? 0 : word.length();
|
||||||
if (offset > fReplacementOffset + wordLength)
|
if (offset >= fReplacementOffset + wordLength)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -282,7 +282,7 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer
|
||||||
} else if (binding instanceof IEnumerator) {
|
} else if (binding instanceof IEnumerator) {
|
||||||
proposals.add(createProposal(name, name, getImage(binding), baseRelevance + RelevanceConstants.ENUMERATOR_TYPE_RELEVANCE, cContext));
|
proposals.add(createProposal(name, name, getImage(binding), baseRelevance + RelevanceConstants.ENUMERATOR_TYPE_RELEVANCE, cContext));
|
||||||
} else {
|
} else {
|
||||||
proposals.add(createProposal(name, name, getImage(binding), baseRelevance, cContext));
|
proposals.add(createProposal(name, name, getImage(binding), baseRelevance + RelevanceConstants.DEFAULT_TYPE_RELEVANCE, cContext));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,373 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2008 Wind River Systems, Inc. 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:
|
||||||
|
* Anton Leherbauer (Wind River Systems) - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.internal.ui.text.contentassist;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.core.resources.IContainer;
|
||||||
|
import org.eclipse.core.resources.IProject;
|
||||||
|
import org.eclipse.core.resources.IResource;
|
||||||
|
import org.eclipse.core.resources.IResourceProxy;
|
||||||
|
import org.eclipse.core.resources.IResourceProxyVisitor;
|
||||||
|
import org.eclipse.core.runtime.CoreException;
|
||||||
|
import org.eclipse.core.runtime.IPath;
|
||||||
|
import org.eclipse.core.runtime.IProgressMonitor;
|
||||||
|
import org.eclipse.core.runtime.Path;
|
||||||
|
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.swt.graphics.Image;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.core.model.CoreModel;
|
||||||
|
import org.eclipse.cdt.core.model.IInclude;
|
||||||
|
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||||
|
import org.eclipse.cdt.core.parser.IExtendedScannerInfo;
|
||||||
|
import org.eclipse.cdt.core.parser.IScannerInfo;
|
||||||
|
import org.eclipse.cdt.ui.CUIPlugin;
|
||||||
|
import org.eclipse.cdt.ui.text.ICPartitions;
|
||||||
|
import org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext;
|
||||||
|
import org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.internal.ui.viewsupport.CElementImageProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A proposal computer for include directives.
|
||||||
|
*
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
public class InclusionProposalComputer implements ICompletionProposalComputer {
|
||||||
|
|
||||||
|
private String fErrorMessage;
|
||||||
|
|
||||||
|
public List computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) {
|
||||||
|
List<CCompletionProposal> proposals= Collections.emptyList();
|
||||||
|
fErrorMessage= null;
|
||||||
|
|
||||||
|
if (context instanceof CContentAssistInvocationContext) {
|
||||||
|
CContentAssistInvocationContext cContext= (CContentAssistInvocationContext) context;
|
||||||
|
if (inIncludeDirective(cContext)) {
|
||||||
|
// add include file proposals
|
||||||
|
proposals= new ArrayList<CCompletionProposal>();
|
||||||
|
try {
|
||||||
|
addInclusionProposals(cContext, proposals);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
fErrorMessage= exc.getMessage();
|
||||||
|
CUIPlugin.log(exc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proposals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrorMessage() {
|
||||||
|
return fErrorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sessionEnded() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sessionStarted() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether the invocation offset is inside the file name part if an include directive.
|
||||||
|
*
|
||||||
|
* @param context the invocation context
|
||||||
|
* @return <code>true</code> if the invocation offset is inside or before the directive keyword
|
||||||
|
*/
|
||||||
|
private boolean inIncludeDirective(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*include\\s*[\"<][^\">]*")) { //$NON-NLS-1$
|
||||||
|
// we are inside the file name part of the include directive
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (BadLocationException exc) {
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addInclusionProposals(CContentAssistInvocationContext context, List<CCompletionProposal> proposals) throws Exception {
|
||||||
|
if (context.isContextInformationStyle()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String prefix;
|
||||||
|
boolean angleBrackets= false;
|
||||||
|
prefix = computeIncludePrefix(context);
|
||||||
|
if (prefix.length() > 0) {
|
||||||
|
angleBrackets= prefix.charAt(0) == '<';
|
||||||
|
prefix= prefix.substring(1);
|
||||||
|
}
|
||||||
|
IPath prefixPath= new Path(prefix);
|
||||||
|
final ITranslationUnit tu= context.getTranslationUnit();
|
||||||
|
String[] potentialIncludes= collectIncludeFiles(tu, prefixPath, angleBrackets);
|
||||||
|
if (potentialIncludes.length > 0) {
|
||||||
|
IInclude[] includes= context.getTranslationUnit().getIncludes();
|
||||||
|
Set<String> alreadyIncluded= new HashSet<String>();
|
||||||
|
for (int i = 0; i < includes.length; i++) {
|
||||||
|
IInclude includeDirective= includes[i];
|
||||||
|
alreadyIncluded.add(includeDirective.getElementName());
|
||||||
|
}
|
||||||
|
Image image = getImage(CElementImageProvider.getIncludeImageDescriptor());
|
||||||
|
for (int i = 0; i < potentialIncludes.length; i++) {
|
||||||
|
String include= potentialIncludes[i];
|
||||||
|
if (alreadyIncluded.add(include)) {
|
||||||
|
final char openingBracket= angleBrackets ? '<' : '"';
|
||||||
|
final char closingBracket= angleBrackets ? '>' : '"';
|
||||||
|
String repString= openingBracket + include;
|
||||||
|
final String dispString= repString + closingBracket;
|
||||||
|
int repLength = prefix.length() + 1;
|
||||||
|
int repOffset= context.getInvocationOffset() - repLength;
|
||||||
|
final boolean needClosingBracket= context.getDocument().getChar(repOffset + repLength) != closingBracket;
|
||||||
|
if (needClosingBracket) {
|
||||||
|
repString += closingBracket;
|
||||||
|
}
|
||||||
|
final boolean isDir= include.endsWith("/"); //$NON-NLS-1$
|
||||||
|
final int relevance= computeRelevance(prefix, include) + (isDir ? 0 : 1);
|
||||||
|
final CCompletionProposal proposal= createProposal(repOffset, repLength, repString, dispString, image, relevance, context);
|
||||||
|
if (!isDir && !needClosingBracket) {
|
||||||
|
// put cursor behind closing bracket
|
||||||
|
proposal.setCursorPosition(repString.length() + 1);
|
||||||
|
}
|
||||||
|
proposals.add(proposal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect potential include files for the given translation unit.
|
||||||
|
*
|
||||||
|
* @param tu the translation unit to include the file
|
||||||
|
* @param prefixPath the path part to match the sub-directory and file name
|
||||||
|
* @param angleBrackets whether angle brackets enclose the include file name
|
||||||
|
* @return an array of incude file names
|
||||||
|
* @throws CoreException
|
||||||
|
*/
|
||||||
|
private String[] collectIncludeFiles(final ITranslationUnit tu, IPath prefixPath, boolean angleBrackets) throws CoreException {
|
||||||
|
final List<String> includeFiles= new ArrayList<String>();
|
||||||
|
if (!angleBrackets) {
|
||||||
|
// search in current directory
|
||||||
|
IResource resource= tu.getResource();
|
||||||
|
if (resource != null) {
|
||||||
|
IContainer parent= resource.getParent();
|
||||||
|
collectIncludeFilesFromContainer(tu, parent, prefixPath, includeFiles);
|
||||||
|
} else {
|
||||||
|
IPath location= tu.getLocation();
|
||||||
|
if (location != null) {
|
||||||
|
collectIncludeFilesFromDirectory(tu, location.removeLastSegments(1), prefixPath, includeFiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IScannerInfo info= tu.getScannerInfo(true);
|
||||||
|
if (info != null) {
|
||||||
|
collectIncludeFilesFromScannerInfo(tu, info, prefixPath, angleBrackets, includeFiles);
|
||||||
|
}
|
||||||
|
return includeFiles.toArray(new String[includeFiles.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tu the translation unit to include the file
|
||||||
|
* @param info the scanner info for this translation unit
|
||||||
|
* @param prefixPath the path part to match the sub-directory and file name
|
||||||
|
* @param angleBrackets whether angle brackets enclose the include file name
|
||||||
|
* @param includeFiles the result list
|
||||||
|
*/
|
||||||
|
private void collectIncludeFilesFromScannerInfo(ITranslationUnit tu, IScannerInfo info, IPath prefixPath, boolean angleBrackets, List<String> includeFiles) {
|
||||||
|
if (!angleBrackets && info instanceof IExtendedScannerInfo) {
|
||||||
|
IExtendedScannerInfo extendedInfo= (IExtendedScannerInfo) info;
|
||||||
|
String[] quoteIncludes= extendedInfo.getLocalIncludePath();
|
||||||
|
|
||||||
|
if (quoteIncludes != null) {
|
||||||
|
for (int i = 0; i < quoteIncludes.length; i++) {
|
||||||
|
IPath includeDir= new Path(quoteIncludes[i]);
|
||||||
|
collectIncludeFilesFromDirectory(tu, includeDir, prefixPath, includeFiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] allIncludes= info.getIncludePaths();
|
||||||
|
for (int i = 0; i < allIncludes.length; i++) {
|
||||||
|
IPath includeDir= new Path(allIncludes[i]);
|
||||||
|
collectIncludeFilesFromDirectory(tu, includeDir, prefixPath, includeFiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect include files from the given file system directory.
|
||||||
|
*
|
||||||
|
* @param tu the translation unit to include the file
|
||||||
|
* @param directory the file system path of the directory
|
||||||
|
* @param prefixPath the path part to match the sub-directory and file name
|
||||||
|
* @param includeFiles the result list
|
||||||
|
*/
|
||||||
|
private void collectIncludeFilesFromDirectory(ITranslationUnit tu, IPath directory, IPath prefixPath, List<String> includeFiles) {
|
||||||
|
final boolean isCpp= tu.isCXXLanguage();
|
||||||
|
final String namePrefix;
|
||||||
|
if (prefixPath.segmentCount() == 0) {
|
||||||
|
namePrefix= ""; //$NON-NLS-1$
|
||||||
|
} else if (prefixPath.hasTrailingSeparator()) {
|
||||||
|
namePrefix= ""; //$NON-NLS-1$
|
||||||
|
prefixPath= prefixPath.removeTrailingSeparator();
|
||||||
|
directory= directory.append(prefixPath);
|
||||||
|
} else {
|
||||||
|
namePrefix= prefixPath.lastSegment();
|
||||||
|
prefixPath= prefixPath.removeLastSegments(1);
|
||||||
|
if (prefixPath.segmentCount() > 0) {
|
||||||
|
directory= directory.append(prefixPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final File fileDir = directory.toFile();
|
||||||
|
if (!fileDir.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final int prefixLength = namePrefix.length();
|
||||||
|
final IProject project= tu.getCProject().getProject();
|
||||||
|
File[] files= fileDir.listFiles();
|
||||||
|
for (int i = 0; i < files.length; i++) {
|
||||||
|
File file = files[i];
|
||||||
|
final String name= file.getName();
|
||||||
|
if (name.length() >= prefixLength && namePrefix.equalsIgnoreCase(name.substring(0, prefixLength))) {
|
||||||
|
if (file.isFile()) {
|
||||||
|
if (isCpp) {
|
||||||
|
if (CoreModel.isValidCXXHeaderUnitName(project, name)) {
|
||||||
|
includeFiles.add(prefixPath.append(name).toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (CoreModel.isValidCHeaderUnitName(project, name)) {
|
||||||
|
includeFiles.add(prefixPath.append(name).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (file.isDirectory()) {
|
||||||
|
includeFiles.add(prefixPath.append(name).addTrailingSeparator().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect include files from the given resource container.
|
||||||
|
*
|
||||||
|
* @param tu the translation unit to include the file
|
||||||
|
* @param parent the resource container
|
||||||
|
* @param prefixPath the path part to match the sub-directory and file name
|
||||||
|
* @param includeFiles the result list
|
||||||
|
* @throws CoreException
|
||||||
|
*/
|
||||||
|
private void collectIncludeFilesFromContainer(final ITranslationUnit tu, IContainer parent, IPath prefixPath, final List<String> includeFiles) throws CoreException {
|
||||||
|
final boolean isCpp= tu.isCXXLanguage();
|
||||||
|
final String namePrefix;
|
||||||
|
if (prefixPath.segmentCount() == 0) {
|
||||||
|
namePrefix= ""; //$NON-NLS-1$
|
||||||
|
} else if (prefixPath.hasTrailingSeparator()) {
|
||||||
|
namePrefix= ""; //$NON-NLS-1$
|
||||||
|
prefixPath= prefixPath.removeTrailingSeparator();
|
||||||
|
parent= parent.getFolder(prefixPath);
|
||||||
|
} else {
|
||||||
|
namePrefix= prefixPath.lastSegment();
|
||||||
|
prefixPath= prefixPath.removeLastSegments(1);
|
||||||
|
if (prefixPath.segmentCount() > 0) {
|
||||||
|
parent= parent.getFolder(prefixPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!parent.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final IPath cPrefixPath= prefixPath;
|
||||||
|
final int prefixLength = namePrefix.length();
|
||||||
|
final IProject project= tu.getCProject().getProject();
|
||||||
|
parent.accept(new IResourceProxyVisitor() {
|
||||||
|
public boolean visit(IResourceProxy proxy) throws CoreException {
|
||||||
|
final String name= proxy.getName();
|
||||||
|
if (name.length() < prefixLength && namePrefix.equalsIgnoreCase(name.substring(0, prefixLength))) {
|
||||||
|
if (proxy.getType() == IResource.FILE) {
|
||||||
|
if (isCpp) {
|
||||||
|
if (CoreModel.isValidCXXHeaderUnitName(project, name)) {
|
||||||
|
includeFiles.add(cPrefixPath.append(name).toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (CoreModel.isValidCHeaderUnitName(project, name)) {
|
||||||
|
includeFiles.add(cPrefixPath.append(name).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (proxy.getType() == IResource.FOLDER) {
|
||||||
|
includeFiles.add(cPrefixPath.append(name).addTrailingSeparator().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the file name portion in an incomplete include directive.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @return the file name portion including the opening bracket or quote
|
||||||
|
* @throws BadLocationException
|
||||||
|
*/
|
||||||
|
private String computeIncludePrefix(CContentAssistInvocationContext context) throws BadLocationException {
|
||||||
|
IDocument document= context.getDocument();
|
||||||
|
if (document == null)
|
||||||
|
return null;
|
||||||
|
int end= context.getInvocationOffset();
|
||||||
|
int start= end;
|
||||||
|
while (--start >= 0) {
|
||||||
|
final char ch= document.getChar(start);
|
||||||
|
if (ch == '"' || ch == '<')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return document.get(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute base relevance depending on quality of name / prefix match.
|
||||||
|
*
|
||||||
|
* @param prefix the completion pefix
|
||||||
|
* @param match the matching identifier
|
||||||
|
* @return a relevance value inidicating the quality of the name match
|
||||||
|
*/
|
||||||
|
protected int computeRelevance(String prefix, String match) {
|
||||||
|
int baseRelevance= 0;
|
||||||
|
boolean caseMatch= prefix.length() > 0 && match.startsWith(prefix);
|
||||||
|
if (caseMatch) {
|
||||||
|
baseRelevance += RelevanceConstants.CASE_MATCH_RELEVANCE;
|
||||||
|
}
|
||||||
|
return baseRelevance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CCompletionProposal createProposal(int repOffset, int repLength, String repString, String dispString, Image image, int relevance, CContentAssistInvocationContext context) {
|
||||||
|
return new CCompletionProposal(repString, repOffset, repLength, image, dispString, dispString, relevance, context.getViewer());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Image getImage(ImageDescriptor desc) {
|
||||||
|
return desc != null ? CUIPlugin.getImageDescriptorRegistry().get(desc) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -103,15 +103,15 @@ public abstract class ParsingBasedProposalComputer implements ICompletionProposa
|
||||||
* @return a relevance value inidicating the quality of the name match
|
* @return a relevance value inidicating the quality of the name match
|
||||||
*/
|
*/
|
||||||
protected int computeBaseRelevance(String prefix, String match) {
|
protected int computeBaseRelevance(String prefix, String match) {
|
||||||
int baseRelevance= RelevanceConstants.DEFAULT_TYPE_RELEVANCE;
|
|
||||||
boolean caseMatch= prefix.length() > 0 && match.startsWith(prefix);
|
boolean caseMatch= prefix.length() > 0 && match.startsWith(prefix);
|
||||||
if (caseMatch) {
|
if (caseMatch) {
|
||||||
baseRelevance += RelevanceConstants.CASE_MATCH_RELEVANCE;
|
return RelevanceConstants.CASE_MATCH_RELEVANCE;
|
||||||
|
} else {
|
||||||
|
boolean exactNameMatch= match.equalsIgnoreCase(prefix);
|
||||||
|
if (exactNameMatch) {
|
||||||
|
return RelevanceConstants.EXACT_NAME_MATCH_RELEVANCE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
boolean exactNameMatch= match.equalsIgnoreCase(prefix);
|
return 0;
|
||||||
if (exactNameMatch) {
|
|
||||||
baseRelevance += RelevanceConstants.EXACT_NAME_MATCH_RELEVANCE;
|
|
||||||
}
|
|
||||||
return baseRelevance;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ public interface RelevanceConstants {
|
||||||
final int NAMESPACE_TYPE_RELEVANCE = 50;
|
final int NAMESPACE_TYPE_RELEVANCE = 50;
|
||||||
final int ENUMERATOR_TYPE_RELEVANCE = 40;
|
final int ENUMERATOR_TYPE_RELEVANCE = 40;
|
||||||
final int ENUMERATION_TYPE_RELEVANCE = 30;
|
final int ENUMERATION_TYPE_RELEVANCE = 30;
|
||||||
final int MACRO_TYPE_RELEVANCE = 20;
|
final int DEFAULT_TYPE_RELEVANCE = 20;
|
||||||
|
final int MACRO_TYPE_RELEVANCE = 15;
|
||||||
|
|
||||||
/** Relevance constant for (key-)word proposals */
|
/** Relevance constant for (key-)word proposals */
|
||||||
final int KEYWORD_TYPE_RELEVANCE = 10;
|
final int KEYWORD_TYPE_RELEVANCE = 10;
|
||||||
|
@ -42,5 +43,4 @@ public interface RelevanceConstants {
|
||||||
/** Relevance constant for editor template proposals */
|
/** Relevance constant for editor template proposals */
|
||||||
final int TEMPLATE_TYPE_RELEVANCE = 5;
|
final int TEMPLATE_TYPE_RELEVANCE = 5;
|
||||||
|
|
||||||
final int DEFAULT_TYPE_RELEVANCE = 0;
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue