mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2025-05-28 03:10:28 -04:00
♻️ refactor: Multiline text suggestion
This commit is contained in:
parent
ac8080542d
commit
09cde8fd3d
@ -1,8 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2023 The Qt Company Ltd.
|
||||||
* Copyright (C) 2024 Petr Mironychev
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
*
|
*
|
||||||
* This file is part of QodeAssist.
|
* 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
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -18,27 +23,25 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "LLMSuggestion.hpp"
|
#include "LLMSuggestion.hpp"
|
||||||
|
|
||||||
#include <QTextCursor>
|
|
||||||
#include <QtWidgets/qtoolbar.h>
|
|
||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
#include <utils/stringutils.h>
|
#include <utils/stringutils.h>
|
||||||
#include <utils/tooltip/tooltip.h>
|
#include <utils/tooltip/tooltip.h>
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
LLMSuggestion::LLMSuggestion(const TextEditor::TextSuggestion::Data &data, QTextDocument *origin)
|
LLMSuggestion::LLMSuggestion(
|
||||||
: TextEditor::TextSuggestion(data, origin)
|
const QList<Data> &suggestions, QTextDocument *sourceDocument, int currentCompletion)
|
||||||
, m_linesCount(0)
|
: TextEditor::CyclicSuggestion(suggestions, sourceDocument, currentCompletion)
|
||||||
, m_suggestion(data)
|
|
||||||
{
|
{
|
||||||
int startPos = data.range.begin.toPositionInDocument(origin);
|
const auto &data = suggestions[currentCompletion];
|
||||||
int endPos = data.range.end.toPositionInDocument(origin);
|
|
||||||
|
|
||||||
startPos = qBound(0, startPos, origin->characterCount() - 1);
|
int startPos = data.range.begin.toPositionInDocument(sourceDocument);
|
||||||
endPos = qBound(startPos, endPos, origin->characterCount() - 1);
|
int endPos = data.range.end.toPositionInDocument(sourceDocument);
|
||||||
|
|
||||||
QTextCursor cursor(origin);
|
startPos = qBound(0, startPos, sourceDocument->characterCount() - 1);
|
||||||
|
endPos = qBound(startPos, endPos, sourceDocument->characterCount() - 1);
|
||||||
|
|
||||||
|
QTextCursor cursor(sourceDocument);
|
||||||
cursor.setPosition(startPos);
|
cursor.setPosition(startPos);
|
||||||
cursor.setPosition(endPos, QTextCursor::KeepAnchor);
|
cursor.setPosition(endPos, QTextCursor::KeepAnchor);
|
||||||
|
|
||||||
@ -49,64 +52,56 @@ LLMSuggestion::LLMSuggestion(const TextEditor::TextSuggestion::Data &data, QText
|
|||||||
int endPosInBlock = endPos - block.position();
|
int endPosInBlock = endPos - block.position();
|
||||||
|
|
||||||
blockText.replace(startPosInBlock, endPosInBlock - startPosInBlock, data.text);
|
blockText.replace(startPosInBlock, endPosInBlock - startPosInBlock, data.text);
|
||||||
|
|
||||||
replacementDocument()->setPlainText(blockText);
|
replacementDocument()->setPlainText(blockText);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLMSuggestion::apply()
|
|
||||||
{
|
|
||||||
return TextEditor::TextSuggestion::apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LLMSuggestion::applyWord(TextEditor::TextEditorWidget *widget)
|
bool LLMSuggestion::applyWord(TextEditor::TextEditorWidget *widget)
|
||||||
{
|
{
|
||||||
return TextEditor::TextSuggestion::applyWord(widget);
|
return applyPart(Word, widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLMSuggestion::applyLine(TextEditor::TextEditorWidget *widget)
|
bool LLMSuggestion::applyLine(TextEditor::TextEditorWidget *widget)
|
||||||
{
|
{
|
||||||
return TextEditor::TextSuggestion::applyLine(widget);
|
return applyPart(Line, widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMSuggestion::reset()
|
bool LLMSuggestion::applyPart(Part part, TextEditor::TextEditorWidget *widget)
|
||||||
{
|
{
|
||||||
reset();
|
const Utils::Text::Range range = suggestions()[currentSuggestion()].range;
|
||||||
m_linesCount = 0;
|
const QTextCursor cursor = range.begin.toTextCursor(sourceDocument());
|
||||||
}
|
QTextCursor currentCursor = widget->textCursor();
|
||||||
|
const QString text = suggestions()[currentSuggestion()].text;
|
||||||
|
|
||||||
void LLMSuggestion::onCounterFinished(int count)
|
const int startPos = currentCursor.positionInBlock() - cursor.positionInBlock()
|
||||||
{
|
+ (cursor.selectionEnd() - cursor.selectionStart());
|
||||||
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');
|
int next = part == Word ? Utils::endOfNextWord(text, startPos) : text.indexOf('\n', startPos);
|
||||||
QString textToInsert = lines.mid(0, count).join('\n');
|
|
||||||
|
|
||||||
cursor.insertText(textToInsert);
|
if (next == -1)
|
||||||
cursor.endEditBlock();
|
return apply();
|
||||||
}
|
|
||||||
|
|
||||||
// void LLMSuggestion::reset()
|
if (part == Line)
|
||||||
// {
|
++next;
|
||||||
// m_start.removeSelectedText();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// int LLMSuggestion::position()
|
QString subText = text.mid(startPos, next - startPos);
|
||||||
// {
|
if (subText.isEmpty())
|
||||||
// return m_start.position();
|
return false;
|
||||||
// }
|
|
||||||
|
|
||||||
void LLMSuggestion::showTooltip(TextEditor::TextEditorWidget *widget, int count)
|
currentCursor.insertText(subText);
|
||||||
{
|
|
||||||
// Utils::ToolTip::hide();
|
if (const int seperatorPos = subText.lastIndexOf('\n'); seperatorPos >= 0) {
|
||||||
// QPoint pos = widget->mapToGlobal(widget->cursorRect().topRight());
|
const QString newCompletionText = text.mid(startPos + seperatorPos + 1);
|
||||||
// pos += QPoint(-10, -50);
|
if (!newCompletionText.isEmpty()) {
|
||||||
// m_counterTooltip = new CounterTooltip(count);
|
const Utils::Text::Position newStart{int(range.begin.line + subText.count('\n')), 0};
|
||||||
// Utils::ToolTip::show(pos, m_counterTooltip, widget);
|
const Utils::Text::Position
|
||||||
// connect(m_counterTooltip, &CounterTooltip::finished, this, &LLMSuggestion::onCounterFinished);
|
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
|
} // namespace QodeAssist
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2023 The Qt Company Ltd.
|
||||||
* Copyright (C) 2024 Petr Mironychev
|
* Copyright (C) 2024 Petr Mironychev
|
||||||
*
|
*
|
||||||
* This file is part of QodeAssist.
|
* 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
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -19,36 +24,21 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QObject>
|
#include <texteditor/texteditor.h>
|
||||||
#include "LSPCompletion.hpp"
|
#include <texteditor/textsuggestion.h>
|
||||||
#include <texteditor/textdocumentlayout.h>
|
|
||||||
|
|
||||||
#include "utils/CounterTooltip.hpp"
|
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
class LLMSuggestion final : public QObject, public TextEditor::TextSuggestion
|
class LLMSuggestion : public TextEditor::CyclicSuggestion
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
public:
|
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 applyWord(TextEditor::TextEditorWidget *widget) override;
|
||||||
bool applyLine(TextEditor::TextEditorWidget *widget) override;
|
bool applyLine(TextEditor::TextEditorWidget *widget) override;
|
||||||
void reset();
|
bool applyPart(Part part, TextEditor::TextEditorWidget *widget);
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
@ -193,8 +193,8 @@ void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &r
|
|||||||
auto isValidCompletion = [](const Completion &completion) {
|
auto isValidCompletion = [](const Completion &completion) {
|
||||||
return completion.isValid() && !completion.text().trimmed().isEmpty();
|
return completion.isValid() && !completion.text().trimmed().isEmpty();
|
||||||
};
|
};
|
||||||
QList<Completion> completions = Utils::filtered(result->completions().toListOrEmpty(),
|
QList<Completion> completions
|
||||||
isValidCompletion);
|
= Utils::filtered(result->completions().toListOrEmpty(), isValidCompletion);
|
||||||
|
|
||||||
// remove trailing whitespaces from the end of the completions
|
// remove trailing whitespaces from the end of the completions
|
||||||
for (Completion &completion : completions) {
|
for (Completion &completion : completions) {
|
||||||
@ -211,9 +211,6 @@ void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &r
|
|||||||
if (delta > 0)
|
if (delta > 0)
|
||||||
completion.setText(completionText.chopped(delta));
|
completion.setText(completionText.chopped(delta));
|
||||||
}
|
}
|
||||||
if (completions.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto suggestions = Utils::transform(completions, [](const Completion &c) {
|
auto suggestions = Utils::transform(completions, [](const Completion &c) {
|
||||||
auto toTextPos = [](const LanguageServerProtocol::Position pos) {
|
auto toTextPos = [](const LanguageServerProtocol::Position pos) {
|
||||||
return Text::Position{pos.line() + 1, pos.character()};
|
return Text::Position{pos.line() + 1, pos.character()};
|
||||||
@ -223,9 +220,9 @@ void QodeAssistClient::handleCompletions(const GetCompletionRequest::Response &r
|
|||||||
Text::Position pos{toTextPos(c.position())};
|
Text::Position pos{toTextPos(c.position())};
|
||||||
return TextSuggestion::Data{range, pos, c.text()};
|
return TextSuggestion::Data{range, pos, c.text()};
|
||||||
});
|
});
|
||||||
|
if (completions.isEmpty())
|
||||||
editor->insertSuggestion(
|
return;
|
||||||
std::make_unique<LLMSuggestion>(suggestions.first(), editor->document()));
|
editor->insertSuggestion(std::make_unique<LLMSuggestion>(suggestions, editor->document()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user