forked from qt-creator/qt-creator
Wizards: Offer auto-completion in "New C++ Class" wizard
That is, offer existing namespaces for the class name line edit and existing classes for the base class line edit. Fixes: QTCREATORBUG-10066 Change-Id: I276036864626eff92997e40e4e22ab16c4f4d617 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -35,7 +35,10 @@
|
|||||||
"trDisplayName": "Class name:",
|
"trDisplayName": "Class name:",
|
||||||
"mandatory": true,
|
"mandatory": true,
|
||||||
"type": "LineEdit",
|
"type": "LineEdit",
|
||||||
"data": { "validator": "(?:(?:[a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*|)" }
|
"data": {
|
||||||
|
"validator": "(?:(?:[a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*|)",
|
||||||
|
"completion": "namespaces"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "BaseCB",
|
"name": "BaseCB",
|
||||||
@@ -55,7 +58,8 @@
|
|||||||
"data":
|
"data":
|
||||||
{
|
{
|
||||||
"trText": "%{BaseCB}",
|
"trText": "%{BaseCB}",
|
||||||
"trDisabledText": "%{BaseCB}"
|
"trDisabledText": "%{BaseCB}",
|
||||||
|
"completion": "classes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,6 @@ Core::LocatorFilterEntry CppClassesFilter::filterEntryFromIndexItem(IndexItem::P
|
|||||||
filterEntry.extraInfo = info->symbolScope().isEmpty()
|
filterEntry.extraInfo = info->symbolScope().isEmpty()
|
||||||
? info->shortNativeFilePath()
|
? info->shortNativeFilePath()
|
||||||
: info->symbolScope();
|
: info->symbolScope();
|
||||||
|
filterEntry.fileName = info->fileName();
|
||||||
return filterEntry;
|
return filterEntry;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,28 +29,37 @@
|
|||||||
#include "jsonwizard.h"
|
#include "jsonwizard.h"
|
||||||
#include "jsonwizardfactory.h"
|
#include "jsonwizardfactory.h"
|
||||||
|
|
||||||
|
#include "../project.h"
|
||||||
|
#include "../projecttree.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
#include <coreplugin/locator/ilocatorfilter.h>
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/fancylineedit.h>
|
#include <utils/fancylineedit.h>
|
||||||
|
#include <utils/fileutils.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/runextensions.h>
|
||||||
#include <utils/stringutils.h>
|
#include <utils/stringutils.h>
|
||||||
#include <utils/theme/theme.h>
|
#include <utils/theme/theme.h>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QApplication>
|
#include <QCompleter>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
#include <QFormLayout>
|
#include <QFormLayout>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
#include <QItemSelectionModel>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QListView>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QRegularExpressionValidator>
|
#include <QRegularExpressionValidator>
|
||||||
|
#include <QStandardItem>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QListView>
|
|
||||||
#include <QStandardItem>
|
|
||||||
#include <QItemSelectionModel>
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
@@ -510,6 +519,18 @@ bool LineEditField::parseData(const QVariant &data, QString *errorMessage)
|
|||||||
}
|
}
|
||||||
m_fixupExpando = consumeValue(tmp, "fixup").toString();
|
m_fixupExpando = consumeValue(tmp, "fixup").toString();
|
||||||
|
|
||||||
|
const QString completion = consumeValue(tmp, "completion").toString();
|
||||||
|
if (completion == "classes") {
|
||||||
|
m_completion = Completion::Classes;
|
||||||
|
} else if (completion == "namespaces") {
|
||||||
|
m_completion = Completion::Namespaces;
|
||||||
|
} else if (!completion.isEmpty()) {
|
||||||
|
*errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
|
||||||
|
"LineEdit (\"%1\") has an invalid value \"%2\" in \"completion\".")
|
||||||
|
.arg(name(), completion);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
warnAboutUnsupportedKeys(tmp, name(), type());
|
warnAboutUnsupportedKeys(tmp, name(), type());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -531,6 +552,7 @@ QWidget *LineEditField::createWidget(const QString &displayName, JsonFieldPage *
|
|||||||
|
|
||||||
w->setEchoMode(m_isPassword ? QLineEdit::Password : QLineEdit::Normal);
|
w->setEchoMode(m_isPassword ? QLineEdit::Password : QLineEdit::Normal);
|
||||||
QObject::connect(w, &FancyLineEdit::textEdited, [this] { setHasUserChanges(); });
|
QObject::connect(w, &FancyLineEdit::textEdited, [this] { setHasUserChanges(); });
|
||||||
|
setupCompletion(w);
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
@@ -598,6 +620,78 @@ QVariant LineEditField::toSettings() const
|
|||||||
return qobject_cast<FancyLineEdit *>(widget())->text();
|
return qobject_cast<FancyLineEdit *>(widget())->text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LineEditField::setupCompletion(FancyLineEdit *lineEdit)
|
||||||
|
{
|
||||||
|
using namespace Core;
|
||||||
|
using namespace Utils;
|
||||||
|
if (m_completion == Completion::None)
|
||||||
|
return;
|
||||||
|
ILocatorFilter * const classesFilter = findOrDefault(
|
||||||
|
ILocatorFilter::allLocatorFilters(),
|
||||||
|
equal(&ILocatorFilter::id, Id("Classes")));
|
||||||
|
if (!classesFilter)
|
||||||
|
return;
|
||||||
|
classesFilter->prepareSearch({});
|
||||||
|
const auto watcher = new QFutureWatcher<LocatorFilterEntry>(lineEdit);
|
||||||
|
const auto handleResults = [this, lineEdit, watcher](int firstIndex, int endIndex) {
|
||||||
|
QSet<QString> namespaces;
|
||||||
|
QStringList classes;
|
||||||
|
QString projectBaseDir;
|
||||||
|
Project * const project = ProjectTree::currentProject();
|
||||||
|
for (int i = firstIndex; i < endIndex; ++i) {
|
||||||
|
static const auto isReservedName = [](const QString &name) {
|
||||||
|
static const QRegularExpression rx1("^_[A-Z].*");
|
||||||
|
static const QRegularExpression rx2(".*::_[A-Z].*");
|
||||||
|
return name.contains("__") || rx1.match(name).hasMatch()
|
||||||
|
|| rx2.match(name).hasMatch();
|
||||||
|
};
|
||||||
|
const LocatorFilterEntry &entry = watcher->resultAt(i);
|
||||||
|
const bool hasNamespace = !entry.extraInfo.isEmpty()
|
||||||
|
&& !entry.extraInfo.startsWith('<') && !entry.extraInfo.contains("::<")
|
||||||
|
&& !isReservedName(entry.extraInfo)
|
||||||
|
&& !entry.extraInfo.startsWith('~')
|
||||||
|
&& !entry.extraInfo.contains("Anonymous:")
|
||||||
|
&& !FileUtils::isAbsolutePath(entry.extraInfo);
|
||||||
|
const bool isBaseClassCandidate = !isReservedName(entry.displayName)
|
||||||
|
&& !entry.displayName.startsWith("Anonymous:");
|
||||||
|
if (isBaseClassCandidate)
|
||||||
|
classes << entry.displayName;
|
||||||
|
if (hasNamespace) {
|
||||||
|
if (isBaseClassCandidate)
|
||||||
|
classes << (entry.extraInfo + "::" + entry.displayName);
|
||||||
|
if (m_completion == Completion::Namespaces) {
|
||||||
|
if (!project
|
||||||
|
|| entry.fileName.startsWith(project->projectDirectory().toString())) {
|
||||||
|
namespaces << entry.extraInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QStringList completionList;
|
||||||
|
if (m_completion == Completion::Namespaces) {
|
||||||
|
completionList = toList(namespaces);
|
||||||
|
completionList = filtered(completionList, [&classes](const QString &ns) {
|
||||||
|
return !classes.contains(ns);
|
||||||
|
});
|
||||||
|
completionList = transform(completionList, [](const QString &ns) {
|
||||||
|
return QString(ns + "::");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
completionList = classes;
|
||||||
|
}
|
||||||
|
completionList.sort();
|
||||||
|
lineEdit->setSpecialCompleter(new QCompleter(completionList, lineEdit));
|
||||||
|
watcher->deleteLater();
|
||||||
|
};
|
||||||
|
QObject::connect(watcher, &QFutureWatcher<LocatorFilterEntry>::resultsReadyAt, lineEdit,
|
||||||
|
handleResults);
|
||||||
|
watcher->setFuture(runAsync([classesFilter](QFutureInterface<LocatorFilterEntry> &f) {
|
||||||
|
const QList<LocatorFilterEntry> matches = classesFilter->matchesFor(f, {});
|
||||||
|
f.reportResults(QVector<LocatorFilterEntry>(matches.cbegin(), matches.cend()));
|
||||||
|
f.reportFinished();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
// TextEditFieldData:
|
// TextEditFieldData:
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ private:
|
|||||||
void fromSettings(const QVariant &value) override;
|
void fromSettings(const QVariant &value) override;
|
||||||
QVariant toSettings() const override;
|
QVariant toSettings() const override;
|
||||||
|
|
||||||
|
void setupCompletion(Utils::FancyLineEdit *lineEdit);
|
||||||
|
|
||||||
bool m_isModified = false;
|
bool m_isModified = false;
|
||||||
bool m_isValidating = false;
|
bool m_isValidating = false;
|
||||||
bool m_restoreLastHistoryItem = false;
|
bool m_restoreLastHistoryItem = false;
|
||||||
@@ -122,6 +124,9 @@ private:
|
|||||||
QRegularExpression m_validatorRegExp;
|
QRegularExpression m_validatorRegExp;
|
||||||
QString m_fixupExpando;
|
QString m_fixupExpando;
|
||||||
mutable QString m_currentText;
|
mutable QString m_currentText;
|
||||||
|
|
||||||
|
enum class Completion { Classes, Namespaces, None };
|
||||||
|
Completion m_completion = Completion::None;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TextEditField : public JsonFieldPage::Field
|
class TextEditField : public JsonFieldPage::Field
|
||||||
|
|||||||
Reference in New Issue
Block a user