Clang: Add ClangPreprocessorAssistProposalItem

Change-Id: Ifb27b9a21b9a2fe9a04496bbb70fa3fa07d1f89c
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
This commit is contained in:
Marco Bubke
2016-01-28 16:39:05 +01:00
parent bb3b7307ef
commit ecca269292
8 changed files with 286 additions and 49 deletions
@@ -42,21 +42,21 @@ using namespace ClangBackEnd;
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
bool ClangAssistProposalItem::prematurelyApplies(const QChar &typedChar) const bool ClangAssistProposalItem::prematurelyApplies(const QChar &typedCharacter) const
{ {
bool applies = false; bool applies = false;
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT)
applies = QString::fromLatin1("(,").contains(typedChar); applies = QString::fromLatin1("(,").contains(typedCharacter);
else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL) else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL)
applies = (typedChar == QLatin1Char('/')) && text().endsWith(QLatin1Char('/')); applies = (typedCharacter == QLatin1Char('/')) && text().endsWith(QLatin1Char('/'));
else if (codeCompletion().completionKind() == CodeCompletion::ObjCMessageCompletionKind) else if (codeCompletion().completionKind() == CodeCompletion::ObjCMessageCompletionKind)
applies = QString::fromLatin1(";.,").contains(typedChar); applies = QString::fromLatin1(";.,").contains(typedCharacter);
else else
applies = QString::fromLatin1(";.,:(").contains(typedChar); applies = QString::fromLatin1(";.,:(").contains(typedCharacter);
if (applies) if (applies)
m_typedChar = typedChar; m_typedCharacter = typedCharacter;
return applies; return applies;
} }
@@ -87,23 +87,16 @@ void ClangAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget,
const CodeCompletion ccr = codeCompletion(); const CodeCompletion ccr = codeCompletion();
QString textToBeInserted = text(); QString textToBeInserted = text();
QString extraChars; QString extraCharacters;
int extraLength = 0; int extraLength = 0;
int cursorOffset = 0; int cursorOffset = 0;
bool autoParenthesesEnabled = true; bool autoParenthesesEnabled = true;
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) { if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
extraChars += QLatin1Char(')'); extraCharacters += QLatin1Char(')');
if (m_typedChar == QLatin1Char('(')) // Eat the opening parenthesis if (m_typedCharacter == QLatin1Char('(')) // Eat the opening parenthesis
m_typedChar = QChar(); m_typedCharacter = QChar();
} else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL) { } else if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind) {
if (!textToBeInserted.endsWith(QLatin1Char('/'))) {
extraChars += QLatin1Char((m_completionOperator == T_ANGLE_STRING_LITERAL) ? '>' : '"');
} else {
if (m_typedChar == QLatin1Char('/')) // Eat the slash
m_typedChar = QChar();
}
} else if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind) {
CompletionChunksToTextConverter converter; CompletionChunksToTextConverter converter;
converter.setupForKeywords(); converter.setupForKeywords();
@@ -131,42 +124,42 @@ void ClangAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget,
// When the user typed the opening parenthesis, he'll likely also type the closing one, // When the user typed the opening parenthesis, he'll likely also type the closing one,
// in which case it would be annoying if we put the cursor after the already automatically // in which case it would be annoying if we put the cursor after the already automatically
// inserted closing parenthesis. // inserted closing parenthesis.
const bool skipClosingParenthesis = m_typedChar != QLatin1Char('('); const bool skipClosingParenthesis = m_typedCharacter != QLatin1Char('(');
if (completionSettings.m_spaceAfterFunctionName) if (completionSettings.m_spaceAfterFunctionName)
extraChars += QLatin1Char(' '); extraCharacters += QLatin1Char(' ');
extraChars += QLatin1Char('('); extraCharacters += QLatin1Char('(');
if (m_typedChar == QLatin1Char('(')) if (m_typedCharacter == QLatin1Char('('))
m_typedChar = QChar(); m_typedCharacter = QChar();
// If the function doesn't return anything, automatically place the semicolon, // If the function doesn't return anything, automatically place the semicolon,
// unless we're doing a scope completion (then it might be function definition). // unless we're doing a scope completion (then it might be function definition).
const QChar characterAtCursor = editorWidget->characterAt(editorWidget->position()); const QChar characterAtCursor = editorWidget->characterAt(editorWidget->position());
bool endWithSemicolon = m_typedChar == QLatin1Char(';')/* bool endWithSemicolon = m_typedCharacter == QLatin1Char(';')/*
|| (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON)*/; //### || (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON)*/; //###
const QChar semicolon = m_typedChar.isNull() ? QLatin1Char(';') : m_typedChar; const QChar semicolon = m_typedCharacter.isNull() ? QLatin1Char(';') : m_typedCharacter;
if (endWithSemicolon && characterAtCursor == semicolon) { if (endWithSemicolon && characterAtCursor == semicolon) {
endWithSemicolon = false; endWithSemicolon = false;
m_typedChar = QChar(); m_typedCharacter = QChar();
} }
// If the function takes no arguments, automatically place the closing parenthesis // If the function takes no arguments, automatically place the closing parenthesis
if (!isOverloaded() && !ccr.hasParameters() && skipClosingParenthesis) { if (!isOverloaded() && !ccr.hasParameters() && skipClosingParenthesis) {
extraChars += QLatin1Char(')'); extraCharacters += QLatin1Char(')');
if (endWithSemicolon) { if (endWithSemicolon) {
extraChars += semicolon; extraCharacters += semicolon;
m_typedChar = QChar(); m_typedCharacter = QChar();
} }
} else if (autoParenthesesEnabled) { } else if (autoParenthesesEnabled) {
const QChar lookAhead = editorWidget->characterAt(editorWidget->position() + 1); const QChar lookAhead = editorWidget->characterAt(editorWidget->position() + 1);
if (MatchingText::shouldInsertMatchingText(lookAhead)) { if (MatchingText::shouldInsertMatchingText(lookAhead)) {
extraChars += QLatin1Char(')'); extraCharacters += QLatin1Char(')');
--cursorOffset; --cursorOffset;
if (endWithSemicolon) { if (endWithSemicolon) {
extraChars += semicolon; extraCharacters += semicolon;
--cursorOffset; --cursorOffset;
m_typedChar = QChar(); m_typedCharacter = QChar();
} }
} }
} }
@@ -187,8 +180,8 @@ void ClangAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget,
} }
// Append an unhandled typed character, adjusting cursor offset when it had been adjusted before // Append an unhandled typed character, adjusting cursor offset when it had been adjusted before
if (!m_typedChar.isNull()) { if (!m_typedCharacter.isNull()) {
extraChars += m_typedChar; extraCharacters += m_typedCharacter;
if (cursorOffset != 0) if (cursorOffset != 0)
--cursorOffset; --cursorOffset;
} }
@@ -205,8 +198,8 @@ void ClangAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget,
break; break;
} }
} }
for (int i = 0; i < extraChars.length(); ++i) { for (int i = 0; i < extraCharacters.length(); ++i) {
const QChar a = extraChars.at(i); const QChar a = extraCharacters.at(i);
const QChar b = editorWidget->characterAt(editorWidget->position() + i + existLength); const QChar b = editorWidget->characterAt(editorWidget->position() + i + existLength);
if (a == b) if (a == b)
++extraLength; ++extraLength;
@@ -214,7 +207,7 @@ void ClangAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget,
break; break;
} }
textToBeInserted += extraChars; textToBeInserted += extraCharacters;
// Insert the remainder of the name // Insert the remainder of the name
const int length = editorWidget->position() - basePosition + existLength + extraLength; const int length = editorWidget->position() - basePosition + existLength + extraLength;
@@ -39,9 +39,7 @@ class ClangAssistProposalItem final : public TextEditor::AssistProposalItemInter
{ {
friend bool operator<(const ClangAssistProposalItem &first, const ClangAssistProposalItem &second); friend bool operator<(const ClangAssistProposalItem &first, const ClangAssistProposalItem &second);
public: public:
ClangAssistProposalItem() {} bool prematurelyApplies(const QChar &typedCharacter) const final;
bool prematurelyApplies(const QChar &c) const final;
bool implicitlyApplies() const final; bool implicitlyApplies() const final;
void apply(TextEditor::TextEditorWidget *editorWidget, int basePosition) const final; void apply(TextEditor::TextEditorWidget *editorWidget, int basePosition) const final;
@@ -66,7 +64,7 @@ private:
QList<ClangBackEnd::CodeCompletion> m_overloads; QList<ClangBackEnd::CodeCompletion> m_overloads;
QString m_text; QString m_text;
unsigned m_completionOperator; unsigned m_completionOperator;
mutable QChar m_typedChar; mutable QChar m_typedCharacter;
}; };
} // namespace Internal } // namespace Internal
@@ -27,6 +27,7 @@ SOURCES += \
clangfunctionhintmodel.cpp \ clangfunctionhintmodel.cpp \
clanghighlightingmarksreporter.cpp \ clanghighlightingmarksreporter.cpp \
clangmodelmanagersupport.cpp \ clangmodelmanagersupport.cpp \
clangpreprocessorassistproposalitem.cpp \
clangtextmark.cpp \ clangtextmark.cpp \
clangutils.cpp clangutils.cpp
@@ -53,6 +54,7 @@ HEADERS += \
clangfunctionhintmodel.h \ clangfunctionhintmodel.h \
clanghighlightingmarksreporter.h \ clanghighlightingmarksreporter.h \
clangmodelmanagersupport.h \ clangmodelmanagersupport.h \
clangpreprocessorassistproposalitem.h \
clangtextmark.h \ clangtextmark.h \
clangutils.h clangutils.h
@@ -79,6 +79,8 @@ QtcPlugin {
"clanghighlightingmarksreporter.h", "clanghighlightingmarksreporter.h",
"clangmodelmanagersupport.cpp", "clangmodelmanagersupport.cpp",
"clangmodelmanagersupport.h", "clangmodelmanagersupport.h",
"clangpreprocessorassistproposalitem.cpp",
"clangpreprocessorassistproposalitem.h",
"clangtextmark.cpp", "clangtextmark.cpp",
"clangtextmark.h", "clangtextmark.h",
"clangutils.cpp", "clangutils.cpp",
@@ -33,6 +33,7 @@
#include "clangeditordocumentprocessor.h" #include "clangeditordocumentprocessor.h"
#include "clangfunctionhintmodel.h" #include "clangfunctionhintmodel.h"
#include "clangcompletionchunkstotextconverter.h" #include "clangcompletionchunkstotextconverter.h"
#include "clangpreprocessorassistproposalitem.h"
#include <cpptools/cppdoxygen.h> #include <cpptools/cppdoxygen.h>
#include <cpptools/cppmodelmanager.h> #include <cpptools/cppmodelmanager.h>
@@ -533,11 +534,11 @@ void ClangCompletionAssistProcessor::completeIncludePath(const QString &realPath
if (fileInfo.isDir()) if (fileInfo.isDir())
text += QLatin1Char('/'); text += QLatin1Char('/');
auto *item = new ClangAssistProposalItem; // TODO: Add IncludeAssistProposalItem auto *item = new ClangPreprocessorAssistProposalItem;
item->setText(text); item->setText(text);
//item->setDetail(hint); item->setDetail(hint);
//item->setIcon(m_icons.keywordIcon()); item->setIcon(m_icons.keywordIcon());
item->keepCompletionOperator(m_completionOperator); item->setCompletionOperator(m_completionOperator);
m_completions.append(item); m_completions.append(item);
} }
} }
@@ -567,11 +568,11 @@ void ClangCompletionAssistProcessor::addCompletionItem(const QString &text,
const QIcon &icon, const QIcon &icon,
int order) int order)
{ {
ClangAssistProposalItem *item = new ClangAssistProposalItem; auto *item = new ClangPreprocessorAssistProposalItem;
item->setText(text); item->setText(text);
//item->setIcon(icon); TODO: Add item for macros and includes item->setIcon(icon);
item->setOrder(order); item->setOrder(order);
item->keepCompletionOperator(m_completionOperator); item->setCompletionOperator(m_completionOperator);
m_completions.append(item); m_completions.append(item);
} }
@@ -0,0 +1,169 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#include "clangpreprocessorassistproposalitem.h"
#include <texteditor/texteditor.h>
#include <cplusplus/Token.h>
namespace ClangCodeModel {
bool ClangPreprocessorAssistProposalItem::prematurelyApplies(const QChar &typedCharacter) const
{
bool applies = false;
if (isInclude())
applies = typedCharacter == QLatin1Char('/') && text().endsWith(QLatin1Char('/'));
if (applies)
m_typedCharacter = typedCharacter;
return applies;
}
bool ClangPreprocessorAssistProposalItem::implicitlyApplies() const
{
return false;
}
void ClangPreprocessorAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget,
int basePosition) const
{
// TODO move in an extra class under tests
QString textToBeInserted = text();
QString extraCharacters;
int extraLength = 0;
int cursorOffset = 0;
if (isInclude()) {
if (!textToBeInserted.endsWith(QLatin1Char('/'))) {
extraCharacters += QLatin1Char((m_completionOperator == CPlusPlus::T_ANGLE_STRING_LITERAL) ? '>' : '"');
} else {
if (m_typedCharacter == QLatin1Char('/')) // Eat the slash
m_typedCharacter = QChar();
}
}
if (!m_typedCharacter.isNull()) {
extraCharacters += m_typedCharacter;
if (cursorOffset != 0)
--cursorOffset;
}
// Avoid inserting characters that are already there
const int endsPosition = editorWidget->position(TextEditor::EndOfLinePosition);
const QString existingText = editorWidget->textAt(editorWidget->position(), endsPosition - editorWidget->position());
int existLength = 0;
if (!existingText.isEmpty()) {
// Calculate the exist length in front of the extra chars
existLength = textToBeInserted.length() - (editorWidget->position() - basePosition);
while (!existingText.startsWith(textToBeInserted.right(existLength))) {
if (--existLength == 0)
break;
}
}
for (int i = 0; i < extraCharacters.length(); ++i) {
const QChar a = extraCharacters.at(i);
const QChar b = editorWidget->characterAt(editorWidget->position() + i + existLength);
if (a == b)
++extraLength;
else
break;
}
textToBeInserted += extraCharacters;
// Insert the remainder of the name
const int length = editorWidget->position() - basePosition + existLength + extraLength;
const auto textToBeReplaced = editorWidget->textAt(basePosition, length);
if (textToBeReplaced != textToBeInserted) {
editorWidget->setCursorPosition(basePosition);
editorWidget->replace(length, textToBeInserted);
if (cursorOffset)
editorWidget->setCursorPosition(editorWidget->position() + cursorOffset);
}
}
void ClangPreprocessorAssistProposalItem::setText(const QString &text)
{
m_text = text;
}
QString ClangPreprocessorAssistProposalItem::text() const
{
return m_text;
}
void ClangPreprocessorAssistProposalItem::setIcon(const QIcon &icon)
{
m_icon = icon;
}
QIcon ClangPreprocessorAssistProposalItem::icon() const
{
return m_icon;
}
void ClangPreprocessorAssistProposalItem::setDetail(const QString &detail)
{
m_detail = detail;
}
QString ClangPreprocessorAssistProposalItem::detail() const
{
return QString();
}
bool ClangPreprocessorAssistProposalItem::isSnippet() const
{
return false;
}
bool ClangPreprocessorAssistProposalItem::isValid() const
{
return true;
}
quint64 ClangPreprocessorAssistProposalItem::hash() const
{
return 0;
}
void ClangPreprocessorAssistProposalItem::setCompletionOperator(uint completionOperator)
{
m_completionOperator = completionOperator;
}
bool ClangPreprocessorAssistProposalItem::isInclude() const
{
return m_completionOperator == CPlusPlus::T_STRING_LITERAL
|| m_completionOperator == CPlusPlus::T_ANGLE_STRING_LITERAL;
}
} // namespace ClangCodeModel
@@ -0,0 +1,72 @@
/****************************************************************************
**
** 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 CLANGCODEMODEL_PREPROCESSORASSISTPROPOSALITEM_H
#define CLANGCODEMODEL_PREPROCESSORASSISTPROPOSALITEM_H
#include <texteditor/codeassist/assistproposaliteminterface.h>
#include <QIcon>
#include <QString>
namespace ClangCodeModel {
class ClangPreprocessorAssistProposalItem final : public TextEditor::AssistProposalItemInterface
{
public:
bool prematurelyApplies(const QChar &typedChar) const final;
virtual bool implicitlyApplies() const final;
void apply(TextEditor::TextEditorWidget *editorWidget, int basePosition) const final;
void setText(const QString &text);
QString text() const final;
void setIcon(const QIcon &icon);
QIcon icon() const final;
void setDetail(const QString &detail);
QString detail() const final;
bool isSnippet() const final;
bool isValid() const final;
quint64 hash() const final;
void setCompletionOperator(uint completionOperator);
private:
bool isInclude() const;
private:
QString m_text;
QString m_detail;
QIcon m_icon;
uint m_completionOperator;
mutable QChar m_typedCharacter;
};
} // namespace ClangCodeModel
#endif // CLANGCODEMODEL_PREPROCESSORASSISTPROPOSALITEM_H
@@ -48,7 +48,7 @@ public:
virtual QString text() const = 0; virtual QString text() const = 0;
virtual bool implicitlyApplies() const = 0; virtual bool implicitlyApplies() const = 0;
virtual bool prematurelyApplies(const QChar &character) const = 0; virtual bool prematurelyApplies(const QChar &typedCharacter) const = 0;
virtual void apply(TextEditorWidget *editorWidget, int basePosition) const = 0; virtual void apply(TextEditorWidget *editorWidget, int basePosition) const = 0;
virtual QIcon icon() const = 0; virtual QIcon icon() const = 0;
virtual QString detail() const = 0; virtual QString detail() const = 0;