Lua: Change TextDocument to TextEditor

Since there may be many editors editing a single TextDocument
it is more useful to start at the TextEditor level.

Change-Id: Ic1c4ea76f22ab0d0741edd6f6ecc24d6befa17e6
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Marcus Tillmanns
2024-07-18 11:22:27 +02:00
parent 2bcc21d598
commit 42e57d60ae
7 changed files with 238 additions and 191 deletions

View File

@@ -16,7 +16,7 @@ add_qtc_plugin(Lua
bindings/messagemanager.cpp
bindings/qtcprocess.cpp
bindings/settings.cpp
bindings/textdocument.cpp
bindings/texteditor.cpp
bindings/utils.cpp
luaengine.cpp
luaengine.h

View File

@@ -1,168 +0,0 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "../luaengine.h"
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/textdocument.h>
#include "sol/sol.hpp"
namespace Lua::Internal {
class TextDocumentRegistry : public QObject
{
Q_OBJECT
public:
static TextDocumentRegistry *instance()
{
static TextDocumentRegistry *instance = new TextDocumentRegistry();
return instance;
}
TextDocumentRegistry()
{
connect(
Core::EditorManager::instance(),
&Core::EditorManager::documentOpened,
this,
&TextDocumentRegistry::onDocumentOpened);
connect(
Core::EditorManager::instance(),
&Core::EditorManager::documentClosed,
this,
&TextDocumentRegistry::onDocumentClosed);
connect(
Core::EditorManager::instance(),
&Core::EditorManager::currentEditorChanged,
this,
[this](Core::IEditor *editor) {
if (!editor) {
emit currentDocumentChanged(nullptr);
return;
}
auto document = editor->document();
auto textDocument = qobject_cast<TextEditor::TextDocument *>(document);
if (textDocument) {
emit currentDocumentChanged(textDocument);
return;
}
emit currentDocumentChanged(nullptr);
});
}
void onDocumentOpened(Core::IDocument *document)
{
auto textDocument = qobject_cast<TextEditor::TextDocument *>(document);
if (!textDocument)
return;
connect(
textDocument,
&TextEditor::TextDocument::contentsChangedWithPosition,
this,
[this, textDocument](int position, int charsRemoved, int charsAdded) {
emit documentContentsChanged(textDocument, position, charsRemoved, charsAdded);
});
emit documentOpened(textDocument);
}
void onDocumentClosed(Core::IDocument *document)
{
auto textDocument = qobject_cast<TextEditor::TextDocument *>(document);
if (!textDocument)
return;
document->disconnect(this);
emit documentClosed(textDocument);
}
signals:
void documentOpened(TextEditor::TextDocument *document);
void currentDocumentChanged(TextEditor::TextDocument *document);
void documentClosed(TextEditor::TextDocument *document);
void documentContentsChanged(
TextEditor::TextDocument *document, int position, int charsRemoved, int charsAdded);
};
void addTextDocumentsModule()
{
TextDocumentRegistry::instance();
LuaEngine::registerProvider("TextDocument", [](sol::state_view lua) -> sol::object {
sol::table documents = lua.create_table();
documents.new_usertype<TextEditor::TextDocument>(
"TextDocument",
sol::no_constructor,
"file",
&TextEditor::TextDocument::filePath,
"lineAndColumn",
[](TextEditor::TextDocument *document,
int position) -> std::optional<std::pair<int, int>> {
QTextBlock block = document->document()->findBlock(position);
if (!block.isValid())
return std::nullopt;
int column = position - block.position();
return std::make_pair(block.blockNumber() + 1, column + 1);
});
return documents;
});
LuaEngine::registerHook("editors.text.opened", [](sol::function func, QObject *guard) {
QObject::connect(
TextDocumentRegistry::instance(),
&TextDocumentRegistry::documentOpened,
guard,
[func](TextEditor::TextDocument *document) {
Utils::expected_str<void> res = LuaEngine::void_safe_call(func, document);
QTC_CHECK_EXPECTED(res);
});
});
LuaEngine::registerHook("editors.text.closed", [](sol::function func, QObject *guard) {
QObject::connect(
TextDocumentRegistry::instance(),
&TextDocumentRegistry::documentClosed,
guard,
[func](TextEditor::TextDocument *document) {
Utils::expected_str<void> res = LuaEngine::void_safe_call(func, document);
QTC_CHECK_EXPECTED(res);
});
});
LuaEngine::registerHook("editors.text.contentsChanged", [](sol::function func, QObject *guard) {
QObject::connect(
TextDocumentRegistry::instance(),
&TextDocumentRegistry::documentContentsChanged,
guard,
[func](TextEditor::TextDocument *document, int position, int charsRemoved, int charsAdded) {
Utils::expected_str<void> res
= LuaEngine::void_safe_call(func, document, position, charsRemoved, charsAdded);
QTC_CHECK_EXPECTED(res);
});
});
LuaEngine::registerHook("editors.text.currentChanged", [](sol::function func, QObject *guard) {
QObject::connect(
TextDocumentRegistry::instance(),
&TextDocumentRegistry::currentDocumentChanged,
guard,
[func](TextEditor::TextDocument *document) {
Utils::expected_str<void> res = LuaEngine::void_safe_call(func, document);
QTC_CHECK_EXPECTED(res);
});
});
}
} // namespace Lua::Internal
#include "textdocument.moc"

View File

@@ -0,0 +1,190 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "../luaengine.h"
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
#include "sol/sol.hpp"
namespace Lua::Internal {
class TextEditorRegistry : public QObject
{
Q_OBJECT
public:
static TextEditorRegistry *instance()
{
static TextEditorRegistry *instance = new TextEditorRegistry();
return instance;
}
TextEditorRegistry()
{
connect(
Core::EditorManager::instance(),
&Core::EditorManager::currentEditorChanged,
this,
[this](Core::IEditor *editor) {
if (!editor) {
emit currentEditorChanged(nullptr);
return;
}
if (m_currentTextEditor) {
m_currentTextEditor->disconnect(this);
m_currentTextEditor->editorWidget()->disconnect(this);
m_currentTextEditor->document()->disconnect(this);
m_currentTextEditor = nullptr;
}
m_currentTextEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor);
if (m_currentTextEditor) {
if (!connectTextEditor(m_currentTextEditor))
m_currentTextEditor = nullptr;
}
emit currentEditorChanged(m_currentTextEditor);
});
}
bool connectTextEditor(TextEditor::BaseTextEditor *editor)
{
auto textEditorWidget = editor->editorWidget();
if (!textEditorWidget)
return false;
TextEditor::TextDocument *textDocument = editor->textDocument();
if (!textDocument)
return false;
connect(
textEditorWidget,
&TextEditor::TextEditorWidget::cursorPositionChanged,
this,
[editor, textEditorWidget, this]() {
emit currentCursorChanged(editor, textEditorWidget->multiTextCursor());
});
connect(
textDocument,
&TextEditor::TextDocument::contentsChangedWithPosition,
this,
[this, textDocument](int position, int charsRemoved, int charsAdded) {
emit documentContentsChanged(textDocument, position, charsRemoved, charsAdded);
});
return true;
}
signals:
void currentEditorChanged(TextEditor::BaseTextEditor *editor);
void documentContentsChanged(
TextEditor::TextDocument *document, int position, int charsRemoved, int charsAdded);
void currentCursorChanged(TextEditor::BaseTextEditor *editor, Utils::MultiTextCursor cursor);
protected:
QPointer<TextEditor::BaseTextEditor> m_currentTextEditor = nullptr;
};
void addTextEditorModule()
{
TextEditorRegistry::instance();
LuaEngine::registerProvider("TextDocument", [](sol::state_view lua) -> sol::object {
sol::table documents = lua.create_table();
documents.new_usertype<Utils::MultiTextCursor>(
"MultiTextCursor",
sol::no_constructor,
"mainCursor",
&Utils::MultiTextCursor::mainCursor,
"cursors",
&Utils::MultiTextCursor::cursors);
documents.new_usertype<QTextCursor>(
"TextCursor",
sol::no_constructor,
"position",
&QTextCursor::position,
"blockNumber",
&QTextCursor::blockNumber,
"columnNumber",
&QTextCursor::columnNumber);
documents.new_usertype<TextEditor::BaseTextEditor>(
"TextEditor",
sol::no_constructor,
"document",
&TextEditor::BaseTextEditor::textDocument,
"cursor",
[](TextEditor::BaseTextEditor *textEditor) {
return textEditor->editorWidget()->multiTextCursor();
});
documents.new_usertype<TextEditor::TextDocument>(
"TextDocument",
sol::no_constructor,
"file",
&TextEditor::TextDocument::filePath,
"blockAndColumn",
[](TextEditor::TextDocument *document,
int position) -> std::optional<std::pair<int, int>> {
QTextBlock block = document->document()->findBlock(position);
if (!block.isValid())
return std::nullopt;
int column = position - block.position();
return std::make_pair(block.blockNumber() + 1, column + 1);
},
"blockCount",
[](TextEditor::TextDocument *document) { return document->document()->blockCount(); });
return documents;
});
LuaEngine::registerHook("editors.text.currentChanged", [](sol::function func, QObject *guard) {
QObject::connect(
TextEditorRegistry::instance(),
&TextEditorRegistry::currentEditorChanged,
guard,
[func](TextEditor::BaseTextEditor *editor) {
Utils::expected_str<void> res = LuaEngine::void_safe_call(func, editor);
QTC_CHECK_EXPECTED(res);
});
});
LuaEngine::registerHook("editors.text.contentsChanged", [](sol::function func, QObject *guard) {
QObject::connect(
TextEditorRegistry::instance(),
&TextEditorRegistry::documentContentsChanged,
guard,
[func](TextEditor::TextDocument *document, int position, int charsRemoved, int charsAdded) {
Utils::expected_str<void> res
= LuaEngine::void_safe_call(func, document, position, charsRemoved, charsAdded);
QTC_CHECK_EXPECTED(res);
});
});
LuaEngine::registerHook("editors.text.cursorChanged", [](sol::function func, QObject *guard) {
QObject::connect(
TextEditorRegistry::instance(),
&TextEditorRegistry::currentCursorChanged,
guard,
[func](TextEditor::BaseTextEditor *editor, const Utils::MultiTextCursor &cursor) {
Utils::expected_str<void> res = LuaEngine::void_safe_call(func, editor, cursor);
QTC_CHECK_EXPECTED(res);
});
});
}
} // namespace Lua::Internal
#include "texteditor.moc"

