Commit c903f497 authored by Marco Bubke's avatar Marco Bubke

TextEditor: Introduce AssistProposalItemInterface

For unit test we need to break every dependency to the TextEditor
Widget etc.. With an abstract interface we can implement it in clang
without relying on unwanted dependencies. It makes it also easier to
compute the values deferred.

Change-Id: I1b313a1625f4e80bd324ab4bf1a7c4f6b690abe9
Reviewed-by: default avatarNikolai Kosjar <nikolai.kosjar@theqtcompany.com>
parent e42bf6eb
......@@ -33,7 +33,7 @@
namespace ClangCodeModel {
namespace Internal {
class ClangAssistProposalItem : public TextEditor::AssistProposalItem
class ClangAssistProposalItem final : public TextEditor::AssistProposalItem
{
friend bool operator<(const ClangAssistProposalItem &first, const ClangAssistProposalItem &second);
public:
......
......@@ -27,7 +27,7 @@
#include "clangassistproposalmodel.h"
#include <texteditor/codeassist/assistproposalitem.h>
#include <texteditor/codeassist/assistproposaliteminterface.h>
#include <algorithm>
......@@ -47,9 +47,10 @@ bool ClangAssistProposalModel::isSortable(const QString &/*prefix*/) const
void ClangAssistProposalModel::sort(const QString &/*prefix*/)
{
using TextEditor::AssistProposalItem;
using TextEditor::AssistProposalItemInterface;
auto currentItemsCompare = [](AssistProposalItem *first, AssistProposalItem *second) {
auto currentItemsCompare = [](AssistProposalItemInterface *first,
AssistProposalItemInterface *second) {
return (first->order() > 0
&& (first->order() < second->order()
|| (first->order() == second->order() && first->text() < second->text())));
......
......@@ -58,14 +58,14 @@ namespace ClangCodeModel {
namespace Internal {
using ClangBackEnd::CodeCompletion;
using TextEditor::AssistProposalItem;
using TextEditor::AssistProposalItemInterface;
namespace {
const char SNIPPET_ICON_PATH[] = ":/texteditor/images/snippet.png";
QList<AssistProposalItem *> toAssistProposalItems(const CodeCompletions &completions)
QList<AssistProposalItemInterface *> toAssistProposalItems(const CodeCompletions &completions)
{
static CPlusPlus::Icons m_icons; // de-deduplicate
......@@ -178,7 +178,7 @@ QList<AssistProposalItem *> toAssistProposalItems(const CodeCompletions &complet
}
}
QList<AssistProposalItem *> results;
QList<AssistProposalItemInterface *> results;
results.reserve(items.size());
std::copy(items.cbegin(), items.cend(), std::back_inserter(results));
......
......@@ -746,10 +746,10 @@ bool hasSnippet(ProposalModel model, const QByteArray &text)
auto *genericModel = static_cast<TextEditor::GenericProposalModel *>(model.data());
for (int i = 0, size = genericModel->size(); i < size; ++i) {
TextEditor::AssistProposalItem *item = genericModel->proposalItem(i);
TextEditor::AssistProposalItemInterface *item = genericModel->proposalItem(i);
QTC_ASSERT(item, continue);
if (item->text() == snippetText)
return item->data().toString().contains(QLatin1Char('$'));
return item->isSnippet();
}
return false;
......
......@@ -101,7 +101,9 @@ private:
class VirtualFunctionProposal : public GenericProposal
{
public:
VirtualFunctionProposal(int cursorPos, const QList<AssistProposalItem *> &items, bool openInSplit)
VirtualFunctionProposal(int cursorPos,
const QList<AssistProposalItemInterface *> &items,
bool openInSplit)
: GenericProposal(cursorPos, items)
, m_openInSplit(openInSplit)
{}
......@@ -126,13 +128,12 @@ public:
{
QTC_ASSERT(m_params.function, return 0);
AssistProposalItem *hintItem
= new VirtualFunctionProposalItem(TextEditorWidget::Link());
auto *hintItem = new VirtualFunctionProposalItem(TextEditorWidget::Link());
hintItem->setText(QCoreApplication::translate("VirtualFunctionsAssistProcessor",
"...searching overrides"));
hintItem->setOrder(-1000);
QList<AssistProposalItem *> items;
QList<AssistProposalItemInterface *> items;
items << itemFromFunction(m_params.function);
items << hintItem;
return new VirtualFunctionProposal(m_params.cursorPosition, items, m_params.openInNextSplit);
......@@ -156,7 +157,7 @@ public:
if (overrides.isEmpty())
return 0;
QList<AssistProposalItem *> items;
QList<AssistProposalItemInterface *> items;
foreach (Function *func, overrides)
items << itemFromFunction(func);
items.first()->setOrder(1000); // Ensure top position for function of static type
......@@ -172,14 +173,14 @@ private:
return func;
}
AssistProposalItem *itemFromFunction(Function *func) const
VirtualFunctionProposalItem *itemFromFunction(Function *func) const
{
const TextEditorWidget::Link link = CppTools::linkToSymbol(maybeDefinitionFor(func));
QString text = m_overview.prettyName(LookupContext::fullyQualifiedName(func));
if (func->isPureVirtual())
text += QLatin1String(" = 0");
AssistProposalItem *item = new VirtualFunctionProposalItem(link, m_params.openInNextSplit);
auto *item = new VirtualFunctionProposalItem(link, m_params.openInNextSplit);
item->setText(text);
item->setIcon(m_icons.iconForSymbol(func));
......
......@@ -32,7 +32,7 @@
namespace CppEditor {
namespace Internal {
class VirtualFunctionProposalItem: public TextEditor::AssistProposalItem
class VirtualFunctionProposalItem final : public TextEditor::AssistProposalItem
{
public:
VirtualFunctionProposalItem(const TextEditor::TextEditorWidget::Link &link,
......
......@@ -80,7 +80,7 @@ struct CompleteFunctionDeclaration
// ---------------------
// CppAssistProposalItem
// ---------------------
class CppAssistProposalItem : public AssistProposalItem
class CppAssistProposalItem final : public AssistProposalItem
{
public:
CppAssistProposalItem() :
......@@ -95,11 +95,14 @@ public:
void keepTypeOfExpression(const QSharedPointer<TypeOfExpression> &typeOfExp)
{ m_typeOfExpression = typeOfExp; }
quint64 hash() const override;
private:
bool m_isOverloaded;
mutable QChar m_typedChar;
unsigned m_completionOperator;
QSharedPointer<TypeOfExpression> m_typeOfExpression;
unsigned m_completionOperator;
mutable QChar m_typedChar;
bool m_isOverloaded;
};
} // Internal
......@@ -115,10 +118,10 @@ bool CppAssistProposalModel::isSortable(const QString &prefix) const
return !prefix.isEmpty();
}
AssistProposalItem *CppAssistProposalModel::proposalItem(int index) const
AssistProposalItemInterface *CppAssistProposalModel::proposalItem(int index) const
{
auto item = static_cast<AssistProposalItem *>(GenericProposalModel::proposalItem(index));
if (!item->data().canConvert<QString>()) {
AssistProposalItemInterface *item = GenericProposalModel::proposalItem(index);
if (!item->isSnippet()) {
CppAssistProposalItem *cppItem = static_cast<CppAssistProposalItem *>(item);
cppItem->keepCompletionOperator(m_completionOperator);
cppItem->keepTypeOfExpression(m_typeOfExpression);
......@@ -178,6 +181,16 @@ static bool isDereferenced(TextEditorWidget *editorWidget, int basePosition)
return false;
}
quint64 CppAssistProposalItem::hash() const
{
if (data().canConvert<Symbol *>())
return quint64(data().value<Symbol *>()->index());
else if (data().canConvert<CompleteFunctionDeclaration>())
return quint64(data().value<CompleteFunctionDeclaration>().function->index());
return 0;
}
void CppAssistProposalItem::applyContextualContent(TextEditorWidget *editorWidget, int basePosition) const
{
Symbol *symbol = 0;
......@@ -892,12 +905,12 @@ IAssistProposal *InternalCppCompletionAssistProcessor::createContentProposal()
{
// Duplicates are kept only if they are snippets.
QSet<QString> processed;
QList<AssistProposalItem *>::iterator it = m_completions.begin();
auto it = m_completions.begin();
while (it != m_completions.end()) {
CppAssistProposalItem *item = static_cast<CppAssistProposalItem *>(*it);
if (!processed.contains(item->text()) || item->data().canConvert<QString>()) {
if (!processed.contains(item->text()) || item->isSnippet()) {
++it;
if (!item->data().canConvert<QString>()) {
if (!item->isSnippet()) {
processed.insert(item->text());
if (!item->isOverloaded()) {
if (Symbol *symbol = qvariant_cast<Symbol *>(item->data())) {
......
......@@ -71,7 +71,7 @@ public:
}
bool isSortable(const QString &prefix) const override;
TextEditor::AssistProposalItem *proposalItem(int index) const override;
TextEditor::AssistProposalItemInterface *proposalItem(int index) const override;
unsigned m_completionOperator;
bool m_replaceDotForArrow;
......
......@@ -44,7 +44,7 @@ protected:
void addSnippets();
int m_positionForProposal;
QList<TextEditor::AssistProposalItem *> m_completions;
QList<TextEditor::AssistProposalItemInterface *> m_completions;
QStringList m_preprocessorCompletions;
TextEditor::IAssistProposal *m_hintProposal;
CPlusPlus::Icons m_icons;
......
......@@ -915,7 +915,7 @@ private:
QString m_needle;
};
class FakeVimAssistProposalItem : public AssistProposalItem
class FakeVimAssistProposalItem final : public AssistProposalItem
{
public:
FakeVimAssistProposalItem(const FakeVimCompletionAssistProvider *provider)
......@@ -948,7 +948,7 @@ private:
class FakeVimAssistProposalModel : public GenericProposalModel
{
public:
FakeVimAssistProposalModel(const QList<AssistProposalItem *> &items)
FakeVimAssistProposalModel(const QList<AssistProposalItemInterface *> &items)
{
loadContent(items);
}
......@@ -976,7 +976,7 @@ public:
tc.setPosition(interface->position());
tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
QList<AssistProposalItem *> items;
QList<AssistProposalItemInterface *> items;
QSet<QString> seen;
QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
while (1) {
......
......@@ -264,7 +264,7 @@ IAssistProposal *GlslCompletionAssistProcessor::perform(const AssistInterface *i
QList<GLSL::Symbol *> members;
QStringList specialMembers;
QList<AssistProposalItem *> m_completions;
QList<AssistProposalItemInterface *> m_completions;
bool functionCall = (ch == QLatin1Char('(') && pos == m_interface->position() - 1);
......
......@@ -78,7 +78,7 @@ enum CompletionOrder {
TypeOrder = -30
};
static void addCompletion(QList<AssistProposalItem *> *completions,
static void addCompletion(QList<AssistProposalItemInterface *> *completions,
const QString &text,
const QIcon &icon,
int order,
......@@ -95,7 +95,7 @@ static void addCompletion(QList<AssistProposalItem *> *completions,
completions->append(item);
}
static void addCompletions(QList<AssistProposalItem *> *completions,
static void addCompletions(QList<AssistProposalItemInterface *> *completions,
const QStringList &newCompletions,
const QIcon &icon,
int order)
......@@ -120,10 +120,10 @@ public:
class CompletionAdder : public PropertyProcessor
{
protected:
QList<AssistProposalItem *> *completions;
QList<AssistProposalItemInterface *> *completions;
public:
CompletionAdder(QList<AssistProposalItem *> *completions,
CompletionAdder(QList<AssistProposalItemInterface *> *completions,
const QIcon &icon, int order)
: completions(completions)
, icon(icon)
......@@ -150,7 +150,7 @@ public:
class LhsCompletionAdder : public CompletionAdder
{
public:
LhsCompletionAdder(QList<AssistProposalItem *> *completions,
LhsCompletionAdder(QList<AssistProposalItemInterface *> *completions,
const QIcon &icon,
int order,
bool afterOn)
......@@ -982,7 +982,7 @@ class QmlJSLessThan
public:
QmlJSLessThan(const QString &searchString) : m_searchString(searchString)
{ }
bool operator() (const AssistProposalItem *a, const AssistProposalItem *b)
bool operator() (const AssistProposalItemInterface *a, const AssistProposalItemInterface *b)
{
if (a->order() != b->order())
return a->order() > b->order();
......@@ -990,8 +990,8 @@ public:
return true;
else if (b->text().isEmpty())
return false;
else if (a->data().isValid() != b->data().isValid())
return a->data().isValid();
else if (a->isValid() != b->isValid())
return a->isValid();
else if (a->text().at(0).isUpper() && b->text().at(0).isLower())
return false;
else if (a->text().at(0).isLower() && b->text().at(0).isUpper())
......@@ -1016,9 +1016,9 @@ void QmlJSAssistProposalModel::filter(const QString &prefix)
GenericProposalModel::filter(prefix);
if (prefix.startsWith(QLatin1String("__")))
return;
QList<AssistProposalItem *> newCurrentItems;
QList<AssistProposalItemInterface *> newCurrentItems;
newCurrentItems.reserve(m_currentItems.size());
foreach (AssistProposalItem *item, m_currentItems)
foreach (AssistProposalItemInterface *item, m_currentItems)
if (!item->text().startsWith(QLatin1String("__")))
newCurrentItems << item;
m_currentItems = newCurrentItems;
......
......@@ -61,7 +61,7 @@ public:
class QmlJSAssistProposalModel : public TextEditor::GenericProposalModel
{
public:
QmlJSAssistProposalModel(const QList<TextEditor::AssistProposalItem *> &items)
QmlJSAssistProposalModel(const QList<TextEditor::AssistProposalItemInterface *> &items)
{
loadContent(items);
}
......@@ -109,7 +109,7 @@ private:
int m_startPosition;
QScopedPointer<const QmlJSCompletionAssistInterface> m_interface;
QList<TextEditor::AssistProposalItem *> m_completions;
QList<TextEditor::AssistProposalItemInterface *> m_completions;
TextEditor::SnippetAssistCollector m_snippetCollector;
};
......
......@@ -90,7 +90,7 @@ public:
QIcon icon = QIcon::fromTheme(QLatin1String("edit-paste"), Core::Icons::PASTE.icon()).pixmap(16);
CircularClipboard * clipboard = CircularClipboard::instance();
QList<AssistProposalItem *> items;
QList<AssistProposalItemInterface *> items;
for (int i = 0; i < clipboard->size(); ++i) {
QSharedPointer<const QMimeData> data = clipboard->next();
......
......@@ -60,19 +60,12 @@ namespace TextEditor {
This is the place to implement the actual application of the item.
*/
AssistProposalItem::AssistProposalItem()
: m_order(0)
{}
AssistProposalItem::~AssistProposalItem()
{}
void AssistProposalItem::setIcon(const QIcon &icon)
{
m_icon = icon;
}
const QIcon &AssistProposalItem::icon() const
QIcon AssistProposalItem::icon() const
{
return m_icon;
}
......@@ -92,7 +85,7 @@ void AssistProposalItem::setDetail(const QString &detail)
m_detail = detail;
}
const QString &AssistProposalItem::detail() const
QString AssistProposalItem::detail() const
{
return m_detail;
}
......@@ -107,14 +100,19 @@ const QVariant &AssistProposalItem::data() const
return m_data;
}
int AssistProposalItem::order() const
bool AssistProposalItem::isSnippet() const
{
return data().canConvert<QString>();
}
bool AssistProposalItem::isValid() const
{
return m_order;
return m_data.isValid();
}
void AssistProposalItem::setOrder(int order)
quint64 AssistProposalItem::hash() const
{
m_order = order;
return 0;
}
bool AssistProposalItem::implicitlyApplies() const
......
......@@ -28,6 +28,7 @@
#include <texteditor/texteditor_global.h>
#include "assistproposaliteminterface.h"
#include <QIcon>
#include <QString>
......@@ -37,30 +38,28 @@ namespace TextEditor {
class TextEditorWidget;
class TEXTEDITOR_EXPORT AssistProposalItem
class TEXTEDITOR_EXPORT AssistProposalItem : public AssistProposalItemInterface
{
public:
AssistProposalItem();
virtual ~AssistProposalItem();
virtual QString text() const;
virtual bool implicitlyApplies() const;
virtual bool prematurelyApplies(const QChar &c) const;
virtual void apply(TextEditorWidget *editorWidget, int basePosition) const;
QString text() const override;
bool implicitlyApplies() const override;
bool prematurelyApplies(const QChar &c) const override;
void apply(TextEditorWidget *editorWidget, int basePosition) const override;
void setIcon(const QIcon &icon);
const QIcon &icon() const;
QIcon icon() const final;
void setText(const QString &text);
void setDetail(const QString &detail);
const QString &detail() const;
QString detail() const final;
void setData(const QVariant &var);
const QVariant &data() const;
int order() const;
void setOrder(int order);
bool isSnippet() const final;
bool isValid() const final;
quint64 hash() const;
virtual void applyContextualContent(TextEditorWidget *editorWidget, int basePosition) const;
virtual void applySnippet(TextEditorWidget *editorWidget, int basePosition) const;
......@@ -71,7 +70,6 @@ private:
QString m_text;
QString m_detail;
QVariant m_data;
int m_order;
};
} // namespace TextEditor
......
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#ifndef TEXTEDITOR_ASSISTPROPOSALITEMINTERFACE_H
#define TEXTEDITOR_ASSISTPROPOSALITEMINTERFACE_H
#include <texteditor/texteditor_global.h>
QT_BEGIN_NAMESPACE
class QChar;
class QIcon;
class QString;
class QVariant;
QT_END_NAMESPACE
#include <QString>
namespace TextEditor {
class TextEditorWidget;
class TEXTEDITOR_EXPORT AssistProposalItemInterface
{
public:
virtual ~AssistProposalItemInterface() noexcept = default;
virtual QString text() const = 0;
virtual bool implicitlyApplies() const = 0;
virtual bool prematurelyApplies(const QChar &character) const = 0;
virtual void apply(TextEditorWidget *editorWidget, int basePosition) const = 0;
virtual QIcon icon() const = 0;
virtual QString detail() const = 0;
virtual bool isSnippet() const = 0;
virtual bool isValid() const = 0;
virtual quint64 hash() const = 0; // it is only for removing duplicates
int order() const { return m_order; }
void setOrder(int order) { m_order = order; }
private:
int m_order = 0;
};
} // namespace TextEditor
#endif // TEXTEDITOR_ASSISTPROPOSALITEMINTERFACE_H
......@@ -85,7 +85,7 @@ public:
private:
void proposalComputed();
void processProposalItem(AssistProposalItem *proposalItem);
void processProposalItem(AssistProposalItemInterface *proposalItem);
void handlePrefixExpansion(const QString &newPrefix);
void finalizeProposal();
void explicitlyAborted();
......@@ -354,7 +354,7 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR
m_editorWidget->position() - basePosition));
}
void CodeAssistantPrivate::processProposalItem(AssistProposalItem *proposalItem)
void CodeAssistantPrivate::processProposalItem(AssistProposalItemInterface *proposalItem)
{
QTC_ASSERT(m_proposal, return);
proposalItem->apply(m_editorWidget, m_proposal->basePosition());
......
......@@ -34,7 +34,7 @@ GenericProposal::GenericProposal(int cursorPos, GenericProposalModel *model)
, m_model(model)
{}
GenericProposal::GenericProposal(int cursorPos, const QList<AssistProposalItem *> &items)
GenericProposal::GenericProposal(int cursorPos, const QList<AssistProposalItemInterface *> &items)
: m_basePosition(cursorPos)
, m_model(new GenericProposalModel)
{
......
......@@ -32,13 +32,13 @@
namespace TextEditor {
class GenericProposalModel;
class AssistProposalItem;
class AssistProposalItemInterface;
class TEXTEDITOR_EXPORT GenericProposal : public IAssistProposal
{
public:
GenericProposal(int cursorPos, GenericProposalModel *model);
GenericProposal(int cursorPos, const QList<AssistProposalItem *> &items);
GenericProposal(int cursorPos, const QList<AssistProposalItemInterface *> &items);
~GenericProposal();
bool isFragile() const override;
......
......@@ -54,7 +54,7 @@ struct ContentLessThan
: m_prefix(prefix)
{}
bool operator()(const AssistProposalItem *a, const AssistProposalItem *b)
bool operator()(const AssistProposalItemInterface *a, const AssistProposalItemInterface *b)
{
// The order is case-insensitive in principle, but case-sensitive when this
// would otherwise mean equality
......@@ -137,7 +137,7 @@ GenericProposalModel::~GenericProposalModel()
qDeleteAll(m_originalItems);
}
void GenericProposalModel::loadContent(const QList<AssistProposalItem *> &items)
void GenericProposalModel::loadContent(const QList<AssistProposalItemInterface *> &items)
{
m_originalItems = items;
m_currentItems = items;
......@@ -170,17 +170,18 @@ QString GenericProposalModel::detail(int index) const
return m_currentItems.at(index)->detail();
}
// make it optional because it is not needed in many cases
void GenericProposalModel::removeDuplicates()
{
QHash<QString, QVariant> unique;
QList<AssistProposalItem *>::iterator it = m_originalItems.begin();
QHash<QString, quint64> unique;
auto it = m_originalItems.begin();
while (it != m_originalItems.end()) {
const AssistProposalItem *item = *it;
const AssistProposalItemInterface *item = *it;
if (unique.contains(item->text())
&& unique.value(item->text(), QVariant()) == item->data()) {
&& unique.value(item->text()) == item->hash()) {
it = m_originalItems.erase(it);
} else {
unique.insert(item->text(), item->data());
unique.insert(item->text(), item->hash());
++it;
}
}
......@@ -245,8 +246,7 @@ void GenericProposalModel::filter(const QString &prefix)
QRegExp regExp(keyRegExp);
m_currentItems.clear();
for (auto it = m_originalItems.begin(); it != m_originalItems.end(); ++it) {
AssistProposalItem *item = *it;
foreach (const auto &item, m_originalItems) {
if (regExp.indexIn(item->text()) == 0)
m_currentItems.append(item);
}
......@@ -306,7 +306,7 @@ QString GenericProposalModel::proposalPrefix() const
return commonPrefix;
}
AssistProposalItem *GenericProposalModel::proposalItem(int index) const
AssistProposalItemInterface *GenericProposalModel::proposalItem(int index) const
{
return m_currentItems.at(index);
}
......@@ -39,7 +39,7 @@ QT_FORWARD_DECLARE_CLASS(QIcon)
namespace TextEditor {
class AssistProposalItem;
class AssistProposalItemInterface;
class TEXTEDITOR_EXPORT GenericProposalModel : public IAssistProposalModel
{
......@@ -61,18 +61,18 @@ public:
virtual bool supportsPrefixExpansion() const;
virtual QString proposalPrefix() const;
virtual bool keepPerfectMatch(AssistReason reason) const;
virtual AssistProposalItem *proposalItem(int index) const;
virtual AssistProposalItemInterface *proposalItem(int index) const;
void loadContent(const QList<AssistProposalItem *> &items);
void loadContent(const QList<AssistProposalItemInterface *> &items);
void setSortingAllowed(bool isAllowed);
bool isSortingAllowed() const;
protected:
QList<AssistProposalItem *> m_currentItems;
QList<AssistProposalItemInterface *> m_currentItems;
private:
QHash<QString, int> m_idByText;
QList<AssistProposalItem *> m_originalItems;
QList<AssistProposalItemInterface *> m_originalItems;
};
} // TextEditor
......