forked from qt-creator/qt-creator
CppEditor: Extract some functions in AddIncludeForUndefinedIdentifier
...to make the high level code path easier to follow. Change-Id: If0ff9a15c9de399286e3a7f59a571949ca8f04de Reviewed-by: Christian Stenger <christian.stenger@digia.com>
This commit is contained in:
@@ -1829,6 +1829,79 @@ void AddIncludeForUndefinedIdentifierOp::perform()
|
|||||||
insertNewIncludeDirective(m_include, file);
|
insertNewIncludeDirective(m_include, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
QString findShortestInclude(const QString currentDocumentFilePath,
|
||||||
|
const QString candidateFilePath,
|
||||||
|
const ProjectPart::HeaderPaths &headerPaths)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
|
||||||
|
const QFileInfo fileInfo(candidateFilePath);
|
||||||
|
|
||||||
|
if (fileInfo.path() == QFileInfo(currentDocumentFilePath).path()) {
|
||||||
|
result = QLatin1Char('"') + fileInfo.fileName() + QLatin1Char('"');
|
||||||
|
} else {
|
||||||
|
foreach (const ProjectPart::HeaderPath &headerPath, headerPaths) {
|
||||||
|
if (!candidateFilePath.startsWith(headerPath.path))
|
||||||
|
continue;
|
||||||
|
QString relativePath = candidateFilePath.mid(headerPath.path.size());
|
||||||
|
if (!relativePath.isEmpty() && relativePath.at(0) == QLatin1Char('/'))
|
||||||
|
relativePath = relativePath.mid(1);
|
||||||
|
if (result.isEmpty() || relativePath.size() + 2 < result.size())
|
||||||
|
result = QLatin1Char('<') + relativePath + QLatin1Char('>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString findIncludeForQtClass(const QString &className,
|
||||||
|
const ProjectPart::HeaderPaths &headerPaths,
|
||||||
|
bool classFoundInLocator)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
|
||||||
|
// for QSomething, propose a <QSomething> include -- if such a class was in the locator
|
||||||
|
if (classFoundInLocator) {
|
||||||
|
result = QLatin1Char('<') + className + QLatin1Char('>');
|
||||||
|
|
||||||
|
// otherwise, check for a header file with the same name in the Qt include paths
|
||||||
|
} else {
|
||||||
|
foreach (const ProjectPart::HeaderPath &headerPath, headerPaths) {
|
||||||
|
if (!headerPath.path.contains(QLatin1String("/Qt"))) // "QtCore", "QtGui" etc...
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const QString headerPathCandidate = headerPath.path + QLatin1Char('/') + className;
|
||||||
|
const QFileInfo fileInfo(headerPathCandidate);
|
||||||
|
if (fileInfo.exists() && fileInfo.isFile()) {
|
||||||
|
result = QLatin1Char('<') + className + QLatin1Char('>');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectPart::HeaderPaths relevantHeaderPaths(const QString &filePath)
|
||||||
|
{
|
||||||
|
ProjectPart::HeaderPaths headerPaths;
|
||||||
|
|
||||||
|
CppModelManager *modelManager = CppModelManager::instance();
|
||||||
|
const QList<ProjectPart::Ptr> projectParts = modelManager->projectPart(filePath);
|
||||||
|
if (projectParts.isEmpty()) { // Not part of any project, better use all include paths than none
|
||||||
|
headerPaths += modelManager->headerPaths();
|
||||||
|
} else {
|
||||||
|
foreach (const ProjectPart::Ptr &part, projectParts)
|
||||||
|
headerPaths += part->headerPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
return headerPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interface,
|
void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interface,
|
||||||
QuickFixOperations &result)
|
QuickFixOperations &result)
|
||||||
{
|
{
|
||||||
@@ -1877,84 +1950,29 @@ void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interfa
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// find the include paths
|
// find the include paths
|
||||||
ProjectPart::HeaderPaths headerPaths;
|
const ProjectPart::HeaderPaths headerPaths = relevantHeaderPaths(doc->fileName());
|
||||||
CppModelManager *modelManager = CppModelManager::instance();
|
|
||||||
QList<ProjectInfo> projectInfos = modelManager->projectInfos();
|
|
||||||
bool inProject = false;
|
|
||||||
foreach (const ProjectInfo &info, projectInfos) {
|
|
||||||
foreach (ProjectPart::Ptr part, info.projectParts()) {
|
|
||||||
foreach (const ProjectFile &file, part->files) {
|
|
||||||
if (file.path == doc->fileName()) {
|
|
||||||
inProject = true;
|
|
||||||
headerPaths += part->headerPaths;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!inProject) {
|
|
||||||
// better use all include paths than none
|
|
||||||
headerPaths = modelManager->headerPaths();
|
|
||||||
}
|
|
||||||
|
|
||||||
// find a include file through the locator
|
// find a include file through the locator
|
||||||
QFutureInterface<Core::LocatorFilterEntry> dummyInterface;
|
QFutureInterface<Core::LocatorFilterEntry> dummyInterface;
|
||||||
QList<Core::LocatorFilterEntry> matches = classesFilter->matchesFor(dummyInterface, className);
|
QList<Core::LocatorFilterEntry> matches = classesFilter->matchesFor(dummyInterface, className);
|
||||||
bool classExists = false;
|
bool classFoundInLocator = false;
|
||||||
foreach (const Core::LocatorFilterEntry &entry, matches) {
|
foreach (const Core::LocatorFilterEntry &entry, matches) {
|
||||||
IndexItem::Ptr info = entry.internalData.value<IndexItem::Ptr>();
|
IndexItem::Ptr info = entry.internalData.value<IndexItem::Ptr>();
|
||||||
if (info->symbolName() != className)
|
if (info->symbolName() != className)
|
||||||
continue;
|
continue;
|
||||||
classExists = true;
|
classFoundInLocator = true;
|
||||||
const QString &fileName = info->fileName();
|
|
||||||
const QFileInfo fileInfo(fileName);
|
|
||||||
|
|
||||||
// find the shortest way to include fileName given the includePaths
|
// find the shortest way to include fileName given the includePaths
|
||||||
QString shortestInclude;
|
const QString include = findShortestInclude(doc->fileName(), info->fileName(), headerPaths);
|
||||||
|
if (!include.isEmpty())
|
||||||
if (fileInfo.path() == QFileInfo(doc->fileName()).path()) {
|
result.append(new AddIncludeForUndefinedIdentifierOp(interface, 0, include));
|
||||||
shortestInclude = QLatin1Char('"') + fileInfo.fileName() + QLatin1Char('"');
|
|
||||||
} else {
|
|
||||||
foreach (const ProjectPart::HeaderPath &headerPath, headerPaths) {
|
|
||||||
if (!fileName.startsWith(headerPath.path))
|
|
||||||
continue;
|
|
||||||
QString relativePath = fileName.mid(headerPath.path.size());
|
|
||||||
if (!relativePath.isEmpty() && relativePath.at(0) == QLatin1Char('/'))
|
|
||||||
relativePath = relativePath.mid(1);
|
|
||||||
if (shortestInclude.isEmpty() || relativePath.size() + 2 < shortestInclude.size())
|
|
||||||
shortestInclude = QLatin1Char('<') + relativePath + QLatin1Char('>');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!shortestInclude.isEmpty())
|
|
||||||
result.append(new AddIncludeForUndefinedIdentifierOp(interface, 0, shortestInclude));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool isProbablyAQtClass = className.size() > 2
|
// If e.g. QString was found in "<qstring.h>" propose an extra prioritized entry "<QString>".
|
||||||
&& className.at(0) == QLatin1Char('Q')
|
if (className.size() > 2 && className.at(0) == QLatin1Char('Q') && className.at(1).isUpper()) {
|
||||||
&& className.at(1).isUpper();
|
const QString include = findIncludeForQtClass(className, headerPaths, classFoundInLocator);
|
||||||
|
if (!include.isEmpty())
|
||||||
if (!isProbablyAQtClass)
|
result.append(new AddIncludeForUndefinedIdentifierOp(interface, 1, include));
|
||||||
return;
|
|
||||||
|
|
||||||
// for QSomething, propose a <QSomething> include -- if such a class was in the locator
|
|
||||||
if (classExists) {
|
|
||||||
const QString include = QLatin1Char('<') + className + QLatin1Char('>');
|
|
||||||
result.append(new AddIncludeForUndefinedIdentifierOp(interface, 1, include));
|
|
||||||
|
|
||||||
// otherwise, check for a header file with the same name in the Qt include paths
|
|
||||||
} else {
|
|
||||||
foreach (const ProjectPart::HeaderPath &headerPath, headerPaths) {
|
|
||||||
if (!headerPath.path.contains(QLatin1String("/Qt"))) // "QtCore", "QtGui" etc...
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const QString headerPathCandidate = headerPath.path + QLatin1Char('/') + className;
|
|
||||||
const QFileInfo fileInfo(headerPathCandidate);
|
|
||||||
if (fileInfo.exists() && fileInfo.isFile()) {
|
|
||||||
const QString include = QLatin1Char('<') + className + QLatin1Char('>');
|
|
||||||
result.append(new AddIncludeForUndefinedIdentifierOp(interface, 1, include));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user