View File

@@ -45,7 +45,7 @@ void addMessageManagerModule();
void addProcessModule();
void addQtModule();
void addSettingsModule();
void addTextDocumentsModule();
void addTextEditorModule();
void addUtilsModule();
class LuaJsExtension : public QObject
@@ -261,7 +261,7 @@ public:
addProcessModule();
addQtModule();
addSettingsModule();
addTextDocumentsModule();
addTextEditorModule();
addUtilsModule();
Core::JsExpander::registerGlobalObject("Lua", [] { return new LuaJsExtension(); });

View File

@@ -40,10 +40,9 @@ QtcPluginDependency = {}
EditorHooks = {}
---@class TextEditorHooks
---@field opened? function function(Documents.TextDocument)
---@field closed? function function(Documents.TextDocument)
---@field currentChanged? function function(Documents.TextDocument)
---@field contentsChanged? function function(document: Documents.TextDocument, position: integer, charsRemoved: integer, charsAdded: integer)
---@field currentChanged? function function(editor: TextEditor)
---@field contentsChanged? function function(document: TextDocument, position: integer, charsRemoved: integer, charsAdded: integer)
---@field cursorChanged? function function(editor: TextEditor, cursor: MultiTextCursor)
---@class Hooks
---@field editors? EditorHooks

