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;
|
||||
else if (index >= suggestion->suggestions().count())
|
||||
index = 0;
|
||||
suggestion->reset();
|
||||
editor->insertSuggestion(std::make_unique<TextEditor::CyclicSuggestion>(
|
||||
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()
|
||||
{
|
||||
emit updateExtraArea();
|
||||
|
@@ -188,9 +188,6 @@ public:
|
||||
static quint8 attributeState(const QTextBlock &block);
|
||||
static void updateSuggestionFormats(const QTextBlock &block,
|
||||
const FontSettings &fontSettings);
|
||||
static bool updateSuggestion(const QTextBlock &block,
|
||||
int position,
|
||||
const FontSettings &fontSettings);
|
||||
|
||||
class TEXTEDITOR_EXPORT FoldValidator
|
||||
{
|
||||
|
@@ -1836,16 +1836,23 @@ void TextEditorWidgetPrivate::updateSuggestion()
|
||||
{
|
||||
if (!m_suggestionBlock.isValid())
|
||||
return;
|
||||
if (m_cursors.mainCursor().block() != m_suggestionBlock) {
|
||||
clearCurrentSuggestion();
|
||||
} else {
|
||||
if (!TextDocumentLayout::updateSuggestion(m_suggestionBlock,
|
||||
m_cursors.mainCursor().position(),
|
||||
m_document->fontSettings())) {
|
||||
clearCurrentSuggestion();
|
||||
const QTextCursor cursor = m_cursors.mainCursor();
|
||||
if (cursor.block() == m_suggestionBlock) {
|
||||
TextSuggestion *suggestion = TextDocumentLayout::suggestion(m_suggestionBlock);
|
||||
if (QTC_GUARD(suggestion)) {
|
||||
const int pos = cursor.position();
|
||||
if (pos >= suggestion->currentPosition()) {
|
||||
suggestion->setCurrentPosition(pos);
|
||||
if (suggestion->filterSuggestions(q)) {
|
||||
TextDocumentLayout::updateSuggestionFormats(
|
||||
m_suggestionBlock, m_document->fontSettings());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
clearCurrentSuggestion();
|
||||
}
|
||||
|
||||
void TextEditorWidgetPrivate::clearCurrentSuggestion()
|
||||
{
|
||||
|
@@ -47,11 +47,11 @@ bool TextSuggestion::applyLine(TextEditorWidget *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.removeSelectedText();
|
||||
return m_suggestion.text.startsWith(c.selectedText(), Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
bool TextSuggestion::applyPart(Part part, TextEditorWidget *widget)
|
||||
@@ -98,6 +98,38 @@ CyclicSuggestion::CyclicSuggestion(
|
||||
, 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
|
||||
{
|
||||
public:
|
||||
@@ -108,33 +140,41 @@ public:
|
||||
, m_currentSuggestion(std::max(0, std::min<int>(currentSuggestion, suggestions.size() - 1)))
|
||||
, m_editor(editor)
|
||||
{
|
||||
auto prev = addAction(
|
||||
Utils::Icons::PREV_TOOLBAR.icon(), Tr::tr("Select Previous Suggestion"));
|
||||
prev->setEnabled(m_suggestions.size() > 1);
|
||||
m_prev = addAction(Utils::Icons::PREV_TOOLBAR.icon(), Tr::tr("Select Previous Suggestion"));
|
||||
addWidget(m_numberLabel);
|
||||
auto next
|
||||
= addAction(Utils::Icons::NEXT_TOOLBAR.icon(), Tr::tr("Select Next Suggestion"));
|
||||
next->setEnabled(m_suggestions.size() > 1);
|
||||
m_next = addAction(Utils::Icons::NEXT_TOOLBAR.icon(), Tr::tr("Select Next Suggestion"));
|
||||
|
||||
auto apply = addAction(Tr::tr("Apply (%1)").arg(QKeySequence(Qt::Key_Tab).toString()));
|
||||
auto applyWord = addAction(
|
||||
Tr::tr("Apply Word (%1)").arg(QKeySequence(QKeySequence::MoveToNextWord).toString()));
|
||||
auto applyLine = addAction(Tr::tr("Apply Line"));
|
||||
|
||||
connect(prev, &QAction::triggered, this, &SuggestionToolTip::selectPrevious);
|
||||
connect(next, &QAction::triggered, this, &SuggestionToolTip::selectNext);
|
||||
connect(m_prev, &QAction::triggered, this, &SuggestionToolTip::selectPrevious);
|
||||
connect(m_next, &QAction::triggered, this, &SuggestionToolTip::selectNext);
|
||||
connect(apply, &QAction::triggered, this, &SuggestionToolTip::apply);
|
||||
connect(applyWord, &QAction::triggered, this, &SuggestionToolTip::applyWord);
|
||||
connect(applyLine, &QAction::triggered, this, &SuggestionToolTip::applyLine);
|
||||
connect(editor->document(), &QTextDocument::contentsChange, this, &SuggestionToolTip::contentsChanged);
|
||||
|
||||
updateLabels();
|
||||
updateSuggestionSelector();
|
||||
}
|
||||
|
||||
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(
|
||||
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()
|
||||
@@ -155,9 +195,7 @@ private:
|
||||
|
||||
void setCurrentSuggestion()
|
||||
{
|
||||
updateLabels();
|
||||
if (TextSuggestion *suggestion = m_editor->currentSuggestion())
|
||||
suggestion->reset();
|
||||
updateSuggestionSelector();
|
||||
m_editor->insertSuggestion(std::make_unique<CyclicSuggestion>(
|
||||
m_suggestions, m_editor->document(), m_currentSuggestion));
|
||||
}
|
||||
@@ -190,6 +228,8 @@ private:
|
||||
}
|
||||
|
||||
QLabel *m_numberLabel;
|
||||
QAction *m_prev;
|
||||
QAction *m_next;
|
||||
QList<TextSuggestion::Data> m_suggestions;
|
||||
int m_currentSuggestion = 0;
|
||||
TextEditorWidget *m_editor;
|
||||
|
@@ -35,7 +35,7 @@ public:
|
||||
// Returns true if the suggestion was applied completely, false if it was only partially applied.
|
||||
virtual bool applyWord(TextEditorWidget *widget);
|
||||
virtual bool applyLine(TextEditorWidget *widget);
|
||||
virtual void reset();
|
||||
virtual bool filterSuggestions(TextEditorWidget *widget);
|
||||
|
||||
int currentPosition() const { return m_currentPosition; }
|
||||
void setCurrentPosition(int position) { m_currentPosition = position; }
|
||||
@@ -63,6 +63,8 @@ public:
|
||||
int currentSuggestion() const { return m_currentSuggestion; }
|
||||
|
||||
private:
|
||||
bool filterSuggestions(TextEditorWidget *widget) override;
|
||||
|
||||
QList<Data> m_suggestions;
|
||||
int m_currentSuggestion = 0;
|
||||
};
|
||||
|
Reference in New Issue
Block a user