forked from qt-creator/qt-creator
CodeAssist: Fix auto completion if function signature is shown
This reverts
commit 3bf19030ab.
CodeAssist: Fragile proposals aren't closed by automatic proposals.
which fixed the case [1] but introduced the regression in case [2]. Re-
implement the fix for [1] in a different way: Check whether the new
proposal has any items to show before closing the function signature
hint.
Case [1]
void f(int);
void g()
{
f(bar // This is what we will have in the end. The steps are:
// 1. Type "f("
// --> OK, function signature pop up is shown.
// 2. Type "bar"
// --> OPS, function signature pop up is closed and no
// new completion list is shown because "bar" does
// not match any declarations.
}
Case [2]
int barman = 0;
void f(int);
void g()
{
f(bar // This is what we will have in the end. The steps are:
// 1. Type "f("
// --> OK, function signature pop up is shown.
// 2. Type "bar"
// --> OPS, no auto completion list for "barman" is
// proposed.
}
Task-number: QTCREATORBUG-16934
Change-Id: I8456275d951de9e6fc53285a5dbcbd448d49ad08
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -181,8 +181,7 @@ void CodeAssistantPrivate::process()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDisplayingProposal())
|
startAutomaticProposalTimer();
|
||||||
startAutomaticProposalTimer();
|
|
||||||
} else {
|
} else {
|
||||||
m_assistKind = TextEditor::Completion;
|
m_assistKind = TextEditor::Completion;
|
||||||
}
|
}
|
||||||
@@ -288,18 +287,33 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR
|
|||||||
|
|
||||||
QScopedPointer<IAssistProposal> proposalCandidate(newProposal);
|
QScopedPointer<IAssistProposal> proposalCandidate(newProposal);
|
||||||
|
|
||||||
|
bool destroyCurrentContext = false;
|
||||||
if (isDisplayingProposal()) {
|
if (isDisplayingProposal()) {
|
||||||
if (!m_proposal->isFragile())
|
if (!m_proposal->isFragile())
|
||||||
return;
|
return;
|
||||||
destroyContext();
|
destroyCurrentContext = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int basePosition = proposalCandidate->basePosition();
|
int basePosition = proposalCandidate->basePosition();
|
||||||
if (m_editorWidget->position() < basePosition)
|
if (m_editorWidget->position() < basePosition) {
|
||||||
|
if (destroyCurrentContext)
|
||||||
|
destroyContext();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_abortedBasePosition == basePosition && reason != ExplicitlyInvoked) {
|
||||||
|
if (destroyCurrentContext)
|
||||||
|
destroyContext();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString prefix = m_editorWidget->textAt(basePosition,
|
||||||
|
m_editorWidget->position() - basePosition);
|
||||||
|
if (!newProposal->hasItemsToPropose(prefix, reason))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_abortedBasePosition == basePosition && reason != ExplicitlyInvoked)
|
if (destroyCurrentContext)
|
||||||
return;
|
destroyContext();
|
||||||
|
|
||||||
clearAbortedPosition();
|
clearAbortedPosition();
|
||||||
m_proposal.reset(proposalCandidate.take());
|
m_proposal.reset(proposalCandidate.take());
|
||||||
@@ -325,9 +339,7 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR
|
|||||||
m_proposalWidget->setModel(m_proposal->model());
|
m_proposalWidget->setModel(m_proposal->model());
|
||||||
m_proposalWidget->setDisplayRect(m_editorWidget->cursorRect(basePosition));
|
m_proposalWidget->setDisplayRect(m_editorWidget->cursorRect(basePosition));
|
||||||
m_proposalWidget->setIsSynchronized(!m_receivedContentWhileWaiting);
|
m_proposalWidget->setIsSynchronized(!m_receivedContentWhileWaiting);
|
||||||
m_proposalWidget->showProposal(m_editorWidget->textAt(
|
m_proposalWidget->showProposal(prefix);
|
||||||
basePosition,
|
|
||||||
m_editorWidget->position() - basePosition));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeAssistantPrivate::processProposalItem(AssistProposalItemInterface *proposalItem)
|
void CodeAssistantPrivate::processProposalItem(AssistProposalItemInterface *proposalItem)
|
||||||
@@ -406,6 +418,8 @@ void CodeAssistantPrivate::notifyChange()
|
|||||||
m_proposalWidget->updateProposal(
|
m_proposalWidget->updateProposal(
|
||||||
m_editorWidget->textAt(m_proposal->basePosition(),
|
m_editorWidget->textAt(m_proposal->basePosition(),
|
||||||
m_editorWidget->position() - m_proposal->basePosition()));
|
m_editorWidget->position() - m_proposal->basePosition()));
|
||||||
|
if (m_proposal->isFragile())
|
||||||
|
startAutomaticProposalTimer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,7 +452,7 @@ void CodeAssistantPrivate::startAutomaticProposalTimer()
|
|||||||
|
|
||||||
void CodeAssistantPrivate::automaticProposalTimeout()
|
void CodeAssistantPrivate::automaticProposalTimeout()
|
||||||
{
|
{
|
||||||
if (isWaitingForProposal() || isDisplayingProposal())
|
if (isWaitingForProposal() || (isDisplayingProposal() && !m_proposal->isFragile()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
requestProposal(IdleEditor, Completion);
|
requestProposal(IdleEditor, Completion);
|
||||||
|
|||||||
@@ -49,6 +49,16 @@ bool GenericProposal::isFragile() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GenericProposal::hasItemsToPropose(const QString &prefix, AssistReason reason) const
|
||||||
|
{
|
||||||
|
if (!prefix.isEmpty()) {
|
||||||
|
m_model->filter(prefix);
|
||||||
|
m_model->setPrefilterPrefix(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_model->hasItemsToPropose(prefix, reason);
|
||||||
|
}
|
||||||
|
|
||||||
IAssistProposalModel *GenericProposal::model() const
|
IAssistProposalModel *GenericProposal::model() const
|
||||||
{
|
{
|
||||||
return m_model;
|
return m_model;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public:
|
|||||||
~GenericProposal();
|
~GenericProposal();
|
||||||
|
|
||||||
bool isFragile() const override;
|
bool isFragile() const override;
|
||||||
|
bool hasItemsToPropose(const QString &prefix, AssistReason reason) const override;
|
||||||
IAssistProposalModel *model() const override;
|
IAssistProposalModel *model() const override;
|
||||||
IAssistProposalWidget *createWidget() const override;
|
IAssistProposalWidget *createWidget() const override;
|
||||||
|
|
||||||
|
|||||||
@@ -145,8 +145,65 @@ void GenericProposalModel::loadContent(const QList<AssistProposalItemInterface *
|
|||||||
m_idByText.insert(m_originalItems.at(i)->text(), i);
|
m_idByText.insert(m_originalItems.at(i)->text(), i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GenericProposalModel::hasItemsToPropose(const QString &prefix, AssistReason reason) const
|
||||||
|
{
|
||||||
|
return size() != 0 && (keepPerfectMatch(reason) || !isPerfectMatch(prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString cleanText(const QString &original)
|
||||||
|
{
|
||||||
|
QString clean = original;
|
||||||
|
int ignore = 0;
|
||||||
|
for (int i = clean.length() - 1; i >= 0; --i, ++ignore) {
|
||||||
|
const QChar &c = clean.at(i);
|
||||||
|
if (c.isLetterOrNumber() || c == QLatin1Char('_')
|
||||||
|
|| c.isHighSurrogate() || c.isLowSurrogate()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ignore)
|
||||||
|
clean.chop(ignore);
|
||||||
|
return clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GenericProposalModel::isPerfectMatch(const QString &prefix) const
|
||||||
|
{
|
||||||
|
if (prefix.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < size(); ++i) {
|
||||||
|
const QString ¤t = cleanText(text(i));
|
||||||
|
if (!current.isEmpty()) {
|
||||||
|
CaseSensitivity cs = TextEditorSettings::completionSettings().m_caseSensitivity;
|
||||||
|
if (cs == TextEditor::CaseSensitive) {
|
||||||
|
if (prefix == current)
|
||||||
|
return true;
|
||||||
|
} else if (cs == TextEditor::CaseInsensitive) {
|
||||||
|
if (prefix.compare(current, Qt::CaseInsensitive) == 0)
|
||||||
|
return true;
|
||||||
|
} else if (cs == TextEditor::FirstLetterCaseSensitive) {
|
||||||
|
if (prefix.at(0) == current.at(0)
|
||||||
|
&& prefix.midRef(1).compare(current.midRef(1), Qt::CaseInsensitive) == 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GenericProposalModel::isPrefiltered(const QString &prefix) const
|
||||||
|
{
|
||||||
|
return !m_prefilterPrefix.isEmpty() && prefix == m_prefilterPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericProposalModel::setPrefilterPrefix(const QString &prefix)
|
||||||
|
{
|
||||||
|
m_prefilterPrefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
void GenericProposalModel::reset()
|
void GenericProposalModel::reset()
|
||||||
{
|
{
|
||||||
|
m_prefilterPrefix.clear();
|
||||||
m_currentItems = m_originalItems;
|
m_currentItems = m_originalItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,11 +65,18 @@ public:
|
|||||||
|
|
||||||
void loadContent(const QList<AssistProposalItemInterface *> &items);
|
void loadContent(const QList<AssistProposalItemInterface *> &items);
|
||||||
|
|
||||||
|
bool isPerfectMatch(const QString &prefix) const;
|
||||||
|
bool hasItemsToPropose(const QString &prefix, AssistReason reason) const;
|
||||||
|
|
||||||
|
bool isPrefiltered(const QString &prefix) const;
|
||||||
|
void setPrefilterPrefix(const QString &prefix);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QList<AssistProposalItemInterface *> m_currentItems;
|
QList<AssistProposalItemInterface *> m_currentItems;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHash<QString, int> m_idByText;
|
QHash<QString, int> m_idByText;
|
||||||
QList<AssistProposalItemInterface *> m_originalItems;
|
QList<AssistProposalItemInterface *> m_originalItems;
|
||||||
|
QString m_prefilterPrefix;
|
||||||
};
|
};
|
||||||
} // TextEditor
|
} // TextEditor
|
||||||
|
|||||||
@@ -54,47 +54,6 @@ using namespace Utils;
|
|||||||
|
|
||||||
namespace TextEditor {
|
namespace TextEditor {
|
||||||
|
|
||||||
static QString cleanText(const QString &original)
|
|
||||||
{
|
|
||||||
QString clean = original;
|
|
||||||
int ignore = 0;
|
|
||||||
for (int i = clean.length() - 1; i >= 0; --i, ++ignore) {
|
|
||||||
const QChar &c = clean.at(i);
|
|
||||||
if (c.isLetterOrNumber() || c == QLatin1Char('_')
|
|
||||||
|| c.isHighSurrogate() || c.isLowSurrogate()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ignore)
|
|
||||||
clean.chop(ignore);
|
|
||||||
return clean;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isPerfectMatch(const QString &prefix, const GenericProposalModel *model)
|
|
||||||
{
|
|
||||||
if (prefix.isEmpty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (int i = 0; i < model->size(); ++i) {
|
|
||||||
const QString ¤t = cleanText(model->text(i));
|
|
||||||
if (!current.isEmpty()) {
|
|
||||||
CaseSensitivity cs = TextEditorSettings::completionSettings().m_caseSensitivity;
|
|
||||||
if (cs == TextEditor::CaseSensitive) {
|
|
||||||
if (prefix == current)
|
|
||||||
return true;
|
|
||||||
} else if (cs == TextEditor::CaseInsensitive) {
|
|
||||||
if (prefix.compare(current, Qt::CaseInsensitive) == 0)
|
|
||||||
return true;
|
|
||||||
} else if (cs == TextEditor::FirstLetterCaseSensitive) {
|
|
||||||
if (prefix.at(0) == current.at(0)
|
|
||||||
&& prefix.midRef(1).compare(current.midRef(1), Qt::CaseInsensitive) == 0)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------
|
// ------------
|
||||||
// ModelAdapter
|
// ModelAdapter
|
||||||
// ------------
|
// ------------
|
||||||
@@ -442,12 +401,12 @@ bool GenericProposalWidget::updateAndCheck(const QString &prefix)
|
|||||||
d->m_model->persistentId(d->m_completionListView->currentIndex().row());
|
d->m_model->persistentId(d->m_completionListView->currentIndex().row());
|
||||||
|
|
||||||
// Filter, sort, etc.
|
// Filter, sort, etc.
|
||||||
d->m_model->reset();
|
if (!d->m_model->isPrefiltered(prefix)) {
|
||||||
if (!prefix.isEmpty())
|
d->m_model->reset();
|
||||||
d->m_model->filter(prefix);
|
if (!prefix.isEmpty())
|
||||||
if (d->m_model->size() == 0
|
d->m_model->filter(prefix);
|
||||||
|| (!d->m_model->keepPerfectMatch(d->m_reason)
|
}
|
||||||
&& isPerfectMatch(prefix, d->m_model))) {
|
if (!d->m_model->hasItemsToPropose(prefix, d->m_reason)) {
|
||||||
d->m_completionListView->reset();
|
d->m_completionListView->reset();
|
||||||
abort();
|
abort();
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "assistenums.h"
|
||||||
|
|
||||||
#include <texteditor/texteditor_global.h>
|
#include <texteditor/texteditor_global.h>
|
||||||
|
|
||||||
namespace TextEditor {
|
namespace TextEditor {
|
||||||
@@ -40,6 +42,7 @@ public:
|
|||||||
virtual ~IAssistProposal();
|
virtual ~IAssistProposal();
|
||||||
|
|
||||||
int basePosition() const;
|
int basePosition() const;
|
||||||
|
virtual bool hasItemsToPropose(const QString &, AssistReason) const { return true; }
|
||||||
virtual bool isFragile() const = 0;
|
virtual bool isFragile() const = 0;
|
||||||
virtual bool isCorrective(TextEditorWidget *editorWidget) const;
|
virtual bool isCorrective(TextEditorWidget *editorWidget) const;
|
||||||
virtual void makeCorrection(TextEditorWidget *editorWidget);
|
virtual void makeCorrection(TextEditorWidget *editorWidget);
|
||||||
|
|||||||
Reference in New Issue
Block a user