forked from qt-creator/qt-creator
Allow certain characters to also trigger the completion
Now you can type characters like ., ( and : to complete the selected item, depending on the type of the completion item. Task-number: QTCREATORBUG-271 Reviewed-by: Roberto Raggi
This commit is contained in:
@@ -1723,7 +1723,33 @@ QList<TextEditor::CompletionItem> CppCodeCompletion::getCompletions()
|
||||
return completionItems;
|
||||
}
|
||||
|
||||
void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
|
||||
bool CppCodeCompletion::typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar)
|
||||
{
|
||||
if (item.data.canConvert<QString>()) // snippet
|
||||
return false;
|
||||
|
||||
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT)
|
||||
return typedChar == QLatin1Char('(')
|
||||
|| typedChar == QLatin1Char(',');
|
||||
|
||||
if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL)
|
||||
return typedChar == QLatin1Char('/')
|
||||
&& item.text.endsWith(QLatin1Char('/'));
|
||||
|
||||
if (item.data.value<Symbol *>())
|
||||
return typedChar == QLatin1Char(':')
|
||||
|| typedChar == QLatin1Char(';')
|
||||
|| typedChar == QLatin1Char('.')
|
||||
|| typedChar == QLatin1Char(',')
|
||||
|| typedChar == QLatin1Char('(');
|
||||
|
||||
if (item.data.canConvert<CompleteFunctionDeclaration>())
|
||||
return typedChar == QLatin1Char('(');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CppCodeCompletion::complete(const TextEditor::CompletionItem &item, QChar typedChar)
|
||||
{
|
||||
Symbol *symbol = 0;
|
||||
|
||||
@@ -1749,10 +1775,15 @@ void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
|
||||
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
|
||||
toInsert = item.text;
|
||||
extraChars += QLatin1Char(')');
|
||||
|
||||
if (typedChar == QLatin1Char('(')) // Eat the opening parenthesis
|
||||
typedChar = QChar();
|
||||
} else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL) {
|
||||
toInsert = item.text;
|
||||
if (!toInsert.endsWith(QLatin1Char('/')))
|
||||
extraChars += QLatin1Char((m_completionOperator == T_ANGLE_STRING_LITERAL) ? '>' : '"');
|
||||
else if (typedChar == QLatin1Char('/')) // Eat the slash
|
||||
typedChar = QChar();
|
||||
} else {
|
||||
toInsert = item.text;
|
||||
|
||||
@@ -1768,7 +1799,7 @@ void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
|
||||
if (! function->hasReturnType() && (function->identity() && !function->identity()->isDestructorNameId())) {
|
||||
// Don't insert any magic, since the user might have just wanted to select the class
|
||||
|
||||
} else if (function->templateParameterCount() != 0) {
|
||||
} else if (function->templateParameterCount() != 0 && typedChar != QLatin1Char('(')) {
|
||||
// If there are no arguments, then we need the template specification
|
||||
if (function->argumentCount() == 0) {
|
||||
extraChars += QLatin1Char('<');
|
||||
@@ -1777,32 +1808,50 @@ void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
|
||||
if (completionSettings().m_spaceAfterFunctionName)
|
||||
extraChars += QLatin1Char(' ');
|
||||
extraChars += QLatin1Char('(');
|
||||
if (typedChar == QLatin1Char('('))
|
||||
typedChar = QChar();
|
||||
|
||||
// If the function doesn't return anything, automatically place the semicolon,
|
||||
// unless we're doing a scope completion (then it might be function definition).
|
||||
bool endWithSemicolon = function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON;
|
||||
const QChar characterAtCursor = m_editor->characterAt(m_editor->position());
|
||||
bool endWithSemicolon = typedChar == QLatin1Char(';')
|
||||
|| (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON);
|
||||
const QChar semicolon = typedChar.isNull() ? QLatin1Char(';') : typedChar;
|
||||
|
||||
if (endWithSemicolon && characterAtCursor == semicolon) {
|
||||
endWithSemicolon = false;
|
||||
typedChar = QChar();
|
||||
}
|
||||
|
||||
// If the function takes no arguments, automatically place the closing parenthesis
|
||||
if (item.duplicateCount == 0 && ! function->hasArguments()) {
|
||||
extraChars += QLatin1Char(')');
|
||||
if (endWithSemicolon)
|
||||
extraChars += QLatin1Char(';');
|
||||
if (endWithSemicolon) {
|
||||
extraChars += semicolon;
|
||||
typedChar = QChar();
|
||||
}
|
||||
} else if (autoParenthesesEnabled) {
|
||||
const QChar lookAhead = m_editor->characterAt(m_editor->position() + 1);
|
||||
if (MatchingText::shouldInsertMatchingText(lookAhead)) {
|
||||
extraChars += QLatin1Char(')');
|
||||
--cursorOffset;
|
||||
if (endWithSemicolon) {
|
||||
extraChars += QLatin1Char(';');
|
||||
extraChars += semicolon;
|
||||
--cursorOffset;
|
||||
typedChar = QChar();
|
||||
}
|
||||
}
|
||||
// TODO: When an opening parenthesis exists, the "semicolon" should really be
|
||||
// inserted after the matching closing parenthesis.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (autoInsertBrackets && item.data.canConvert<CompleteFunctionDeclaration>()) {
|
||||
if (typedChar == QLatin1Char('('))
|
||||
typedChar = QChar();
|
||||
|
||||
// everything from the closing parenthesis on are extra chars, to
|
||||
// make sure an auto-inserted ")" gets replaced by ") const" if necessary
|
||||
int closingParen = toInsert.lastIndexOf(QLatin1Char(')'));
|
||||
@@ -1811,6 +1860,13 @@ void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
|
||||
}
|
||||
}
|
||||
|
||||
// Append an unhandled typed character, adjusting cursor offset when it had been adjusted before
|
||||
if (!typedChar.isNull()) {
|
||||
extraChars += typedChar;
|
||||
if (cursorOffset != 0)
|
||||
--cursorOffset;
|
||||
}
|
||||
|
||||
// Avoid inserting characters that are already there
|
||||
for (int i = 0; i < extraChars.length(); ++i) {
|
||||
const QChar a = extraChars.at(i);
|
||||
@@ -1836,7 +1892,7 @@ bool CppCodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem
|
||||
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
|
||||
return false;
|
||||
} else if (completionItems.count() == 1) {
|
||||
complete(completionItems.first());
|
||||
complete(completionItems.first(), QChar());
|
||||
return true;
|
||||
} else if (m_completionOperator != T_LPAREN) {
|
||||
return TextEditor::ICompletionCollector::partiallyComplete(completionItems);
|
||||
|
||||
@@ -78,7 +78,8 @@ public:
|
||||
int startCompletion(TextEditor::ITextEditable *editor);
|
||||
void completions(QList<TextEditor::CompletionItem> *completions);
|
||||
|
||||
void complete(const TextEditor::CompletionItem &item);
|
||||
bool typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar);
|
||||
void complete(const TextEditor::CompletionItem &item, QChar typedChar);
|
||||
bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems);
|
||||
void cleanup();
|
||||
|
||||
|
||||
@@ -878,8 +878,19 @@ void CodeCompletion::completions(QList<TextEditor::CompletionItem> *completions)
|
||||
}
|
||||
}
|
||||
|
||||
void CodeCompletion::complete(const TextEditor::CompletionItem &item)
|
||||
bool CodeCompletion::typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar)
|
||||
{
|
||||
if (item.data.canConvert<QString>()) // snippet
|
||||
return false;
|
||||
|
||||
return (item.text.endsWith(QLatin1String(": ")) && typedChar == QLatin1Char(':'))
|
||||
|| (item.text.endsWith(QLatin1Char('.')) && typedChar == QLatin1Char('.'));
|
||||
}
|
||||
|
||||
void CodeCompletion::complete(const TextEditor::CompletionItem &item, QChar typedChar)
|
||||
{
|
||||
Q_UNUSED(typedChar) // Currently always included in the completion item when used
|
||||
|
||||
QString toInsert = item.text;
|
||||
|
||||
if (QmlJSTextEditor *edit = qobject_cast<QmlJSTextEditor *>(m_editor->widget())) {
|
||||
@@ -895,7 +906,7 @@ void CodeCompletion::complete(const TextEditor::CompletionItem &item)
|
||||
QString replacableChars;
|
||||
if (toInsert.endsWith(QLatin1String(": ")))
|
||||
replacableChars = QLatin1String(": ");
|
||||
else if (toInsert.endsWith(QLatin1String(".")))
|
||||
else if (toInsert.endsWith(QLatin1Char('.')))
|
||||
replacableChars = QLatin1String(".");
|
||||
|
||||
int replacedLength = 0;
|
||||
@@ -924,7 +935,7 @@ bool CodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem> &
|
||||
const TextEditor::CompletionItem item = completionItems.first();
|
||||
|
||||
if (!item.data.canConvert<QString>()) {
|
||||
complete(item);
|
||||
complete(item, QChar());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,8 @@ public:
|
||||
virtual bool triggersCompletion(TextEditor::ITextEditable *editor);
|
||||
virtual int startCompletion(TextEditor::ITextEditable *editor);
|
||||
virtual void completions(QList<TextEditor::CompletionItem> *completions);
|
||||
virtual void complete(const TextEditor::CompletionItem &item);
|
||||
virtual bool typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar);
|
||||
virtual void complete(const TextEditor::CompletionItem &item, QChar typedChar);
|
||||
virtual bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems);
|
||||
virtual QList<TextEditor::CompletionItem> getCompletions();
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ CompletionSupport::CompletionSupport()
|
||||
|
||||
void CompletionSupport::performCompletion(const CompletionItem &item)
|
||||
{
|
||||
item.collector->complete(item);
|
||||
item.collector->complete(item, m_completionList->typedChar());
|
||||
m_checkCompletionTrigger = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -199,6 +199,11 @@ void CompletionWidget::showCompletions(int startPos)
|
||||
setFocus();
|
||||
}
|
||||
|
||||
QChar CompletionWidget::typedChar() const
|
||||
{
|
||||
return m_completionListView->m_typedChar;
|
||||
}
|
||||
|
||||
void CompletionWidget::updatePositionAndSize(int startPos)
|
||||
{
|
||||
// Determine size by calculating the space of the visible items
|
||||
@@ -415,6 +420,16 @@ bool CompletionListView::event(QEvent *e)
|
||||
}
|
||||
|
||||
if (forwardKeys && ! m_quickFix) {
|
||||
if (ke->text().length() == 1 && currentIndex().isValid() && qApp->focusWidget() == this) {
|
||||
QChar typedChar = ke->text().at(0);
|
||||
const CompletionItem &item = m_model->itemAt(currentIndex());
|
||||
if (item.collector->typedCharCompletes(item, typedChar)) {
|
||||
m_typedChar = typedChar;
|
||||
m_completionWidget->closeList(currentIndex());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
m_blockFocusOut = true;
|
||||
QApplication::sendEvent(m_editorWidget, e);
|
||||
m_blockFocusOut = false;
|
||||
|
||||
@@ -61,6 +61,8 @@ public:
|
||||
void setCompletionItems(const QList<TextEditor::CompletionItem> &completionitems);
|
||||
void showCompletions(int startPos);
|
||||
|
||||
QChar typedChar() const;
|
||||
|
||||
signals:
|
||||
void itemSelected(const TextEditor::CompletionItem &item);
|
||||
void completionListClosed();
|
||||
@@ -115,6 +117,7 @@ private:
|
||||
CompletionSupport *m_support;
|
||||
QPointer<CompletionInfoFrame> m_infoFrame;
|
||||
QTimer m_infoTimer;
|
||||
QChar m_typedChar;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -111,9 +111,19 @@ public:
|
||||
*/
|
||||
virtual void completions(QList<CompletionItem> *completions) = 0;
|
||||
|
||||
/* This method should complete the given completion item.
|
||||
/**
|
||||
* This method should return true when the given typed character should cause
|
||||
* the selected completion item to be completed.
|
||||
*/
|
||||
virtual void complete(const CompletionItem &item) = 0;
|
||||
virtual bool typedCharCompletes(const CompletionItem &item, QChar typedChar) = 0;
|
||||
|
||||
/**
|
||||
* This method should complete the given completion item.
|
||||
*
|
||||
* \param typedChar Non-null when completion was triggered by typing a
|
||||
* character. Possible values depend on typedCharCompletes()
|
||||
*/
|
||||
virtual void complete(const CompletionItem &item, QChar typedChar) = 0;
|
||||
|
||||
/* This method gives the completion collector a chance to partially complete
|
||||
* based on a set of items. The general use case is to complete the common
|
||||
@@ -153,6 +163,17 @@ public:
|
||||
IQuickFixCollector(QObject *parent = 0) : ICompletionCollector(parent) {}
|
||||
virtual ~IQuickFixCollector() {}
|
||||
|
||||
virtual bool typedCharCompletes(const CompletionItem &, QChar)
|
||||
{ return false; }
|
||||
|
||||
virtual void fix(const TextEditor::CompletionItem &item) = 0;
|
||||
|
||||
virtual void complete(const CompletionItem &item, QChar typedChar)
|
||||
{
|
||||
Q_UNUSED(typedChar)
|
||||
fix(item);
|
||||
}
|
||||
|
||||
virtual bool triggersCompletion(TextEditor::ITextEditable *)
|
||||
{ return false; }
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ void QuickFixCollector::completions(QList<TextEditor::CompletionItem> *quickFixI
|
||||
}
|
||||
}
|
||||
|
||||
void QuickFixCollector::complete(const TextEditor::CompletionItem &item)
|
||||
void QuickFixCollector::fix(const TextEditor::CompletionItem &item)
|
||||
{
|
||||
const int index = item.data.toInt();
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
virtual bool triggersCompletion(TextEditor::ITextEditable *editor);
|
||||
virtual int startCompletion(TextEditor::ITextEditable *editor);
|
||||
virtual void completions(QList<TextEditor::CompletionItem> *completions);
|
||||
virtual void complete(const TextEditor::CompletionItem &item);
|
||||
virtual void fix(const TextEditor::CompletionItem &item);
|
||||
virtual void cleanup();
|
||||
|
||||
virtual TextEditor::QuickFixState *initializeCompletion(TextEditor::ITextEditable *editable) = 0;
|
||||
|
||||
Reference in New Issue
Block a user