Skip to content

Commit

Permalink
♻️ refactor: Multiline text suggestion
Browse files Browse the repository at this point in the history
  • Loading branch information
Palm1r committed Dec 16, 2024
1 parent ac80805 commit 09cde8f
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 91 deletions.
113 changes: 54 additions & 59 deletions LLMSuggestion.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
/*
/*
* Copyright (C) 2023 The Qt Company Ltd.
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* The Qt Company portions:
* SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
*
* Petr Mironychev portions:
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
Expand All @@ -18,27 +23,25 @@
*/

#include "LLMSuggestion.hpp"

#include <QTextCursor>
#include <QtWidgets/qtoolbar.h>
#include <texteditor/texteditor.h>
#include <utils/stringutils.h>
#include <utils/tooltip/tooltip.h>

namespace QodeAssist {

LLMSuggestion::LLMSuggestion(const TextEditor::TextSuggestion::Data &data, QTextDocument *origin)
: TextEditor::TextSuggestion(data, origin)
, m_linesCount(0)
, m_suggestion(data)
LLMSuggestion::LLMSuggestion(
const QList<Data> &suggestions, QTextDocument *sourceDocument, int currentCompletion)
: TextEditor::CyclicSuggestion(suggestions, sourceDocument, currentCompletion)
{
int startPos = data.range.begin.toPositionInDocument(origin);
int endPos = data.range.end.toPositionInDocument(origin);
const auto &data = suggestions[currentCompletion];

int startPos = data.range.begin.toPositionInDocument(sourceDocument);
int endPos = data.range.end.toPositionInDocument(sourceDocument);

startPos = qBound(0, startPos, origin->characterCount() - 1);
endPos = qBound(startPos, endPos, origin->characterCount() - 1);
startPos = qBound(0, startPos, sourceDocument->characterCount() - 1);
endPos = qBound(startPos, endPos, sourceDocument->characterCount() - 1);

QTextCursor cursor(origin);
QTextCursor cursor(sourceDocument);
cursor.setPosition(startPos);
cursor.setPosition(endPos, QTextCursor::KeepAnchor);

Expand All @@ -49,64 +52,56 @@ LLMSuggestion::LLMSuggestion(const TextEditor::TextSuggestion::Data &data, QText
int endPosInBlock = endPos - block.position();

blockText.replace(startPosInBlock, endPosInBlock - startPosInBlock, data.text);

replacementDocument()->setPlainText(blockText);
}

bool LLMSuggestion::apply()
{
return TextEditor::TextSuggestion::apply();
}

bool LLMSuggestion::applyWord(TextEditor::TextEditorWidget *widget)
{
return TextEditor::TextSuggestion::applyWord(widget);
return applyPart(Word, widget);
}

bool LLMSuggestion::applyLine(TextEditor::TextEditorWidget *widget)
{
return TextEditor::TextSuggestion::applyLine(widget);
return applyPart(Line, widget);
}

void LLMSuggestion::reset()
{
reset();
m_linesCount = 0;
}

void LLMSuggestion::onCounterFinished(int count)
{
Utils::ToolTip::hide();
m_linesCount = 0;
QTextCursor cursor = m_completion.range().toSelection(m_start.document());
cursor.beginEditBlock();
cursor.removeSelectedText();

QStringList lines = m_completion.text().split('\n');
QString textToInsert = lines.mid(0, count).join('\n');

cursor.insertText(textToInsert);
cursor.endEditBlock();
}

// void LLMSuggestion::reset()
// {
// m_start.removeSelectedText();
// }

// int LLMSuggestion::position()
// {
// return m_start.position();
// }

void LLMSuggestion::showTooltip(TextEditor::TextEditorWidget *widget, int count)
bool LLMSuggestion::applyPart(Part part, TextEditor::TextEditorWidget *widget)
{
// Utils::ToolTip::hide();
// QPoint pos = widget->mapToGlobal(widget->cursorRect().topRight());
// pos += QPoint(-10, -50);
// m_counterTooltip = new CounterTooltip(count);
// Utils::ToolTip::show(pos, m_counterTooltip, widget);
// connect(m_counterTooltip, &CounterTooltip::finished, this, &LLMSuggestion::onCounterFinished);
const Utils::Text::Range range = suggestions()[currentSuggestion()].range;
const QTextCursor cursor = range.begin.toTextCursor(sourceDocument());
QTextCursor currentCursor = widget->textCursor();
const QString text = suggestions()[currentSuggestion()].text;

const int startPos = currentCursor.positionInBlock() - cursor.positionInBlock()
+ (cursor.selectionEnd() - cursor.selectionStart());

int next = part == Word ? Utils::endOfNextWord(text, startPos) : text.indexOf('\n', startPos);

if (next == -1)
return apply();

if (part == Line)
++next;

QString subText = text.mid(startPos, next - startPos);
if (subText.isEmpty())
return false;

currentCursor.insertText(subText);

if (const int seperatorPos = subText.lastIndexOf('\n'); seperatorPos >= 0) {
const QString newCompletionText = text.mid(startPos + seperatorPos + 1);
if (!newCompletionText.isEmpty()) {
const Utils::Text::Position newStart{int(range.begin.line + subText.count('\n')), 0};
const Utils::Text::Position
newEnd{newStart.line, int(subText.length() - seperatorPos - 1)};
const Utils::Text::Range newRange{newStart, newEnd};
const QList<Data> newSuggestion{{newRange, newEnd, newCompletionText}};
widget->insertSuggestion(
std::make_unique<LLMSuggestion>(newSuggestion, widget->document(), 0));
}
}
return false;
}

} // namespace QodeAssist
38 changes: 14 additions & 24 deletions LLMSuggestion.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
/*
/*
* Copyright (C) 2023 The Qt Company Ltd.
* Copyright (C) 2024 Petr Mironychev
*
* This file is part of QodeAssist.
*
* The Qt Company portions:
* SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
*
* Petr Mironychev portions:
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
Expand All @@ -19,36 +24,21 @@

#pragma once

#include <QObject>
#include "LSPCompletion.hpp"
#include <texteditor/textdocumentlayout.h>

#include "utils/CounterTooltip.hpp"
#include <texteditor/texteditor.h>
#include <texteditor/textsuggestion.h>

namespace QodeAssist {

class LLMSuggestion final : public QObject, public TextEditor::TextSuggestion
class LLMSuggestion : public TextEditor::CyclicSuggestion
{
Q_OBJECT
public:
LLMSuggestion(const TextEditor::TextSuggestion::Data &data, QTextDocument *origin);
enum Part { Word, Line };

LLMSuggestion(
const QList<Data> &suggestions, QTextDocument *sourceDocument, int currentCompletion = 0);

bool apply() override;
bool applyWord(TextEditor::TextEditorWidget *widget) override;
bool applyLine(TextEditor::TextEditorWidget *widget) override;
void reset();

void showTooltip(TextEditor::TextEditorWidget *widget, int count);
void onCounterFinished(int count);

private:
Completion m_completion;
QTextCursor m_start;
int m_linesCount;

CounterTooltip *m_counterTooltip = nullptr;
int m_startPosition;
TextEditor::TextSuggestion::Data m_suggestion;
bool applyPart(Part part, TextEditor::TextEditorWidget *widget);
};

} // namespace QodeAssist
13 changes: 5 additions & 8 deletions QodeAssistClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &r
auto isValidCompletion = [](const Completion &completion) {
return completion.isValid() && !completion.text().trimmed().isEmpty();
};
QList<Completion> completions = Utils::filtered(result->completions().toListOrEmpty(),
isValidCompletion);
QList<Completion> completions
= Utils::filtered(result->completions().toListOrEmpty(), isValidCompletion);

// remove trailing whitespaces from the end of the completions
for (Completion &completion : completions) {
Expand All @@ -211,9 +211,6 @@ void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &r
if (delta > 0)
completion.setText(completionText.chopped(delta));
}
if (completions.isEmpty())
return;

auto suggestions = Utils::transform(completions, [](const Completion &c) {
auto toTextPos = [](const LanguageServerProtocol::Position pos) {
return Text::Position{pos.line() + 1, pos.character()};
Expand All @@ -223,9 +220,9 @@ void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &r
Text::Position pos{toTextPos(c.position())};
return TextSuggestion::Data{range, pos, c.text()};
});

editor->insertSuggestion(
std::make_unique<LLMSuggestion>(suggestions.first(), editor->document()));
if (completions.isEmpty())
return;
editor->insertSuggestion(std::make_unique<LLMSuggestion>(suggestions, editor->document()));
}
}

Expand Down

0 comments on commit 09cde8f

Please sign in to comment.