Gå till innehållet

Skriva en inhemsk insticksmodul

En minimal inhemsk insticksmodul är en klass, ett fabriksmakro, en JSON-fil och en kort CMakeLists.txt. Du kan skriva den helt utanför källkodsträdet mot insticksmoduls-SDK:t — ingen utcheckning av MTE:s källkod krävs.

Inhemsk eller Python?

Den här sidan handlar om inhemska C++-insticksmoduler (kompilerade till en .dll/.so/.dylib och inlästa av Qt-laddaren). Vill du hellre leverera en mapp med .py-filer som redigeraren startar som en underprocess, se Python-insticksmoduler — samma yta av PluginApi, en annan backend, ingen kompilator behövs.

Utkast

Den här genomgången bygger på PLUGIN_ARCHITECTURE.md; kontrollera det exakta SDK-paketnamnet och argumenten till mte_add_plugin() mot det levererade SDK:t innan du förlitar dig på dem.

1. Implementera IPlugin

#include <PluginApi/IPlugin.h>
#include <PluginApi/IPluginContext.h>

class HelloWorldPlugin : public MTE::plugin::IPlugin {
public:
    MTE::plugin::PluginInfo info() const override {
        return { "org.mte.helloworld", "Hello World", "1.0.0",
                 "Example Vendor", "Says hi.", MTE_PLUGIN_API_VERSION };
    }

    bool initialize(MTE::plugin::IPluginContext& ctx) override {
        m_cmd = ctx.commands().registerCommand(
            { "helloworld.sayHi", "Say Hi", "Ctrl+Alt+H", "Hello" },
            [&ctx] { ctx.log().log(MTE::plugin::LogLevel::Info, "Hi!"); });
        m_menu = ctx.menus().addItem("Plugins/Say Hi", "helloworld.sayHi");
        return true;
    }

    void shutdown() noexcept override {}   // tokens tear down automatically

private:
    MTE::plugin::CommandToken m_cmd;
    MTE::plugin::MenuToken    m_menu;
};

2. Exportera fabriken (inhemsk Qt-backend)

#include <PluginApi/Qt/PluginQtGlue.h>

class HelloWorldFactory : public QObject, public MTE::plugin::IPluginFactory {
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.mte.IPluginFactory/1" FILE "plugin.json")
    Q_INTERFACES(MTE::plugin::IPluginFactory)
public:
    std::unique_ptr<MTE::plugin::IPlugin> create() override {
        return std::make_unique<HelloWorldPlugin>();
    }
};

3. Beskriv den i plugin.json

{
  "id":         "org.mte.helloworld",
  "name":       "Hello World",
  "version":    "1.0.0",
  "apiVersion": 1,
  "entry":      "native",
  "requires":   [],
  "order":      600,
  "menus": [
    { "title": "Plugins", "barPriority": 570, "itemPriority": 100 }
  ]
}

apiVersion här och den som info() returnerar måste båda vara lika med MTE_PLUGIN_API_VERSION, annars vägrar värden läsa in insticksmodulen.

Till den hör två valfria nycklar:

  • apiMinorVersion (standard 0) — den lägsta minorversionen av värdens API som din insticksmodul behöver. En värd vars MTE_PLUGIN_API_MINOR_VERSION är lägre visar modulen med «Kräver en nyare version av OctoEdit» och läser inte in den. Värden kontrollerar detta manifestvärde innan den läser in något, och därefter PluginInfo::apiMinorVersion från info() en gång till — det värdet är som standard minorversionen för de headerfiler du kompilerade mot, så låt det vara om du inte vet att du inte använder något nyare. Se Versionshantering.
  • requires (standard []) — id:n för de insticksmoduler som din modul inte kan fungera utan, till exempel ["org.mte.lsp"]. Bara id:n: det finns inget versionsintervall och ingen valfri form — en modul som din bara fungerar bättre med hör inte hit; prata med den under körning via händelser och leverantörsregister. Redigeraren initierar dina beroenden före dig, oavsett vad order säger, och stänger ner dem efter dig. Om något av dem saknas, är inaktiverat, behöver en licens som inte är aktiv eller inte lästes in, läses din modul inte in alls, och Inställningar ▸ Insticksmoduler talar om vilken modul den behöver och varför («Kräver insticksmodulen LSP Provider, som är inaktiverad»). En cykel blockerar varje modul i den. Samma nyckel betyder samma sak i plugin.json för en Python-modul.

Ange medföljande bibliotek — thirdParty

Om din insticksmodul levererar eller länkar mot en tredjepartskomponent, deklarera den i en valfri vektor thirdParty så att redigeraren kan uppfylla dess angivelseskyldigheter:

