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:
Nikolai Kosjar
2017-04-27 15:44:37 +02:00
parent a4a78ae8b1
commit fab4dd068e
7 changed files with 108 additions and 57 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 &current = 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;
} }

View File

@@ -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

View File

@@ -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 &current = 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;

View File

@@ -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);