forked from qt-creator/qt-creator
TextEditor: filter suggestions while typing
This will remove suggestions that are no longer applyable with the current editor content. Change-Id: Ibfc47495d5dda99a9ef2d7fe3f69c83a2d7e3997 Reviewed-by: <lie@spyro-soft.com> Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
@@ -45,7 +45,6 @@ static void cycleSuggestion(TextEditor::TextEditorWidget *editor, Direction dire
|
|||||||
index = suggestion->suggestions().count() - 1;
|
index = suggestion->suggestions().count() - 1;
|
||||||
else if (index >= suggestion->suggestions().count())
|
else if (index >= suggestion->suggestions().count())
|
||||||
index = 0;
|
index = 0;
|
||||||
suggestion->reset();
|
|
||||||
editor->insertSuggestion(std::make_unique<TextEditor::CyclicSuggestion>(
|
editor->insertSuggestion(std::make_unique<TextEditor::CyclicSuggestion>(
|
||||||
suggestion->suggestions(), editor->document(), index));
|
suggestion->suggestions(), editor->document(), index));
|
||||||
}
|
}
|
||||||
|
@@ -608,26 +608,6 @@ void TextDocumentLayout::updateSuggestionFormats(const QTextBlock &block,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextDocumentLayout::updateSuggestion(const QTextBlock &block,
|
|
||||||
int position,
|
|
||||||
const FontSettings &fontSettings)
|
|
||||||
{
|
|
||||||
if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(block)) {
|
|
||||||
if (position < suggestion->currentPosition())
|
|
||||||
return false;
|
|
||||||
const int positionInBlock = position - block.position();
|
|
||||||
const QString start = block.text().left(positionInBlock);
|
|
||||||
const QString end = block.text().mid(positionInBlock);
|
|
||||||
const QString replacement = suggestion->replacementDocument()->firstBlock().text();
|
|
||||||
if (replacement.startsWith(start) && replacement.indexOf(end, start.size()) >= 0) {
|
|
||||||
suggestion->setCurrentPosition(position);
|
|
||||||
TextDocumentLayout::updateSuggestionFormats(block, fontSettings);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextDocumentLayout::requestExtraAreaUpdate()
|
void TextDocumentLayout::requestExtraAreaUpdate()
|
||||||
{
|
{
|
||||||
emit updateExtraArea();
|
emit updateExtraArea();
|
||||||
|
@@ -188,9 +188,6 @@ public:
|
|||||||
static quint8 attributeState(const QTextBlock &block);
|
static quint8 attributeState(const QTextBlock &block);
|
||||||
static void updateSuggestionFormats(const QTextBlock &block,
|
static void updateSuggestionFormats(const QTextBlock &block,
|
||||||
const FontSettings &fontSettings);
|
const FontSettings &fontSettings);
|
||||||
static bool updateSuggestion(const QTextBlock &block,
|
|
||||||
int position,
|
|
||||||
const FontSettings &fontSettings);
|
|
||||||
|
|
||||||
class TEXTEDITOR_EXPORT FoldValidator
|
class TEXTEDITOR_EXPORT FoldValidator
|
||||||
{
|
{
|
||||||
|
@@ -1836,15 +1836,22 @@ void TextEditorWidgetPrivate::updateSuggestion()
|
|||||||
{
|
{
|
||||||
if (!m_suggestionBlock.isValid())
|
if (!m_suggestionBlock.isValid())
|
||||||
return;
|
return;
|
||||||
if (m_cursors.mainCursor().block() != m_suggestionBlock) {
|
const QTextCursor cursor = m_cursors.mainCursor();
|
||||||
clearCurrentSuggestion();
|
if (cursor.block() == m_suggestionBlock) {
|
||||||
} else {
|
TextSuggestion *suggestion = TextDocumentLayout::suggestion(m_suggestionBlock);
|
||||||
if (!TextDocumentLayout::updateSuggestion(m_suggestionBlock,
|
if (QTC_GUARD(suggestion)) {
|
||||||
m_cursors.mainCursor().position(),
|
const int pos = cursor.position();
|
||||||
m_document->fontSettings())) {
|
if (pos >= suggestion->currentPosition()) {
|
||||||
clearCurrentSuggestion();
|
suggestion->setCurrentPosition(pos);
|
||||||
|
if (suggestion->filterSuggestions(q)) {
|
||||||
|
TextDocumentLayout::updateSuggestionFormats(
|
||||||
|
m_suggestionBlock, m_document->fontSettings());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
clearCurrentSuggestion();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextEditorWidgetPrivate::clearCurrentSuggestion()
|
void TextEditorWidgetPrivate::clearCurrentSuggestion()
|
||||||
|
@@ -47,11 +47,11 @@ bool TextSuggestion::applyLine(TextEditorWidget *widget)
|
|||||||
return applyPart(Line, widget);
|
return applyPart(Line, widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextSuggestion::reset()
|
bool TextSuggestion::filterSuggestions(TextEditorWidget *widget)
|
||||||
{
|
{
|
||||||
QTextCursor c = m_suggestion.position.toTextCursor(sourceDocument());
|
QTextCursor c = m_suggestion.range.begin.toTextCursor(sourceDocument());
|
||||||
c.setPosition(currentPosition(), QTextCursor::KeepAnchor);
|
c.setPosition(currentPosition(), QTextCursor::KeepAnchor);
|
||||||
c.removeSelectedText();
|
return m_suggestion.text.startsWith(c.selectedText(), Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextSuggestion::applyPart(Part part, TextEditorWidget *widget)
|
bool TextSuggestion::applyPart(Part part, TextEditorWidget *widget)
|
||||||
@@ -98,6 +98,38 @@ CyclicSuggestion::CyclicSuggestion(
|
|||||||
, m_currentSuggestion(currentSuggestion)
|
, m_currentSuggestion(currentSuggestion)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
bool operator==(const TextSuggestion::Data &lhs, const TextSuggestion::Data &rhs)
|
||||||
|
{
|
||||||
|
return lhs.text == rhs.text && lhs.range == rhs.range && lhs.position == rhs.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CyclicSuggestion::filterSuggestions(TextEditorWidget *widget)
|
||||||
|
{
|
||||||
|
QList<Data> newSuggestions;
|
||||||
|
int newIndex = -1;
|
||||||
|
int currentIndex = 0;
|
||||||
|
for (auto suggestion : m_suggestions) {
|
||||||
|
QTextCursor c = suggestion.range.begin.toTextCursor(sourceDocument());
|
||||||
|
c.setPosition(currentPosition(), QTextCursor::KeepAnchor);
|
||||||
|
if (suggestion.text.startsWith(c.selectedText())) {
|
||||||
|
newSuggestions.append(suggestion);
|
||||||
|
if (currentIndex == m_currentSuggestion)
|
||||||
|
newIndex = newSuggestions.size() - 1;
|
||||||
|
} else if (currentIndex == m_currentSuggestion) {
|
||||||
|
newIndex = 0;
|
||||||
|
}
|
||||||
|
++currentIndex;
|
||||||
|
}
|
||||||
|
if (newSuggestions.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (newSuggestions != m_suggestions) {
|
||||||
|
widget->insertSuggestion(
|
||||||
|
std::make_unique<CyclicSuggestion>(newSuggestions, sourceDocument(), newIndex));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
class SuggestionToolTip : public QToolBar
|
class SuggestionToolTip : public QToolBar
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -108,33 +140,41 @@ public:
|
|||||||
, m_currentSuggestion(std::max(0, std::min<int>(currentSuggestion, suggestions.size() - 1)))
|
, m_currentSuggestion(std::max(0, std::min<int>(currentSuggestion, suggestions.size() - 1)))
|
||||||
, m_editor(editor)
|
, m_editor(editor)
|
||||||
{
|
{
|
||||||
auto prev = addAction(
|
m_prev = addAction(Utils::Icons::PREV_TOOLBAR.icon(), Tr::tr("Select Previous Suggestion"));
|
||||||
Utils::Icons::PREV_TOOLBAR.icon(), Tr::tr("Select Previous Suggestion"));
|
|
||||||
prev->setEnabled(m_suggestions.size() > 1);
|
|
||||||
addWidget(m_numberLabel);
|
addWidget(m_numberLabel);
|
||||||
auto next
|
m_next = addAction(Utils::Icons::NEXT_TOOLBAR.icon(), Tr::tr("Select Next Suggestion"));
|
||||||
= addAction(Utils::Icons::NEXT_TOOLBAR.icon(), Tr::tr("Select Next Suggestion"));
|
|
||||||
next->setEnabled(m_suggestions.size() > 1);
|
|
||||||
|
|
||||||
auto apply = addAction(Tr::tr("Apply (%1)").arg(QKeySequence(Qt::Key_Tab).toString()));
|
auto apply = addAction(Tr::tr("Apply (%1)").arg(QKeySequence(Qt::Key_Tab).toString()));
|
||||||
auto applyWord = addAction(
|
auto applyWord = addAction(
|
||||||
Tr::tr("Apply Word (%1)").arg(QKeySequence(QKeySequence::MoveToNextWord).toString()));
|
Tr::tr("Apply Word (%1)").arg(QKeySequence(QKeySequence::MoveToNextWord).toString()));
|
||||||
auto applyLine = addAction(Tr::tr("Apply Line"));
|
auto applyLine = addAction(Tr::tr("Apply Line"));
|
||||||
|
|
||||||
connect(prev, &QAction::triggered, this, &SuggestionToolTip::selectPrevious);
|
connect(m_prev, &QAction::triggered, this, &SuggestionToolTip::selectPrevious);
|
||||||
connect(next, &QAction::triggered, this, &SuggestionToolTip::selectNext);
|
connect(m_next, &QAction::triggered, this, &SuggestionToolTip::selectNext);
|
||||||
connect(apply, &QAction::triggered, this, &SuggestionToolTip::apply);
|
connect(apply, &QAction::triggered, this, &SuggestionToolTip::apply);
|
||||||
connect(applyWord, &QAction::triggered, this, &SuggestionToolTip::applyWord);
|
connect(applyWord, &QAction::triggered, this, &SuggestionToolTip::applyWord);
|
||||||
connect(applyLine, &QAction::triggered, this, &SuggestionToolTip::applyLine);
|
connect(applyLine, &QAction::triggered, this, &SuggestionToolTip::applyLine);
|
||||||
|
connect(editor->document(), &QTextDocument::contentsChange, this, &SuggestionToolTip::contentsChanged);
|
||||||
|
|
||||||
updateLabels();
|
updateSuggestionSelector();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateLabels()
|
void contentsChanged()
|
||||||
|
{
|
||||||
|
if (auto *suggestion = dynamic_cast<CyclicSuggestion *>(m_editor->currentSuggestion())) {
|
||||||
|
m_suggestions = suggestion->suggestions();
|
||||||
|
m_currentSuggestion = suggestion->currentSuggestion();
|
||||||
|
updateSuggestionSelector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSuggestionSelector()
|
||||||
{
|
{
|
||||||
m_numberLabel->setText(
|
m_numberLabel->setText(
|
||||||
Tr::tr("%1 of %2").arg(m_currentSuggestion + 1).arg(m_suggestions.count()));
|
Tr::tr("%1 of %2").arg(m_currentSuggestion + 1).arg(m_suggestions.count()));
|
||||||
|
m_prev->setEnabled(m_suggestions.size() > 1);
|
||||||
|
m_next->setEnabled(m_suggestions.size() > 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectPrevious()
|
void selectPrevious()
|
||||||
@@ -155,9 +195,7 @@ private:
|
|||||||
|
|
||||||
void setCurrentSuggestion()
|
void setCurrentSuggestion()
|
||||||
{
|
{
|
||||||
updateLabels();
|
updateSuggestionSelector();
|
||||||
if (TextSuggestion *suggestion = m_editor->currentSuggestion())
|
|
||||||
suggestion->reset();
|
|
||||||
m_editor->insertSuggestion(std::make_unique<CyclicSuggestion>(
|
m_editor->insertSuggestion(std::make_unique<CyclicSuggestion>(
|
||||||
m_suggestions, m_editor->document(), m_currentSuggestion));
|
m_suggestions, m_editor->document(), m_currentSuggestion));
|
||||||
}
|
}
|
||||||
@@ -190,6 +228,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
QLabel *m_numberLabel;
|
QLabel *m_numberLabel;
|
||||||
|
QAction *m_prev;
|
||||||
|
QAction *m_next;
|
||||||
QList<TextSuggestion::Data> m_suggestions;
|
QList<TextSuggestion::Data> m_suggestions;
|
||||||
int m_currentSuggestion = 0;
|
int m_currentSuggestion = 0;
|
||||||
TextEditorWidget *m_editor;
|
TextEditorWidget *m_editor;
|
||||||
|
@@ -35,7 +35,7 @@ public:
|
|||||||
// Returns true if the suggestion was applied completely, false if it was only partially applied.
|
// Returns true if the suggestion was applied completely, false if it was only partially applied.
|
||||||
virtual bool applyWord(TextEditorWidget *widget);
|
virtual bool applyWord(TextEditorWidget *widget);
|
||||||
virtual bool applyLine(TextEditorWidget *widget);
|
virtual bool applyLine(TextEditorWidget *widget);
|
||||||
virtual void reset();
|
virtual bool filterSuggestions(TextEditorWidget *widget);
|
||||||
|
|
||||||
int currentPosition() const { return m_currentPosition; }
|
int currentPosition() const { return m_currentPosition; }
|
||||||
void setCurrentPosition(int position) { m_currentPosition = position; }
|
void setCurrentPosition(int position) { m_currentPosition = position; }
|
||||||
@@ -63,6 +63,8 @@ public:
|
|||||||
int currentSuggestion() const { return m_currentSuggestion; }
|
int currentSuggestion() const { return m_currentSuggestion; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool filterSuggestions(TextEditorWidget *widget) override;
|
||||||
|
|
||||||
QList<Data> m_suggestions;
|
QList<Data> m_suggestions;
|
||||||
int m_currentSuggestion = 0;
|
int m_currentSuggestion = 0;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user