"thirdParty": [
  { "name": "Qt WebEngine", "license": "LGPL-3.0",
    "url": "https://doc.qt.io/qt-6/qtwebengine-index.html",
    "file": "QtWebEngine-LGPLv3.txt" }
]

Varje post behöver ett name (poster utan sådant faller bort); license, url och file är valfria. Licenstexterna tillhör din insticksmodul — leverera dem bredvid modulen under Plugins/Licenses/<your-plugin-id>/, där file är det bara filnamnet. Dialogrutan Om listar varje installerad insticksmoduls thirdParty-komponenter och pekar användare mot den mappen; kärnan bär aldrig en insticksmoduls tredjepartstexter åt den. (Den medföljande HtmlPreviewPlugin, som bäddar in QtWebEngine + Chromium, är referensexemplet.)

4. Bygg den

Använd SDK:ts CMake-hjälpare:

find_package(MTEPluginSDK REQUIRED)
mte_add_plugin(NAME HelloWorldPlugin
    SOURCES Src/HelloWorldPlugin.cpp
    # TRANSLATIONS Translations/HelloWorld_en.ts ...
)

Byggkrav

En inhemsk insticksmodul läses in i redigerarens process, så den måste byggas med en verktygskedja som är binärkompatibel med det redigerarbygge den riktar sig mot. Det här är kraven för den aktuella utgåvan (byggd med Qt 6.11.2):

Windows macOS
Kompilator Visual Studio 2022, MSVC-verktygsuppsättning 14.44 eller en äldre 14.x (VS 2015–2022 delar ett C++-ABI); clang-cl fungerar också. Bygg med MinGW / GCC kan inte läsas in. Apple clang från Xcode (Xcode 27, Apple clang 21.0.0); enbart arm64
Byggkonfiguration Release, dynamisk runtime (/MD, CMake:s standard). En Debug-insticksmodul avvisas av laddaren; en insticksmodul med statisk runtime (/MT) kraschar när minne korsar modulgränsen Release; deployment target 14.4 (samma som redigeraren)
Qt Qt 6.11, kitet msvc2022_64. En insticksmodul byggd mot en äldre 6.x-minor läses in; en byggd mot en nyare minor kanske inte gör det Qt 6.11, samma arkitektur
C++-standard C++20 (API-headerna använder std::span och koncept) C++20
CMake 3.16+ 3.16+
Insticksmoduls-API MTE_PLUGIN_API_VERSION för det SDK som följde med den redigerarversion du riktar dig mot detsamma

Två följder värda att känna till på Windows:

  • Den Visual C++ Redistributable som är installerad på användarens maskin måste vara minst lika ny som den nyaste MSVC-verktygsuppsättning som redigeraren eller någon inläst insticksmodul använt. Att bygga din insticksmodul med en nyare verktygsuppsättning än redigerarens höjer det kravet för dina användare; att bygga med redigerarens verktygsuppsättning eller en äldre gör det inte.
  • Den version av Windows SDK du kompilerar mot påverkar inte kompatibiliteten med redigeraren; den avgör bara vilka Windows-API:er din insticksmodul kan anropa, och redigeraren stöder Windows 10 1809 och senare.

Och på macOS:

  • Redigeraren levereras enbart som arm64; en insticksmodul som bara är x86_64 kan inte läsas in (en universell binär går bra). Håll deployment target på 14.4: en insticksmodul byggd för ett nyare minsta macOS avvisas av systemets laddare på äldre Mac-datorer, och eftersom operativsystemets egen libc++ är runtime uppstår ingen fråga om någon redistributable.
  • Hänvisa till Qt-ramverken genom @rpath så att insticksmodulen plockar upp de kopior som medföljer inuti OctoEdit.app. Qt-kit vars ramverk bär absoluta installationsnamn (Homebrews, till exempel) behöver install_name_tool på den byggda insticksmodulen före leverans; annars letar modulen efter Qt på byggmaskinens sökväg.

Inget av detta gäller Python-insticksmoduler: de körs i en separat process och delar inget binärt gränssnitt med redigeraren.

5. Installera och kör

Du kan komprimera det byggda biblioteket + plugin.json som <name>.mteplugin och installera det via Inställningar ▸ Insticksmoduler ▸ Installera insticksmodul... (paketet valideras — manifest, arkivets säkerhet och binärens inbäddade metadata — innan något rörs), eller lägga filerna för hand i en upptäckt insticksmodulskatalog (se Arkitekturöversikt). Starta om redigeraren och aktivera den vid behov i Inställningar ▸ Insticksmoduler.

Nästa