View File

@@ -1,16 +0,0 @@
---@meta TextDocument
---@class TextDocument
local TextDocument = {}
---Returns the file path of the document.
---@return FilePath filePath The file path of the document.
function TextDocument:file() end
---Returns the line and column of the given position.
---@param position integer The position to convert.
---@return integer line The line of the position.
---@return integer column The column of the position.
function TextDocument:lineAndColumn(position) end
return TextDocument

View File

@@ -0,0 +1,42 @@
---@meta TextEditor
---@class TextCursor
---@field position integer The position of the cursor.
---@field blockNumber integer The block (line) number of the cursor.
---@field columnNumber integer The column number of the cursor.
local TextCursor = {}
---@class MultiTextCursor
---@field mainCursor TextCursor The main cursor.
---@field cursors TextCursor[] The cursors.
local MultiTextCursor = {}
---@class TextDocument
local TextDocument = {}
---Returns the file path of the document.
---@return FilePath filePath The file path of the document.
function TextDocument:file() end
---Returns the block (line) and column for the given position.
---@param position integer The position to convert.
---@return integer block The block (line) of the position.
---@return integer column The column of the position.
function TextDocument:blockAndColumn(position) end
---Returns the number of blocks (lines) in the document.
---@return integer blockCount The number of blocks in the document.
function TextDocument:blockCount() end
---@class TextEditor
local TextEditor = {}
---Returns the document of the editor.
---@return TextDocument document The document of the editor.
function TextEditor:document() end
---Returns the cursor of the editor.
---@return MultiTextCursor cursor The cursor of the editor.
function TextEditor:cursor() end
return TextDocument