diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/AbstractContentAssistTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/AbstractContentAssistTest.java index dc41719be4b..b055f90177c 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/AbstractContentAssistTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/AbstractContentAssistTest.java @@ -65,6 +65,7 @@ public abstract class AbstractContentAssistTest extends BaseUITestCase { protected final static int IS_TEMPLATE = 0x02; protected final static int FILTER_RESULTS = 0x04; protected final static int ALLOW_EXTRA_RESULTS = 0x08; + protected final static int CHECK_ORDER = 0x10; protected final static int DEFAULT_FLAGS = FILTER_RESULTS; @@ -171,12 +172,15 @@ public abstract class AbstractContentAssistTest extends BaseUITestCase { boolean isTemplate = (flags & IS_TEMPLATE) != 0; boolean filterResults = (flags & FILTER_RESULTS) != 0; boolean allowExtraResults = (flags & ALLOW_EXTRA_RESULTS) != 0; + boolean checkOrder = (flags & CHECK_ORDER) != 0; ContentAssistResult r = invokeContentAssist(offset, length, isCompletion, isTemplate, filterResults); String[] resultStrings= toStringArray(r.results, compareType); - Arrays.sort(expected); - Arrays.sort(resultStrings); + if (!checkOrder) { + Arrays.sort(expected); + Arrays.sort(resultStrings); + } if (CTestPlugin.getDefault().isDebugging()) { System.out.println("Time: " + (r.endTime - r.startTime) + " ms"); diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTestBase.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTestBase.java index 52c7cba9085..51836253af7 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTestBase.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTestBase.java @@ -53,11 +53,19 @@ public class CompletionTestBase extends AbstractContentAssistTest { protected void assertMinimumCompletionResults(int offset, String[] expected, CompareType compareType) throws Exception { assertContentAssistResults(offset, expected, DEFAULT_FLAGS | ALLOW_EXTRA_RESULTS, compareType); } + + protected void assertOrderedCompletionResults(int offset, String[] expected, CompareType compareType) throws Exception { + assertContentAssistResults(offset, expected, DEFAULT_FLAGS | CHECK_ORDER, compareType); + } protected void assertCompletionResults(String[] expected) throws Exception { assertCompletionResults(fCursorOffset, expected, REPLACEMENT); } + protected void assertOrderedCompletionResults(String[] expected) throws Exception { + assertOrderedCompletionResults(fCursorOffset, expected, REPLACEMENT); + } + protected void assertParameterHint(String[] expected) throws Exception { assertContentAssistResults(fCursorOffset, expected, AbstractContentAssistTest.DEFAULT_FLAGS, CONTEXT); } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java index ffa8c00c04a..09b6af99549 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java @@ -908,6 +908,29 @@ public class CompletionTests extends CompletionTestBase { deleteDir(tempDir); } } + + //#include "/*cursor*/ + public void testHeaderFileWithNoExtension_292229() throws Exception { + File tempRoot= new File(System.getProperty("java.io.tmpdir")); + File tempDir= new File(tempRoot, "cdttest_292229"); + tempDir.mkdir(); + try { + createIncludeFiles(tempDir, new String[] { + "h1/bar", + "h1/foo.hpp" + }); + // A file like h1/bar which is not known to be a header should appear + // in the proposal list, but below files that are known to be headers + // like h1/foo.hpp. + String[] expected = { + "\"foo.hpp\"", + "\"bar\"" + }; + assertOrderedCompletionResults(expected); + } finally { + deleteDir(tempDir); + } + } public static void deleteDir(File dir) { File[] files = dir.listFiles(); diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/TemplateProposalTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/TemplateProposalTest.java index 4a40f0f7440..6edae0b2ce6 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/TemplateProposalTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/TemplateProposalTest.java @@ -19,7 +19,6 @@ import org.eclipse.jface.text.templates.persistence.TemplateStore; import org.eclipse.cdt.core.testplugin.util.BaseTestCase; import org.eclipse.cdt.ui.CUIPlugin; -import org.eclipse.cdt.ui.tests.text.contentassist2.AbstractContentAssistTest.CompareType; import org.eclipse.cdt.internal.corext.template.c.CContextType; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/InclusionProposalComputer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/InclusionProposalComputer.java index 82d892e5c9b..e1503601378 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/InclusionProposalComputer.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/InclusionProposalComputer.java @@ -125,6 +125,16 @@ public class InclusionProposalComputer implements ICompletionProposalComputer { return false; } + private static class PotentialInclude { + public String fPath; + public boolean fKnownContentType; // true for directories and files known to be headers + + public PotentialInclude(String path, boolean knownContentType) { + fPath = path; + fKnownContentType = knownContentType; + } + } + private void addInclusionProposals(CContentAssistInvocationContext context, List proposals) throws Exception { if (context.isContextInformationStyle()) { return; @@ -141,7 +151,7 @@ public class InclusionProposalComputer implements ICompletionProposalComputer { prefix= prefix.substring(1); } IPath prefixPath= new Path(prefix); - String[] potentialIncludes= collectIncludeFiles(tu, prefixPath, angleBrackets); + PotentialInclude[] potentialIncludes= collectIncludeFiles(tu, prefixPath, angleBrackets); if (potentialIncludes.length > 0) { IInclude[] includes= tu.getIncludes(); Set alreadyIncluded= new HashSet(); @@ -149,11 +159,11 @@ public class InclusionProposalComputer implements ICompletionProposalComputer { alreadyIncluded.add(includeDirective.getElementName()); } Image image = getImage(CElementImageProvider.getIncludeImageDescriptor()); - for (String include : potentialIncludes) { - if (alreadyIncluded.add(include)) { + for (PotentialInclude include : potentialIncludes) { + if (alreadyIncluded.add(include.fPath)) { final char openingBracket= angleBrackets ? '<' : '"'; final char closingBracket= angleBrackets ? '>' : '"'; - String repString= openingBracket + include; + String repString= openingBracket + include.fPath; final String dispString= repString + closingBracket; int repLength = prefix.length() + 1; int repOffset= context.getInvocationOffset() - repLength; @@ -161,7 +171,7 @@ public class InclusionProposalComputer implements ICompletionProposalComputer { if (needClosingBracket) { repString += closingBracket; } - final boolean isDir= include.endsWith("/"); //$NON-NLS-1$ + final boolean isDir= include.fPath.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) { @@ -183,8 +193,8 @@ public class InclusionProposalComputer implements ICompletionProposalComputer { * @return an array of incude file names * @throws CoreException */ - private String[] collectIncludeFiles(final ITranslationUnit tu, IPath prefixPath, boolean angleBrackets) throws CoreException { - final List includeFiles= new ArrayList(); + private PotentialInclude[] collectIncludeFiles(final ITranslationUnit tu, IPath prefixPath, boolean angleBrackets) throws CoreException { + final List includeFiles= new ArrayList<>(); if (!angleBrackets) { // search in current directory IResource resource= tu.getResource(); @@ -202,7 +212,7 @@ public class InclusionProposalComputer implements ICompletionProposalComputer { if (info != null) { collectIncludeFilesFromScannerInfo(tu, info, prefixPath, angleBrackets, includeFiles); } - return includeFiles.toArray(new String[includeFiles.size()]); + return includeFiles.toArray(new PotentialInclude[includeFiles.size()]); } /** @@ -212,7 +222,8 @@ public class InclusionProposalComputer implements ICompletionProposalComputer { * @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 includeFiles) { + private void collectIncludeFilesFromScannerInfo(ITranslationUnit tu, IScannerInfo info, IPath prefixPath, + boolean angleBrackets, List includeFiles) { if (!angleBrackets && info instanceof IExtendedScannerInfo) { IExtendedScannerInfo extendedInfo= (IExtendedScannerInfo) info; String[] quoteIncludes= extendedInfo.getLocalIncludePath(); @@ -240,7 +251,8 @@ public class InclusionProposalComputer implements ICompletionProposalComputer { * @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 includeFiles) { + private void collectIncludeFilesFromDirectory(ITranslationUnit tu, IPath directory, IPath prefixPath, + List includeFiles) { final String namePrefix; if (prefixPath.segmentCount() == 0) { namePrefix= ""; //$NON-NLS-1$ @@ -269,17 +281,28 @@ public class InclusionProposalComputer implements ICompletionProposalComputer { for (File file : files) { final String name= file.getName(); if (name.length() >= prefixLength && matcher.match(name.toCharArray())) { - if (file.isFile()) { - if (CoreModel.isValidCXXHeaderUnitName(project, name) || CoreModel.isValidCHeaderUnitName(project, name)) { - includeFiles.add(prefixPath.append(name).toString()); - } - } else if (file.isDirectory()) { - includeFiles.add(prefixPath.append(name).addTrailingSeparator().toString()); - } + maybeAddInclude(prefixPath, includeFiles, project, file.isFile(), file.isDirectory(), name); } } } + private static void maybeAddInclude(IPath prefixPath, List includeFiles, + final IProject project, boolean isFile, boolean isDirectory, final String name) { + if (isFile) { + boolean definitelyHeader = CoreModel.isValidHeaderUnitName(project, name); + boolean definitelyNotHeader = false; + if (!definitelyHeader) { + definitelyNotHeader = CoreModel.isValidSourceUnitName(project, name) || name.startsWith("."); //$NON-NLS-1$ + } + if (definitelyHeader || !definitelyNotHeader) { + includeFiles.add(new PotentialInclude(prefixPath.append(name).toString(), definitelyHeader)); + } + } else if (isDirectory) { + includeFiles.add(new PotentialInclude( + prefixPath.append(name).addTrailingSeparator().toString(), true)); + } + } + /** * Collect include files from the given resource container. * @@ -289,7 +312,8 @@ public class InclusionProposalComputer implements ICompletionProposalComputer { * @param includeFiles the result list * @throws CoreException */ - private void collectIncludeFilesFromContainer(final ITranslationUnit tu, IContainer parent, IPath prefixPath, final List includeFiles) throws CoreException { + private void collectIncludeFilesFromContainer(final ITranslationUnit tu, IContainer parent, + IPath prefixPath, final List includeFiles) throws CoreException { final String namePrefix; if (prefixPath.segmentCount() == 0) { namePrefix= ""; //$NON-NLS-1$ @@ -328,13 +352,8 @@ public class InclusionProposalComputer implements ICompletionProposalComputer { return true; } if (name.length() >= prefixLength && matcher.match(name.toCharArray())) { - if (type == IResource.FILE) { - if (CoreModel.isValidCXXHeaderUnitName(project, name) || CoreModel.isValidCHeaderUnitName(project, name)) { - includeFiles.add(cPrefixPath.append(name).toString()); - } - } else if (type == IResource.FOLDER) { - includeFiles.add(cPrefixPath.append(name).addTrailingSeparator().toString()); - } + maybeAddInclude(cPrefixPath, includeFiles, project, type == IResource.FILE, + type == IResource.FOLDER, name); } return false; }}, IResource.DEPTH_ONE); @@ -369,12 +388,15 @@ public class InclusionProposalComputer implements ICompletionProposalComputer { * @param match the matching identifier * @return a relevance value inidicating the quality of the name match */ - protected int computeRelevance(String prefix, String match) { + protected int computeRelevance(String prefix, PotentialInclude match) { int baseRelevance= 0; - boolean caseMatch= prefix.length() > 0 && match.startsWith(prefix); + boolean caseMatch= prefix.length() > 0 && match.fPath.startsWith(prefix); if (caseMatch) { baseRelevance += RelevanceConstants.CASE_MATCH_RELEVANCE; } + if (match.fKnownContentType) { + baseRelevance += RelevanceConstants.FILE_TYPE_RELEVANCE; + } return baseRelevance; } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/RelevanceConstants.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/RelevanceConstants.java index e31ebc08023..a474def2bf3 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/RelevanceConstants.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/RelevanceConstants.java @@ -12,6 +12,12 @@ package org.eclipse.cdt.internal.ui.text.contentassist; public interface RelevanceConstants { + /** + * For inclusion proposals only, relevance increment for matches + * with a file type known to be a header file. + */ + final int FILE_TYPE_RELEVANCE = 2000; + /** Relevance increment for same case matches */ final int CASE_MATCH_RELEVANCE = 1000;