forked from qt-creator/qt-creator
Generalized the filtering of completion items.
This commit is contained in:
@@ -1332,12 +1332,18 @@ const QList<LookupItem> QuickFixOperation::typeOf(CPlusPlus::ExpressionAST *ast)
|
||||
}
|
||||
|
||||
CPPQuickFixCollector::CPPQuickFixCollector()
|
||||
: _modelManager(CppTools::CppModelManagerInterface::instance()), _editor(0)
|
||||
: _modelManager(CppTools::CppModelManagerInterface::instance()), _editable(0), _editor(0)
|
||||
{ }
|
||||
|
||||
CPPQuickFixCollector::~CPPQuickFixCollector()
|
||||
{ }
|
||||
|
||||
TextEditor::ITextEditable *CPPQuickFixCollector::editor() const
|
||||
{ return _editable; }
|
||||
|
||||
int CPPQuickFixCollector::startPosition() const
|
||||
{ return _editable->position(); }
|
||||
|
||||
bool CPPQuickFixCollector::supportsEditor(TextEditor::ITextEditable *editor)
|
||||
{ return qobject_cast<CPPEditorEditable *>(editor) != 0; }
|
||||
|
||||
@@ -1348,6 +1354,7 @@ int CPPQuickFixCollector::startCompletion(TextEditor::ITextEditable *editable)
|
||||
{
|
||||
Q_ASSERT(editable != 0);
|
||||
|
||||
_editable = editable;
|
||||
_editor = qobject_cast<CPPEditor *>(editable->widget());
|
||||
Q_ASSERT(_editor != 0);
|
||||
|
||||
|
||||
@@ -151,6 +151,8 @@ public:
|
||||
|
||||
QList<QuickFixOperationPtr> quickFixes() const { return _quickFixes; }
|
||||
|
||||
virtual TextEditor::ITextEditable *editor() const;
|
||||
virtual int startPosition() const;
|
||||
virtual bool supportsEditor(TextEditor::ITextEditable *editor);
|
||||
virtual bool triggersCompletion(TextEditor::ITextEditable *editor);
|
||||
virtual int startCompletion(TextEditor::ITextEditable *editor);
|
||||
@@ -163,6 +165,7 @@ public Q_SLOTS:
|
||||
|
||||
private:
|
||||
CppTools::CppModelManagerInterface *_modelManager;
|
||||
TextEditor::ITextEditable *_editable;
|
||||
CPPEditor *_editor;
|
||||
QList<QuickFixOperationPtr> _quickFixes;
|
||||
};
|
||||
|
||||
@@ -690,6 +690,12 @@ static int startOfOperator(TextEditor::ITextEditable *editor,
|
||||
bool CppCodeCompletion::supportsEditor(TextEditor::ITextEditable *editor)
|
||||
{ return m_manager->isCppEditor(editor); }
|
||||
|
||||
TextEditor::ITextEditable *CppCodeCompletion::editor() const
|
||||
{ return m_editor; }
|
||||
|
||||
int CppCodeCompletion::startPosition() const
|
||||
{ return m_startPosition; }
|
||||
|
||||
bool CppCodeCompletion::triggersCompletion(TextEditor::ITextEditable *editor)
|
||||
{
|
||||
const int pos = editor->position();
|
||||
@@ -1466,12 +1472,11 @@ bool CppCodeCompletion::completeQtMethod(const QList<LookupItem> &results,
|
||||
void CppCodeCompletion::completions(QList<TextEditor::CompletionItem> *completions)
|
||||
{
|
||||
const int length = m_editor->position() - m_startPosition;
|
||||
const QString key = m_editor->textAt(m_startPosition, length);
|
||||
|
||||
if (length == 0)
|
||||
*completions = m_completions;
|
||||
else if (length > 0) {
|
||||
const QString key = m_editor->textAt(m_startPosition, length);
|
||||
|
||||
/* Close on the trailing slash for include completion, to enable the slash to
|
||||
* trigger a new completion list. */
|
||||
if ((m_completionOperator == T_STRING_LITERAL ||
|
||||
@@ -1479,62 +1484,14 @@ void CppCodeCompletion::completions(QList<TextEditor::CompletionItem> *completio
|
||||
return;
|
||||
|
||||
if (m_completionOperator != T_LPAREN) {
|
||||
/*
|
||||
* This code builds a regular expression in order to more intelligently match
|
||||
* camel-case style. This means upper-case characters will be rewritten as follows:
|
||||
*
|
||||
* A => [a-z0-9_]*A (for any but the first capital letter)
|
||||
*
|
||||
* Meaning it allows any sequence of lower-case characters to preceed an
|
||||
* upper-case character. So for example gAC matches getActionController.
|
||||
*
|
||||
* It also implements the first-letter-only case sensitivity.
|
||||
*/
|
||||
QString keyRegExp;
|
||||
keyRegExp += QLatin1Char('^');
|
||||
bool first = true;
|
||||
const QLatin1String wordContinuation("[a-z0-9_]*");
|
||||
foreach (const QChar &c, key) {
|
||||
if (m_caseSensitivity == CaseInsensitive ||
|
||||
(m_caseSensitivity == FirstLetterCaseSensitive && !first)) {
|
||||
filter(m_completions, completions, key, m_caseSensitivity);
|
||||
|
||||
keyRegExp += QLatin1String("(?:");
|
||||
if (c.isUpper() && !first)
|
||||
keyRegExp += wordContinuation;
|
||||
keyRegExp += QRegExp::escape(c.toUpper());
|
||||
keyRegExp += "|";
|
||||
keyRegExp += QRegExp::escape(c.toLower());
|
||||
keyRegExp += QLatin1Char(')');
|
||||
} else {
|
||||
if (c.isUpper() && !first)
|
||||
keyRegExp += wordContinuation;
|
||||
keyRegExp += QRegExp::escape(c);
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
const QRegExp regExp(keyRegExp);
|
||||
|
||||
const bool hasKey = !key.isEmpty();
|
||||
foreach (TextEditor::CompletionItem item, m_completions) {
|
||||
if (regExp.indexIn(item.text) == 0) {
|
||||
if (hasKey) {
|
||||
if (item.text.startsWith(key, Qt::CaseSensitive)) {
|
||||
item.relevance = 2;
|
||||
} else if (m_caseSensitivity != CaseSensitive
|
||||
&& item.text.startsWith(key, Qt::CaseInsensitive)) {
|
||||
item.relevance = 1;
|
||||
}
|
||||
}
|
||||
(*completions) << item;
|
||||
}
|
||||
}
|
||||
} else if (m_completionOperator == T_LPAREN ||
|
||||
m_completionOperator == T_SIGNAL ||
|
||||
m_completionOperator == T_SLOT) {
|
||||
foreach (TextEditor::CompletionItem item, m_completions) {
|
||||
if (item.text.startsWith(key, Qt::CaseInsensitive)) {
|
||||
(*completions) << item;
|
||||
completions->append(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1681,23 +1638,7 @@ bool CppCodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem
|
||||
complete(completionItems.first());
|
||||
return true;
|
||||
} else if (m_partialCompletionEnabled && m_completionOperator != T_LPAREN) {
|
||||
// Compute common prefix
|
||||
QString firstKey = completionItems.first().text;
|
||||
QString lastKey = completionItems.last().text;
|
||||
const int length = qMin(firstKey.length(), lastKey.length());
|
||||
firstKey.truncate(length);
|
||||
lastKey.truncate(length);
|
||||
|
||||
while (firstKey != lastKey) {
|
||||
firstKey.chop(1);
|
||||
lastKey.chop(1);
|
||||
}
|
||||
|
||||
int typedLength = m_editor->position() - m_startPosition;
|
||||
if (!firstKey.isEmpty() && firstKey.length() > typedLength) {
|
||||
m_editor->setCurPos(m_startPosition);
|
||||
m_editor->replace(typedLength, firstKey);
|
||||
}
|
||||
return TextEditor::ICompletionCollector::partiallyComplete(completionItems);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -65,6 +65,8 @@ public:
|
||||
void setObjcEnabled(bool objcEnabled)
|
||||
{ m_objcEnabled = objcEnabled; }
|
||||
|
||||
TextEditor::ITextEditable *editor() const;
|
||||
int startPosition() const;
|
||||
QList<TextEditor::CompletionItem> getCompletions();
|
||||
bool supportsEditor(TextEditor::ITextEditable *editor);
|
||||
bool triggersCompletion(TextEditor::ITextEditable *editor);
|
||||
@@ -77,12 +79,6 @@ public:
|
||||
|
||||
QIcon iconForSymbol(CPlusPlus::Symbol *symbol) const;
|
||||
|
||||
enum CaseSensitivity {
|
||||
CaseInsensitive,
|
||||
CaseSensitive,
|
||||
FirstLetterCaseSensitive
|
||||
};
|
||||
|
||||
CaseSensitivity caseSensitivity() const;
|
||||
void setCaseSensitivity(CaseSensitivity caseSensitivity);
|
||||
|
||||
|
||||
@@ -715,6 +715,12 @@ Qt::CaseSensitivity QmlCodeCompletion::caseSensitivity() const
|
||||
void QmlCodeCompletion::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity)
|
||||
{ m_caseSensitivity = caseSensitivity; }
|
||||
|
||||
TextEditor::ITextEditable *QmlCodeCompletion::editor() const
|
||||
{ return m_editor; }
|
||||
|
||||
int QmlCodeCompletion::startPosition() const
|
||||
{ return m_startPosition; }
|
||||
|
||||
bool QmlCodeCompletion::supportsEditor(TextEditor::ITextEditable *editor)
|
||||
{
|
||||
if (qobject_cast<QmlJSTextEditor *>(editor->widget()))
|
||||
@@ -1007,8 +1013,6 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
|
||||
|
||||
void QmlCodeCompletion::completions(QList<TextEditor::CompletionItem> *completions)
|
||||
{
|
||||
// ### FIXME: this code needs to be generalized.
|
||||
|
||||
const int length = m_editor->position() - m_startPosition;
|
||||
|
||||
if (length == 0)
|
||||
@@ -1016,41 +1020,7 @@ void QmlCodeCompletion::completions(QList<TextEditor::CompletionItem> *completio
|
||||
else if (length > 0) {
|
||||
const QString key = m_editor->textAt(m_startPosition, length);
|
||||
|
||||
/*
|
||||
* This code builds a regular expression in order to more intelligently match
|
||||
* camel-case style. This means upper-case characters will be rewritten as follows:
|
||||
*
|
||||
* A => [a-z0-9_]*A (for any but the first capital letter)
|
||||
*
|
||||
* Meaning it allows any sequence of lower-case characters to preceed an
|
||||
* upper-case character. So for example gAC matches getActionController.
|
||||
*/
|
||||
QString keyRegExp;
|
||||
keyRegExp += QLatin1Char('^');
|
||||
bool first = true;
|
||||
foreach (const QChar &c, key) {
|
||||
if (c.isUpper() && !first) {
|
||||
keyRegExp += QLatin1String("[a-z0-9_]*");
|
||||
keyRegExp += c;
|
||||
} else if (m_caseSensitivity == Qt::CaseInsensitive && c.isLower()) {
|
||||
keyRegExp += QLatin1Char('[');
|
||||
keyRegExp += c;
|
||||
keyRegExp += c.toUpper();
|
||||
keyRegExp += QLatin1Char(']');
|
||||
} else {
|
||||
keyRegExp += QRegExp::escape(c);
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
const QRegExp regExp(keyRegExp, Qt::CaseSensitive);
|
||||
|
||||
foreach (TextEditor::CompletionItem item, m_completions) {
|
||||
if (regExp.indexIn(item.text) == 0) {
|
||||
item.relevance = (key.length() > 0 &&
|
||||
item.text.startsWith(key, Qt::CaseInsensitive)) ? 1 : 0;
|
||||
(*completions) << item;
|
||||
}
|
||||
}
|
||||
filter(m_completions, completions, key, FirstLetterCaseSensitive);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1089,25 +1059,7 @@ bool QmlCodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem
|
||||
}
|
||||
}
|
||||
|
||||
// Compute common prefix
|
||||
QString firstKey = completionItems.first().text;
|
||||
QString lastKey = completionItems.last().text;
|
||||
const int length = qMin(firstKey.length(), lastKey.length());
|
||||
firstKey.truncate(length);
|
||||
lastKey.truncate(length);
|
||||
|
||||
while (firstKey != lastKey) {
|
||||
firstKey.chop(1);
|
||||
lastKey.chop(1);
|
||||
}
|
||||
|
||||
int typedLength = m_editor->position() - m_startPosition;
|
||||
if (!firstKey.isEmpty() && firstKey.length() > typedLength) {
|
||||
m_editor->setCurPos(m_startPosition);
|
||||
m_editor->replace(typedLength, firstKey);
|
||||
}
|
||||
|
||||
return false;
|
||||
return TextEditor::ICompletionCollector::partiallyComplete(completionItems);
|
||||
}
|
||||
|
||||
void QmlCodeCompletion::cleanup()
|
||||
|
||||
@@ -59,6 +59,8 @@ public:
|
||||
Qt::CaseSensitivity caseSensitivity() const;
|
||||
void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity);
|
||||
|
||||
virtual TextEditor::ITextEditable *editor() const;
|
||||
virtual int startPosition() const;
|
||||
virtual bool supportsEditor(TextEditor::ITextEditable *editor);
|
||||
virtual bool triggersCompletion(TextEditor::ITextEditable *editor);
|
||||
virtual int startCompletion(TextEditor::ITextEditable *editor);
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
**************************************************************************/
|
||||
|
||||
#include "icompletioncollector.h"
|
||||
#include "itexteditable.h"
|
||||
#include <QtCore/QRegExp>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace TextEditor;
|
||||
@@ -84,3 +86,84 @@ QList<CompletionItem> ICompletionCollector::getCompletions()
|
||||
return uniquelist;
|
||||
}
|
||||
|
||||
bool ICompletionCollector::partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems)
|
||||
{
|
||||
// Compute common prefix
|
||||
QString firstKey = completionItems.first().text;
|
||||
QString lastKey = completionItems.last().text;
|
||||
const int length = qMin(firstKey.length(), lastKey.length());
|
||||
firstKey.truncate(length);
|
||||
lastKey.truncate(length);
|
||||
|
||||
while (firstKey != lastKey) {
|
||||
firstKey.chop(1);
|
||||
lastKey.chop(1);
|
||||
}
|
||||
|
||||
if (ITextEditable *ed = editor()) {
|
||||
const int typedLength = ed->position() - startPosition();
|
||||
if (!firstKey.isEmpty() && firstKey.length() > typedLength) {
|
||||
ed->setCurPos(startPosition());
|
||||
ed->replace(typedLength, firstKey);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ICompletionCollector::filter(const QList<TextEditor::CompletionItem> &items,
|
||||
QList<TextEditor::CompletionItem> *filteredItems,
|
||||
const QString &key,
|
||||
ICompletionCollector::CaseSensitivity caseSensitivity)
|
||||
{
|
||||
/*
|
||||
* This code builds a regular expression in order to more intelligently match
|
||||
* camel-case style. This means upper-case characters will be rewritten as follows:
|
||||
*
|
||||
* A => [a-z0-9_]*A (for any but the first capital letter)
|
||||
*
|
||||
* Meaning it allows any sequence of lower-case characters to preceed an
|
||||
* upper-case character. So for example gAC matches getActionController.
|
||||
*
|
||||
* It also implements the first-letter-only case sensitivity.
|
||||
*/
|
||||
QString keyRegExp;
|
||||
keyRegExp += QLatin1Char('^');
|
||||
bool first = true;
|
||||
const QLatin1String wordContinuation("[a-z0-9_]*");
|
||||
foreach (const QChar &c, key) {
|
||||
if (caseSensitivity == CaseInsensitive ||
|
||||
(caseSensitivity == FirstLetterCaseSensitive && !first)) {
|
||||
|
||||
keyRegExp += QLatin1String("(?:");
|
||||
if (c.isUpper() && !first)
|
||||
keyRegExp += wordContinuation;
|
||||
keyRegExp += QRegExp::escape(c.toUpper());
|
||||
keyRegExp += "|";
|
||||
keyRegExp += QRegExp::escape(c.toLower());
|
||||
keyRegExp += QLatin1Char(')');
|
||||
} else {
|
||||
if (c.isUpper() && !first)
|
||||
keyRegExp += wordContinuation;
|
||||
keyRegExp += QRegExp::escape(c);
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
const QRegExp regExp(keyRegExp);
|
||||
|
||||
const bool hasKey = !key.isEmpty();
|
||||
foreach (TextEditor::CompletionItem item, items) {
|
||||
if (regExp.indexIn(item.text) == 0) {
|
||||
if (hasKey) {
|
||||
if (item.text.startsWith(key, Qt::CaseSensitive)) {
|
||||
item.relevance = 2;
|
||||
} else if (caseSensitivity != CaseSensitive
|
||||
&& item.text.startsWith(key, Qt::CaseInsensitive)) {
|
||||
item.relevance = 1;
|
||||
}
|
||||
}
|
||||
filteredItems->append(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,10 @@ public:
|
||||
|
||||
virtual QList<CompletionItem> getCompletions();
|
||||
|
||||
/* Returns the current active ITextEditable */
|
||||
virtual ITextEditable *editor() const = 0;
|
||||
virtual int startPosition() const = 0;
|
||||
|
||||
/*
|
||||
* Returns true if this completion collector can be used with the given editor.
|
||||
*/
|
||||
@@ -107,12 +111,25 @@ public:
|
||||
*
|
||||
* Returns whether the completion popup should be closed.
|
||||
*/
|
||||
virtual bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems) = 0;
|
||||
virtual bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems);
|
||||
|
||||
/* Called when it's safe to clean up the completion items.
|
||||
*/
|
||||
virtual void cleanup() = 0;
|
||||
|
||||
// helpers
|
||||
|
||||
enum CaseSensitivity {
|
||||
CaseInsensitive,
|
||||
CaseSensitive,
|
||||
FirstLetterCaseSensitive
|
||||
};
|
||||
|
||||
void filter(const QList<TextEditor::CompletionItem> &items,
|
||||
QList<TextEditor::CompletionItem> *filteredItems,
|
||||
const QString &key,
|
||||
CaseSensitivity caseSensitivity);
|
||||
|
||||
protected:
|
||||
static bool compareChar(const QChar &item, const QChar &other);
|
||||
static bool lessThan(const QString &item, const QString &other);
|
||||
|
||||
Reference in New Issue
Block a user