Clang: Remove TextEditorWidget from AssistProposalItemInterface

Change-Id: I0ac924f88c1347d1b0027c47118b7ed21daf4869
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
Reviewed-by: David Schulz <david.schulz@theqtcompany.com>
This commit is contained in:
Marco Bubke
2016-01-19 14:54:59 +01:00
parent a0cc107b74
commit f41c6b0c4b
22 changed files with 373 additions and 128 deletions

View File

@@ -66,22 +66,7 @@ bool ClangAssistProposalItem::implicitlyApplies() const
return false; return false;
} }
static bool hasOnlyBlanksBeforeCursorInLine(QTextCursor textCursor) void ClangAssistProposalItem::apply(TextEditor::TextDocumentManipulatorInterface &manipulator,
{
textCursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
const auto textBeforeCursor = textCursor.selectedText();
const auto nonSpace = std::find_if(textBeforeCursor.cbegin(),
textBeforeCursor.cend(),
[] (const QChar &signBeforeCursor) {
return !signBeforeCursor.isSpace();
});
return nonSpace == textBeforeCursor.cend();
}
void ClangAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget,
int basePosition) const int basePosition) const
{ {
const CodeCompletion ccr = codeCompletion(); const CodeCompletion ccr = codeCompletion();
@@ -134,7 +119,7 @@ void ClangAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget,
// 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 = manipulator.characterAt(manipulator.currentPosition());
bool endWithSemicolon = m_typedCharacter == 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_typedCharacter.isNull() ? QLatin1Char(';') : m_typedCharacter; const QChar semicolon = m_typedCharacter.isNull() ? QLatin1Char(';') : m_typedCharacter;
@@ -152,7 +137,7 @@ void ClangAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget,
m_typedCharacter = QChar(); m_typedCharacter = QChar();
} }
} else if (autoParenthesesEnabled) { } else if (autoParenthesesEnabled) {
const QChar lookAhead = editorWidget->characterAt(editorWidget->position() + 1); const QChar lookAhead = manipulator.characterAt(manipulator.currentPosition() + 1);
if (MatchingText::shouldInsertMatchingText(lookAhead)) { if (MatchingText::shouldInsertMatchingText(lookAhead)) {
extraCharacters += QLatin1Char(')'); extraCharacters += QLatin1Char(')');
--cursorOffset; --cursorOffset;
@@ -187,12 +172,12 @@ void ClangAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget,
} }
// Avoid inserting characters that are already there // Avoid inserting characters that are already there
const int endsPosition = editorWidget->position(TextEditor::EndOfLinePosition); const int endsPosition = manipulator.positionAt(TextEditor::EndOfLinePosition);
const QString existingText = editorWidget->textAt(editorWidget->position(), endsPosition - editorWidget->position()); const QString existingText = manipulator.textAt(manipulator.currentPosition(), endsPosition - manipulator.currentPosition());
int existLength = 0; int existLength = 0;
if (!existingText.isEmpty() && ccr.completionKind() != CodeCompletion::KeywordCompletionKind) { if (!existingText.isEmpty() && ccr.completionKind() != CodeCompletion::KeywordCompletionKind) {
// Calculate the exist length in front of the extra chars // Calculate the exist length in front of the extra chars
existLength = textToBeInserted.length() - (editorWidget->position() - basePosition); existLength = textToBeInserted.length() - (manipulator.currentPosition() - basePosition);
while (!existingText.startsWith(textToBeInserted.right(existLength))) { while (!existingText.startsWith(textToBeInserted.right(existLength))) {
if (--existLength == 0) if (--existLength == 0)
break; break;
@@ -200,7 +185,7 @@ void ClangAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget,
} }
for (int i = 0; i < extraCharacters.length(); ++i) { for (int i = 0; i < extraCharacters.length(); ++i) {
const QChar a = extraCharacters.at(i); const QChar a = extraCharacters.at(i);
const QChar b = editorWidget->characterAt(editorWidget->position() + i + existLength); const QChar b = manipulator.characterAt(manipulator.currentPosition() + i + existLength);
if (a == b) if (a == b)
++extraLength; ++extraLength;
else else
@@ -209,27 +194,15 @@ void ClangAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget,
textToBeInserted += extraCharacters; textToBeInserted += extraCharacters;
// Insert the remainder of the name const int length = manipulator.currentPosition() - basePosition + existLength + extraLength;
const int length = editorWidget->position() - basePosition + existLength + extraLength;
const auto textToBeReplaced = editorWidget->textAt(basePosition, length);
if (textToBeReplaced != textToBeInserted) { const bool isReplaced = manipulator.replace(basePosition, length, textToBeInserted);
editorWidget->setCursorPosition(basePosition); if (isReplaced) {
editorWidget->replace(length, textToBeInserted);
if (cursorOffset) if (cursorOffset)
editorWidget->setCursorPosition(editorWidget->position() + cursorOffset); manipulator.setCursorPosition(manipulator.currentPosition() + cursorOffset);
// indent the statement if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind)
if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind) { manipulator.autoIndent(basePosition, textToBeInserted.size());
auto selectionCursor = editorWidget->textCursor();
selectionCursor.setPosition(basePosition);
selectionCursor.setPosition(basePosition + textToBeInserted.size(), QTextCursor::KeepAnchor);
auto basePositionCursor = editorWidget->textCursor();
basePositionCursor.setPosition(basePosition);
if (hasOnlyBlanksBeforeCursorInLine(basePositionCursor))
editorWidget->textDocument()->autoIndent(selectionCursor);
}
} }
} }

View File

@@ -41,7 +41,7 @@ class ClangAssistProposalItem final : public TextEditor::AssistProposalItemInter
public: public:
bool prematurelyApplies(const QChar &typedCharacter) const final; bool prematurelyApplies(const QChar &typedCharacter) const final;
bool implicitlyApplies() const final; bool implicitlyApplies() const final;
void apply(TextEditor::TextEditorWidget *editorWidget, int basePosition) const final; void apply(TextEditor::TextDocumentManipulatorInterface &manipulator, int basePosition) const final;
void setText(const QString &text); void setText(const QString &text);
QString text() const final; QString text() const final;

View File

@@ -49,7 +49,7 @@ bool ClangPreprocessorAssistProposalItem::implicitlyApplies() const
return false; return false;
} }
void ClangPreprocessorAssistProposalItem::apply(TextEditor::TextEditorWidget *editorWidget, void ClangPreprocessorAssistProposalItem::apply(TextEditor::TextDocumentManipulatorInterface &manipulator,
int basePosition) const int basePosition) const
{ {
// TODO move in an extra class under tests // TODO move in an extra class under tests
@@ -76,12 +76,12 @@ void ClangPreprocessorAssistProposalItem::apply(TextEditor::TextEditorWidget *ed
} }
// Avoid inserting characters that are already there // Avoid inserting characters that are already there
const int endsPosition = editorWidget->position(TextEditor::EndOfLinePosition); const int endsPosition = manipulator.positionAt(TextEditor::EndOfLinePosition);
const QString existingText = editorWidget->textAt(editorWidget->position(), endsPosition - editorWidget->position()); const QString existingText = manipulator.textAt(manipulator.currentPosition(), endsPosition - manipulator.currentPosition());
int existLength = 0; int existLength = 0;
if (!existingText.isEmpty()) { if (!existingText.isEmpty()) {
// Calculate the exist length in front of the extra chars // Calculate the exist length in front of the extra chars
existLength = textToBeInserted.length() - (editorWidget->position() - basePosition); existLength = textToBeInserted.length() - (manipulator.currentPosition() - basePosition);
while (!existingText.startsWith(textToBeInserted.right(existLength))) { while (!existingText.startsWith(textToBeInserted.right(existLength))) {
if (--existLength == 0) if (--existLength == 0)
break; break;
@@ -89,7 +89,7 @@ void ClangPreprocessorAssistProposalItem::apply(TextEditor::TextEditorWidget *ed
} }
for (int i = 0; i < extraCharacters.length(); ++i) { for (int i = 0; i < extraCharacters.length(); ++i) {
const QChar a = extraCharacters.at(i); const QChar a = extraCharacters.at(i);
const QChar b = editorWidget->characterAt(editorWidget->position() + i + existLength); const QChar b = manipulator.characterAt(manipulator.currentPosition() + i + existLength);
if (a == b) if (a == b)
++extraLength; ++extraLength;
else else
@@ -99,15 +99,11 @@ void ClangPreprocessorAssistProposalItem::apply(TextEditor::TextEditorWidget *ed
textToBeInserted += extraCharacters; 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 = manipulator.currentPosition() - basePosition + existLength + extraLength;
const auto textToBeReplaced = editorWidget->textAt(basePosition, length);
if (textToBeReplaced != textToBeInserted) { const bool isReplaced = manipulator.replace(basePosition, length, textToBeInserted);
editorWidget->setCursorPosition(basePosition); if (isReplaced && cursorOffset)
editorWidget->replace(length, textToBeInserted); manipulator.setCursorPosition(manipulator.currentPosition() + cursorOffset);
if (cursorOffset)
editorWidget->setCursorPosition(editorWidget->position() + cursorOffset);
}
} }
void ClangPreprocessorAssistProposalItem::setText(const QString &text) void ClangPreprocessorAssistProposalItem::setText(const QString &text)

View File

@@ -38,7 +38,8 @@ class ClangPreprocessorAssistProposalItem final : public TextEditor::AssistPropo
public: public:
bool prematurelyApplies(const QChar &typedChar) const final; bool prematurelyApplies(const QChar &typedChar) const final;
virtual bool implicitlyApplies() const final; virtual bool implicitlyApplies() const final;
void apply(TextEditor::TextEditorWidget *editorWidget, int basePosition) const final; void apply(TextEditor::TextDocumentManipulatorInterface &manipulator,
int basePosition) const final;
void setText(const QString &text); void setText(const QString &text);
QString text() const final; QString text() const final;

View File

@@ -87,7 +87,7 @@ public:
m_isOverloaded(false) {} m_isOverloaded(false) {}
bool prematurelyApplies(const QChar &c) const override; bool prematurelyApplies(const QChar &c) const override;
void applyContextualContent(TextEditorWidget *editorWidget, int basePosition) const override; void applyContextualContent(TextDocumentManipulatorInterface &manipulator, int basePosition) const override;
bool isOverloaded() const { return m_isOverloaded; } bool isOverloaded() const { return m_isOverloaded; }
void markAsOverloaded() { m_isOverloaded = true; } void markAsOverloaded() { m_isOverloaded = true; }
@@ -161,9 +161,9 @@ bool CppAssistProposalItem::prematurelyApplies(const QChar &typedChar) const
return false; return false;
} }
static bool isDereferenced(TextEditorWidget *editorWidget, int basePosition) static bool isDereferenced(TextDocumentManipulatorInterface &manipulator, int basePosition)
{ {
QTextCursor cursor = editorWidget->textCursor(); QTextCursor cursor = manipulator.textCursorAt(basePosition);
cursor.setPosition(basePosition); cursor.setPosition(basePosition);
BackwardsScanner scanner(cursor, LanguageFeatures()); BackwardsScanner scanner(cursor, LanguageFeatures());
@@ -191,7 +191,7 @@ quint64 CppAssistProposalItem::hash() const
return 0; return 0;
} }
void CppAssistProposalItem::applyContextualContent(TextEditorWidget *editorWidget, int basePosition) const void CppAssistProposalItem::applyContextualContent(TextDocumentManipulatorInterface &manipulator, int basePosition) const
{ {
Symbol *symbol = 0; Symbol *symbol = 0;
@@ -241,7 +241,7 @@ void CppAssistProposalItem::applyContextualContent(TextEditorWidget *editorWidge
if (function->argumentCount() == 0) if (function->argumentCount() == 0)
extraChars += QLatin1Char('<'); extraChars += QLatin1Char('<');
#endif #endif
} else if (!isDereferenced(editorWidget, basePosition) && !function->isAmbiguous()) { } else if (!isDereferenced(manipulator, basePosition) && !function->isAmbiguous()) {
// 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.
@@ -255,7 +255,7 @@ void CppAssistProposalItem::applyContextualContent(TextEditorWidget *editorWidge
// 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 = manipulator.characterAt(manipulator.currentPosition());
bool endWithSemicolon = m_typedChar == QLatin1Char(';') bool endWithSemicolon = m_typedChar == 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_typedChar.isNull() ? QLatin1Char(';') : m_typedChar;
@@ -273,7 +273,7 @@ void CppAssistProposalItem::applyContextualContent(TextEditorWidget *editorWidge
m_typedChar = QChar(); m_typedChar = QChar();
} }
} else if (autoParenthesesEnabled) { } else if (autoParenthesesEnabled) {
const QChar lookAhead = editorWidget->characterAt(editorWidget->position() + 1); const QChar lookAhead = manipulator.characterAt(manipulator.currentPosition() + 1);
if (MatchingText::shouldInsertMatchingText(lookAhead)) { if (MatchingText::shouldInsertMatchingText(lookAhead)) {
extraChars += QLatin1Char(')'); extraChars += QLatin1Char(')');
--cursorOffset; --cursorOffset;
@@ -311,11 +311,12 @@ void CppAssistProposalItem::applyContextualContent(TextEditorWidget *editorWidge
// Determine the length of characters that should just be kept on the editor, but do // Determine the length of characters that should just be kept on the editor, but do
// not consider content that ends as an identifier (which could be undesired). // not consider content that ends as an identifier (which could be undesired).
const int lineEnd = editorWidget->position(EndOfLinePosition); const int lineEnd = manipulator.positionAt(EndOfLinePosition);
const QString inEditor = editorWidget->textAt(editorWidget->position(), lineEnd - editorWidget->position()); const QString inEditor = manipulator.textAt(manipulator.currentPosition(),
lineEnd - manipulator.currentPosition());
int preserveLength = 0; int preserveLength = 0;
if (!inEditor.isEmpty()) { if (!inEditor.isEmpty()) {
preserveLength = toInsert.length() - (editorWidget->position() - basePosition); preserveLength = toInsert.length() - (manipulator.currentPosition() - basePosition);
const int inEditorLength = inEditor.length(); const int inEditorLength = inEditor.length();
while (preserveLength > 0) { while (preserveLength > 0) {
if (inEditor.startsWith(toInsert.right(preserveLength)) if (inEditor.startsWith(toInsert.right(preserveLength))
@@ -329,7 +330,7 @@ void CppAssistProposalItem::applyContextualContent(TextEditorWidget *editorWidge
for (int i = 0; i < extraChars.length(); ++i) { for (int i = 0; i < extraChars.length(); ++i) {
const QChar a = extraChars.at(i); const QChar a = extraChars.at(i);
const QChar b = editorWidget->characterAt(editorWidget->position() + i + preserveLength); const QChar b = manipulator.characterAt(manipulator.currentPosition() + i + preserveLength);
if (a == b) if (a == b)
++extraLength; ++extraLength;
else else
@@ -339,11 +340,10 @@ void CppAssistProposalItem::applyContextualContent(TextEditorWidget *editorWidge
toInsert += extraChars; toInsert += extraChars;
// Insert the remainder of the name // Insert the remainder of the name
const int length = editorWidget->position() - basePosition + preserveLength + extraLength; const int length = manipulator.currentPosition() - basePosition + preserveLength + extraLength;
editorWidget->setCursorPosition(basePosition); manipulator.replace(basePosition, length, toInsert);
editorWidget->replace(length, toInsert);
if (cursorOffset) if (cursorOffset)
editorWidget->setCursorPosition(editorWidget->position() + cursorOffset); manipulator.setCursorPosition(manipulator.currentPosition() + cursorOffset);
} }
// -------------------- // --------------------

View File

@@ -933,7 +933,7 @@ public:
return text() == m_provider->needle(); return text() == m_provider->needle();
} }
void applyContextualContent(TextEditorWidget *, int) const override void applyContextualContent(TextDocumentManipulatorInterface &, int) const override
{ {
QTC_ASSERT(m_provider->handler(), return); QTC_ASSERT(m_provider->handler(), return);
m_provider->handler()->handleReplay(text().mid(m_provider->needle().size())); m_provider->handler()->handleReplay(text().mid(m_provider->needle().size()));

View File

@@ -348,12 +348,11 @@ bool QmlJSAssistProposalItem::prematurelyApplies(const QChar &c) const
|| (text().endsWith(QLatin1Char('.')) && c == QLatin1Char('.')); || (text().endsWith(QLatin1Char('.')) && c == QLatin1Char('.'));
} }
void QmlJSAssistProposalItem::applyContextualContent(TextEditorWidget *editorWidget, void QmlJSAssistProposalItem::applyContextualContent(TextEditor::TextDocumentManipulatorInterface &manipulator,
int basePosition) const int basePosition) const
{ {
const int currentPosition = editorWidget->position(); const int currentPosition = manipulator.currentPosition();
editorWidget->setCursorPosition(basePosition); manipulator.replace(basePosition, currentPosition - basePosition, QString());
editorWidget->remove(currentPosition - basePosition);
QString content = text(); QString content = text();
int cursorOffset = 0; int cursorOffset = 0;
@@ -372,16 +371,16 @@ void QmlJSAssistProposalItem::applyContextualContent(TextEditorWidget *editorWid
int replacedLength = 0; int replacedLength = 0;
for (int i = 0; i < replaceable.length(); ++i) { for (int i = 0; i < replaceable.length(); ++i) {
const QChar a = replaceable.at(i); const QChar a = replaceable.at(i);
const QChar b = editorWidget->characterAt(editorWidget->position() + i); const QChar b = manipulator.characterAt(manipulator.currentPosition() + i);
if (a == b) if (a == b)
++replacedLength; ++replacedLength;
else else
break; break;
} }
const int length = editorWidget->position() - basePosition + replacedLength; const int length = manipulator.currentPosition() - basePosition + replacedLength;
editorWidget->replace(length, content); manipulator.replace(basePosition, length, content);
if (cursorOffset) if (cursorOffset)
editorWidget->setCursorPosition(editorWidget->position() + cursorOffset); manipulator.setCursorPosition(manipulator.currentPosition() + cursorOffset);
} }
// ------------------------- // -------------------------

View File

@@ -49,12 +49,12 @@ class QmlJSCompletionAssistInterface;
namespace Internal { namespace Internal {
class QmlJSAssistProposalItem : public TextEditor::AssistProposalItem class QmlJSAssistProposalItem final : public TextEditor::AssistProposalItem
{ {
public: public:
bool prematurelyApplies(const QChar &c) const override; bool prematurelyApplies(const QChar &c) const final;
void applyContextualContent(TextEditor::TextEditorWidget *editorWidget, void applyContextualContent(TextEditor::TextDocumentManipulatorInterface &manipulator,
int basePosition) const override; int basePosition) const final;
}; };

View File

@@ -58,7 +58,7 @@ public:
setText(text); setText(text);
} }
void apply(TextEditorWidget *editorWidget, int /*basePosition*/) const override void apply(TextDocumentManipulatorInterface &manipulator, int /*basePosition*/) const override
{ {
//Move to last in circular clipboard //Move to last in circular clipboard
@@ -72,7 +72,7 @@ public:
TextEditorWidget::duplicateMimeData(m_mimeData.data())); TextEditorWidget::duplicateMimeData(m_mimeData.data()));
//Paste //Paste
editorWidget->paste(); manipulator.paste();
} }
private: private:

View File

@@ -126,35 +126,33 @@ bool AssistProposalItem::prematurelyApplies(const QChar &c) const
return false; return false;
} }
void AssistProposalItem::apply(TextEditorWidget *editorWidget, int basePosition) const void AssistProposalItem::apply(TextDocumentManipulatorInterface &manipulator, int basePosition) const
{ {
if (data().canConvert<QString>()) { if (data().canConvert<QString>()) {
applySnippet(editorWidget, basePosition); applySnippet(manipulator, basePosition);
} else if (data().canConvert<QuickFixOperation::Ptr>()) { } else if (data().canConvert<QuickFixOperation::Ptr>()) {
applyQuickFix(editorWidget, basePosition); applyQuickFix(manipulator, basePosition);
} else { } else {
applyContextualContent(editorWidget, basePosition); applyContextualContent(manipulator, basePosition);
editorWidget->encourageApply(); manipulator.encourageApply();
} }
} }
void AssistProposalItem::applyContextualContent(TextEditorWidget *editorWidget, int basePosition) const void AssistProposalItem::applyContextualContent(TextDocumentManipulatorInterface &manipulator, int basePosition) const
{ {
const int currentPosition = editorWidget->position(); const int currentPosition = manipulator.currentPosition();
editorWidget->setCursorPosition(basePosition); manipulator.replace(basePosition, currentPosition - basePosition, text());
editorWidget->replace(currentPosition - basePosition, text());
} }
void AssistProposalItem::applySnippet(TextEditorWidget *editorWidget, int basePosition) const void AssistProposalItem::applySnippet(TextDocumentManipulatorInterface &manipulator, int basePosition) const
{ {
QTextCursor tc = editorWidget->textCursor(); manipulator.insertCodeSnippet(basePosition, data().toString());
tc.setPosition(basePosition, QTextCursor::KeepAnchor);
editorWidget->insertCodeSnippet(tc, data().toString());
} }
void AssistProposalItem::applyQuickFix(TextEditorWidget *editorWidget, int basePosition) const void AssistProposalItem::applyQuickFix(TextDocumentManipulatorInterface &manipulator, int basePosition) const
{ {
Q_UNUSED(editorWidget) Q_UNUSED(manipulator)
Q_UNUSED(basePosition) Q_UNUSED(basePosition)
QuickFixOperation::Ptr op = data().value<QuickFixOperation::Ptr>(); QuickFixOperation::Ptr op = data().value<QuickFixOperation::Ptr>();

View File

@@ -44,7 +44,7 @@ public:
QString text() const override; QString text() const override;
bool implicitlyApplies() const override; bool implicitlyApplies() const override;
bool prematurelyApplies(const QChar &c) const override; bool prematurelyApplies(const QChar &c) const override;
void apply(TextEditorWidget *editorWidget, int basePosition) const override; void apply(TextDocumentManipulatorInterface &manipulator, int basePosition) const override;
void setIcon(const QIcon &icon); void setIcon(const QIcon &icon);
QIcon icon() const final; QIcon icon() const final;
@@ -61,9 +61,9 @@ public:
bool isValid() const final; bool isValid() const final;
quint64 hash() const; quint64 hash() const;
virtual void applyContextualContent(TextEditorWidget *editorWidget, int basePosition) const; virtual void applyContextualContent(TextDocumentManipulatorInterface &manipulator, int basePosition) const;
virtual void applySnippet(TextEditorWidget *editorWidget, int basePosition) const; virtual void applySnippet(TextDocumentManipulatorInterface &manipulator, int basePosition) const;
virtual void applyQuickFix(TextEditorWidget *editorWidget, int basePosition) const; virtual void applyQuickFix(TextDocumentManipulatorInterface &manipulator, int basePosition) const;
private: private:
QIcon m_icon; QIcon m_icon;

View File

@@ -26,10 +26,9 @@
#ifndef TEXTEDITOR_ASSISTPROPOSALITEMINTERFACE_H #ifndef TEXTEDITOR_ASSISTPROPOSALITEMINTERFACE_H
#define TEXTEDITOR_ASSISTPROPOSALITEMINTERFACE_H #define TEXTEDITOR_ASSISTPROPOSALITEMINTERFACE_H
#include <texteditor/texteditor_global.h> #include "textdocumentmanipulatorinterface.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QChar;
class QIcon; class QIcon;
class QString; class QString;
class QVariant; class QVariant;
@@ -49,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 &typedCharacter) const = 0; virtual bool prematurelyApplies(const QChar &typedCharacter) const = 0;
virtual void apply(TextEditorWidget *editorWidget, int basePosition) const = 0; virtual void apply(TextDocumentManipulatorInterface &manipulator, int basePosition) const = 0;
virtual QIcon icon() const = 0; virtual QIcon icon() const = 0;
virtual QString detail() const = 0; virtual QString detail() const = 0;
virtual bool isSnippet() const = 0; virtual bool isSnippet() const = 0;

View File

@@ -34,6 +34,7 @@
#include "assistinterface.h" #include "assistinterface.h"
#include "assistproposalitem.h" #include "assistproposalitem.h"
#include "runner.h" #include "runner.h"
#include "textdocumentmanipulator.h"
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <texteditor/texteditorsettings.h> #include <texteditor/texteditorsettings.h>
@@ -357,7 +358,8 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR
void CodeAssistantPrivate::processProposalItem(AssistProposalItemInterface *proposalItem) void CodeAssistantPrivate::processProposalItem(AssistProposalItemInterface *proposalItem)
{ {
QTC_ASSERT(m_proposal, return); QTC_ASSERT(m_proposal, return);
proposalItem->apply(m_editorWidget, m_proposal->basePosition()); TextDocumentManipulator manipulator(m_editorWidget);
proposalItem->apply(manipulator, m_proposal->basePosition());
destroyContext(); destroyContext();
process(); process();
} }

View File

@@ -92,20 +92,22 @@ bool KeywordsAssistProposalItem::prematurelyApplies(const QChar &c) const
return c == QLatin1Char('(') && m_isFunction; return c == QLatin1Char('(') && m_isFunction;
} }
void KeywordsAssistProposalItem::applyContextualContent(TextEditorWidget *editorWidget, void KeywordsAssistProposalItem::applyContextualContent(TextDocumentManipulatorInterface &manipulator,
int basePosition) const int basePosition) const
{ {
const CompletionSettings &settings = TextEditorSettings::completionSettings(); const CompletionSettings &settings = TextEditorSettings::completionSettings();
int replaceLength = editorWidget->position() - basePosition; int replaceLength = manipulator.currentPosition() - basePosition;
QString toInsert = text(); QString toInsert = text();
int cursorOffset = 0; int cursorOffset = 0;
const QChar characterAtCurrentPosition = manipulator.characterAt(manipulator.currentPosition());
if (m_isFunction && settings.m_autoInsertBrackets) { if (m_isFunction && settings.m_autoInsertBrackets) {
if (settings.m_spaceAfterFunctionName) { if (settings.m_spaceAfterFunctionName) {
if (editorWidget->textAt(editorWidget->position(), 2) == QLatin1String(" (")) { if (manipulator.textAt(manipulator.currentPosition(), 2) == QLatin1String(" (")) {
cursorOffset = 2; cursorOffset = 2;
} else if (editorWidget->characterAt(editorWidget->position()) == QLatin1Char('(') } else if ( characterAtCurrentPosition == QLatin1Char('(')
|| editorWidget->characterAt(editorWidget->position()) == QLatin1Char(' ')) { || characterAtCurrentPosition == QLatin1Char(' ')) {
replaceLength += 1; replaceLength += 1;
toInsert += QLatin1String(" ("); toInsert += QLatin1String(" (");
} else { } else {
@@ -113,7 +115,7 @@ void KeywordsAssistProposalItem::applyContextualContent(TextEditorWidget *editor
cursorOffset = -1; cursorOffset = -1;
} }
} else { } else {
if (editorWidget->characterAt(editorWidget->position()) == QLatin1Char('(')) { if (characterAtCurrentPosition == QLatin1Char('(')) {
cursorOffset = 1; cursorOffset = 1;
} else { } else {
toInsert += QLatin1String("()"); toInsert += QLatin1String("()");
@@ -122,10 +124,9 @@ void KeywordsAssistProposalItem::applyContextualContent(TextEditorWidget *editor
} }
} }
editorWidget->setCursorPosition(basePosition); manipulator.replace(basePosition, replaceLength, toInsert);
editorWidget->replace(replaceLength, toInsert);
if (cursorOffset) if (cursorOffset)
editorWidget->setCursorPosition(editorWidget->position() + cursorOffset); manipulator.setCursorPosition(manipulator.currentPosition() + cursorOffset);
} }
// ------------------------- // -------------------------

View File

@@ -58,7 +58,7 @@ public:
~KeywordsAssistProposalItem(); ~KeywordsAssistProposalItem();
bool prematurelyApplies(const QChar &c) const override; bool prematurelyApplies(const QChar &c) const override;
void applyContextualContent(TextEditorWidget *editorWidget, int basePosition) const override; void applyContextualContent(TextDocumentManipulatorInterface &manipulator, int basePosition) const override;
private: private:
bool m_isFunction; bool m_isFunction;
}; };

View File

@@ -0,0 +1,143 @@
/****************************************************************************
**
** 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 "textdocumentmanipulator.h"
#include <texteditor/texteditor.h>
#include <texteditor/textdocument.h>
namespace TextEditor {
TextDocumentManipulator::TextDocumentManipulator(TextEditorWidget *textEditorWidget)
: m_textEditorWidget(textEditorWidget)
{
}
int TextDocumentManipulator::currentPosition() const
{
return m_textEditorWidget->position();
}
int TextDocumentManipulator::positionAt(TextPositionOperation textPositionOperation) const
{
return m_textEditorWidget->position(textPositionOperation);
}
QChar TextDocumentManipulator::characterAt(int position) const
{
return m_textEditorWidget->characterAt(position);
}
QString TextDocumentManipulator::textAt(int position, int length) const
{
return m_textEditorWidget->textAt(position, length);
}
QTextCursor TextDocumentManipulator::textCursorAt(int position) const
{
auto cursor = m_textEditorWidget->textCursor();
cursor.setPosition(position);
return cursor;
}
void TextDocumentManipulator::setCursorPosition(int position)
{
m_textEditorWidget->setCursorPosition(position);
}
bool TextDocumentManipulator::replace(int position, int length, const QString &text)
{
bool textWillBeReplaced = textIsDifferentAt(position, length, text);
if (textWillBeReplaced)
replaceWithoutCheck(position, length, text);
return textWillBeReplaced;
}
void TextDocumentManipulator::insertCodeSnippet(int position, const QString &text)
{
auto cursor = m_textEditorWidget->textCursor();
cursor.setPosition(position);
m_textEditorWidget->insertCodeSnippet(cursor, text);
}
void TextDocumentManipulator::paste()
{
m_textEditorWidget->paste();
}
void TextDocumentManipulator::encourageApply()
{
m_textEditorWidget->encourageApply();
}
namespace {
bool hasOnlyBlanksBeforeCursorInLine(QTextCursor textCursor)
{
textCursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
const auto textBeforeCursor = textCursor.selectedText();
const auto nonSpace = std::find_if(textBeforeCursor.cbegin(),
textBeforeCursor.cend(),
[] (const QChar &signBeforeCursor) {
return !signBeforeCursor.isSpace();
});
return nonSpace == textBeforeCursor.cend();
}
}
void TextDocumentManipulator::autoIndent(int position, int length)
{
auto cursor = m_textEditorWidget->textCursor();
cursor.setPosition(position);
if (hasOnlyBlanksBeforeCursorInLine(cursor)) {
cursor.setPosition(position + length, QTextCursor::KeepAnchor);
m_textEditorWidget->textDocument()->autoIndent(cursor);
}
}
bool TextDocumentManipulator::textIsDifferentAt(int position, int length, const QString &text) const
{
const auto textToBeReplaced = m_textEditorWidget->textAt(position, length);
return text != textToBeReplaced;
}
void TextDocumentManipulator::replaceWithoutCheck(int position, int length, const QString &text)
{
auto cursor = m_textEditorWidget->textCursor();
cursor.setPosition(position);
cursor.setPosition(position + length, QTextCursor::KeepAnchor);
cursor.insertText(text);
}
} // namespace TextEditor

View File

@@ -0,0 +1,63 @@
/****************************************************************************
**
** 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 TEXTDOCUMENTMANIPULATOR_H
#define TEXTDOCUMENTMANIPULATOR_H
#include "textdocumentmanipulatorinterface.h"
namespace TextEditor {
class TextEditorWidget;
class TextDocumentManipulator final : public TextDocumentManipulatorInterface
{
public:
TextDocumentManipulator(TextEditorWidget *textEditorWidget);
int currentPosition() const final;
int positionAt(TextPositionOperation textPositionOperation) const final;
QChar characterAt(int position) const final;
QString textAt(int position, int length) const final;
QTextCursor textCursorAt(int position) const final;
void setCursorPosition(int position) final;
bool replace(int position, int length, const QString &text) final;
void insertCodeSnippet(int position, const QString &text) final;
void paste() final;
void encourageApply() final;
void autoIndent(int position, int length);
private:
bool textIsDifferentAt(int position, int length, const QString &text) const;
void replaceWithoutCheck(int position, int length, const QString &text);
private:
TextEditorWidget *m_textEditorWidget;
};
} // namespace TextEditor
#endif // TEXTDOCUMENTMANIPULATOR_H

View File

@@ -0,0 +1,60 @@
/****************************************************************************
**
** 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 TEXTDOCUMENTMANIPULATORINTERFACE_H
#define TEXTDOCUMENTMANIPULATORINTERFACE_H
#include <texteditor/texteditor_global.h>
QT_BEGIN_NAMESPACE
class QChar;
class QString;
class QTextCursor;
QT_END_NAMESPACE
namespace TextEditor {
class TEXTEDITOR_EXPORT TextDocumentManipulatorInterface
{
public:
virtual ~TextDocumentManipulatorInterface() = default;
virtual int currentPosition() const = 0;
virtual int positionAt(TextPositionOperation textPositionOperation) const = 0;
virtual QChar characterAt(int position) const = 0;
virtual QString textAt(int position, int length) const = 0;
virtual QTextCursor textCursorAt(int position) const = 0;
virtual void setCursorPosition(int position) = 0;
virtual bool replace(int position, int length, const QString &text) = 0;
virtual void insertCodeSnippet(int position, const QString &text) = 0;
virtual void paste() = 0;
virtual void encourageApply() = 0;
virtual void autoIndent(int position, int length) = 0;
};
} // namespace TextEditor
#endif // TEXTDOCUMENTMANIPULATORINTERFACE_H

View File

@@ -87,15 +87,6 @@ class MarginSettings;
class StorageSettings; class StorageSettings;
class TypingSettings; class TypingSettings;
enum TextPositionOperation
{
CurrentPosition = 1,
EndOfLinePosition = 2,
StartOfLinePosition = 3,
AnchorPosition = 4,
EndOfDocPosition = 5
};
enum TextMarkRequestKind enum TextMarkRequestKind
{ {
BreakpointRequest, BreakpointRequest,

View File

@@ -87,6 +87,7 @@ SOURCES += texteditorplugin.cpp \
codeassist/genericproposal.cpp \ codeassist/genericproposal.cpp \
codeassist/genericproposalwidget.cpp \ codeassist/genericproposalwidget.cpp \
codeassist/iassistproposalmodel.cpp \ codeassist/iassistproposalmodel.cpp \
codeassist/textdocumentmanipulator.cpp \
tabsettingswidget.cpp \ tabsettingswidget.cpp \
simplecodestylepreferences.cpp \ simplecodestylepreferences.cpp \
simplecodestylepreferenceswidget.cpp \ simplecodestylepreferenceswidget.cpp \
@@ -197,6 +198,8 @@ HEADERS += texteditorplugin.h \
codeassist/genericproposal.h \ codeassist/genericproposal.h \
codeassist/genericproposalwidget.h \ codeassist/genericproposalwidget.h \
codeassist/iassistproposalmodel.h \ codeassist/iassistproposalmodel.h \
codeassist/textdocumentmanipulator.h \
codeassist/textdocumentmanipulatorinterface.h \
tabsettingswidget.h \ tabsettingswidget.h \
simplecodestylepreferences.h \ simplecodestylepreferences.h \
simplecodestylepreferenceswidget.h \ simplecodestylepreferenceswidget.h \

View File

@@ -115,6 +115,9 @@ QtcPlugin {
"textdocument.h", "textdocument.h",
"textdocumentlayout.cpp", "textdocumentlayout.cpp",
"textdocumentlayout.h", "textdocumentlayout.h",
"textdocumentmanipulator.cpp",
"textdocumentmanipulator.h",
"textdocumentmanipulatorinterface.h",
"texteditor.cpp", "texteditor.cpp",
"texteditor.h", "texteditor.h",
"texteditor.qrc", "texteditor.qrc",

View File

@@ -34,4 +34,17 @@
# define TEXTEDITOR_EXPORT Q_DECL_IMPORT # define TEXTEDITOR_EXPORT Q_DECL_IMPORT
#endif #endif
namespace TextEditor {
enum TextPositionOperation
{
CurrentPosition = 1,
EndOfLinePosition = 2,
StartOfLinePosition = 3,
AnchorPosition = 4,
EndOfDocPosition = 5
};
} // namespace TextEditor
#endif // TEXTEDITOR_GLOBAL_H #endif // TEXTEDITOR_GLOBAL_H