LUA: Add setSuggestions() in TextEditor bindings

Change-Id: I29067ab0beb8eeddc4348bb0ddf2ab37a4af8a09
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
Reviewed-by: <mua@spyro-soft.com>
This commit is contained in:
Lukasz Papierkowski
2024-07-19 11:44:26 +02:00
committed by lie
parent b733a16cce
commit 8b4cb5f9f5
2 changed files with 175 additions and 3 deletions

View File

@@ -3,13 +3,138 @@
#include "../luaengine.h" #include "../luaengine.h"
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <utils/stringutils.h>
#include "sol/sol.hpp" #include "sol/sol.hpp"
namespace {
class Suggestion
{
public:
Suggestion(
Utils::Text::Position start,
Utils::Text::Position end,
Utils::Text::Position position,
const QString &text)
: m_start(start)
, m_end(end)
, m_position(position)
, m_text(text)
{}
Suggestion(const Suggestion &other)
: m_start(other.m_start)
, m_end(other.m_end)
, m_position(other.m_position)
, m_text(other.m_text)
{}
Utils::Text::Position start() const { return m_start; }
Utils::Text::Position end() const { return m_end; }
Utils::Text::Position position() const { return m_position; }
QString text() const { return m_text; }
private:
Utils::Text::Position m_start;
Utils::Text::Position m_end;
Utils::Text::Position m_position;
QString m_text;
};
QTextCursor toTextCursor(QTextDocument *doc, const Utils::Text::Position &position)
{
QTextCursor cursor(doc);
cursor.setPosition(position.toPositionInDocument(doc));
return cursor;
}
QTextCursor toSelection(
QTextDocument *doc, const Utils::Text::Position &start, const Utils::Text::Position &end)
{
QTC_ASSERT(doc, return {});
QTextCursor cursor = toTextCursor(doc, start);
cursor.setPosition(end.toPositionInDocument(doc), QTextCursor::KeepAnchor);
return cursor;
}
class CyclicSuggestion : public TextEditor::TextSuggestion
{
public:
CyclicSuggestion(
const QList<Suggestion> &suggestions, QTextDocument *origin, int current_suggestion = 0)
: m_current_suggestion(current_suggestion)
, m_suggestions(suggestions)
{
QTC_ASSERT(current_suggestion < suggestions.size(), return);
const auto &suggestion = m_suggestions.at(m_current_suggestion);
const auto start = suggestion.start();
const auto end = suggestion.end();
QString text = toTextCursor(origin, start).block().text();
int length = text.length() - start.column;
if (start.line == end.line)
length = end.column - start.column;
text.replace(start.column, length, suggestion.text());
document()->setPlainText(text);
m_start = toTextCursor(origin, suggestion.position());
m_start.setKeepPositionOnInsert(true);
setCurrentPosition(m_start.position());
}
virtual bool apply() override
{
QTC_ASSERT(m_current_suggestion < m_suggestions.size(), return false);
reset();
const auto &suggestion = m_suggestions.at(m_current_suggestion);
QTextCursor cursor = toSelection(m_start.document(), suggestion.start(), suggestion.end());
cursor.insertText(suggestion.text());
return true;
}
// Returns true if the suggestion was applied completely, false if it was only partially applied.
virtual bool applyWord(TextEditor::TextEditorWidget *widget) override
{
QTC_ASSERT(m_current_suggestion < m_suggestions.size(), return false);
const auto &suggestion = m_suggestions.at(m_current_suggestion);
QTextCursor cursor = toSelection(m_start.document(), suggestion.start(), suggestion.end());
QTextCursor currentCursor = widget->textCursor();
const QString text = suggestion.text();
const int startPos = currentCursor.positionInBlock() - cursor.positionInBlock()
+ (cursor.selectionEnd() - cursor.selectionStart());
const int next = Utils::endOfNextWord(text, startPos);
if (next == -1)
return apply();
// TODO: Allow adding more than one line
QString subText = text.mid(startPos, next - startPos);
subText = subText.left(subText.indexOf('\n'));
if (subText.isEmpty())
return false;
currentCursor.insertText(subText);
return false;
}
virtual void reset() override { m_start.removeSelectedText(); }
virtual int position() override { return m_start.selectionEnd(); }
private:
int m_current_suggestion;
QTextCursor m_start;
QList<Suggestion> m_suggestions;
};
} // namespace
namespace Lua::Internal { namespace Lua::Internal {
class TextEditorRegistry : public QObject class TextEditorRegistry : public QObject
@@ -132,6 +257,20 @@ void addTextEditorModule()
return textEditor->editorWidget()->multiTextCursor(); return textEditor->editorWidget()->multiTextCursor();
}); });
result.new_usertype<Suggestion>(
"Suggestion",
"create",
[](int start_line,
int start_character,
int end_line,
int end_character,
const QString &text) -> Suggestion {
auto one_based = [](int zero_based) { return zero_based + 1; };
Utils::Text::Position start_pos = {one_based(start_line), start_character};
Utils::Text::Position end_pos = {one_based(end_line), end_character};
return {start_pos, end_pos, start_pos, text};
});
result.new_usertype<TextEditor::TextDocument>( result.new_usertype<TextEditor::TextDocument>(
"TextDocument", "TextDocument",
sol::no_constructor, sol::no_constructor,
@@ -149,7 +288,26 @@ void addTextEditorModule()
return std::make_pair(block.blockNumber() + 1, column + 1); return std::make_pair(block.blockNumber() + 1, column + 1);
}, },
"blockCount", "blockCount",
[](TextEditor::TextDocument *document) { return document->document()->blockCount(); }); [](TextEditor::TextDocument *document) { return document->document()->blockCount(); },
"setSuggestions",
[](TextEditor::TextDocument *document, QList<Suggestion> suggestions) {
if (suggestions.isEmpty())
return;
const auto textEditor = TextEditor::BaseTextEditor::currentTextEditor();
if (!textEditor || textEditor->document() != document)
return;
auto *widget = textEditor->editorWidget();
if (widget->isReadOnly() || widget->multiTextCursor().hasMultipleCursors())
return;
widget->insertSuggestion(
std::make_unique<CyclicSuggestion>(suggestions, document->document()));
}
);
return result; return result;
}); });

View File

@@ -29,6 +29,17 @@ function TextDocument:blockAndColumn(position) end
---@return integer blockCount The number of blocks in the document. ---@return integer blockCount The number of blocks in the document.
function TextDocument:blockCount() end function TextDocument:blockCount() end
---@class Suggestion
local Suggestion = {}
---@param startLine integer Start position line where to apply the suggestion.
---@param startCharacter integer Start position character where to apply the suggestion.
---@param endLine integer End position line where to apply the suggestion.
---@param endCharacter integer End position character where to apply the suggestion.
---@param text string Suggestions text.
---@return Suggestion suggestion The created suggestion.
function Suggestion:create(startLine, startCharacter, endLine, endCharacter, text) end
---@class TextEditor ---@class TextEditor
local TextEditor = {} local TextEditor = {}
@@ -40,6 +51,9 @@ function TextEditor:document() end
---@return MultiTextCursor cursor The cursor of the editor. ---@return MultiTextCursor cursor The cursor of the editor.
function TextEditor:cursor() end function TextEditor:cursor() end
---@param suggestions Suggestion[] A list of possible suggestions to display
function TextEditor:setSuggestions(suggestions) end
---Returns the current editor or nil. ---Returns the current editor or nil.
---@return TextEditor|nil editor The currently active editor or nil if there is none. ---@return TextEditor|nil editor The currently active editor or nil if there is none.
function textEditor.currentEditor() end function textEditor.currentEditor() end