Gå till innehållet

Radmärken och marginalklicket

Ett radmärke är en symbol i redigerarens bokmärkeskolumn och/eller en toning över hela raden, fäst vid en rad i ett dokument och buren med den medan användaren redigerar. Brytpunkter (fyllda, ihåliga, villkorliga), en pil för aktuell sats och versionshanteringens ändringsremsor är alla radmärken. Du når tjänsten via IPluginContext::lineMarkers() (PluginApi/ILineMarkerService.h).

Insticksmodulen beskriver hur en typ av märke ser ut och var märken av den typen sitter; ritandet, marginalen och redigeringsmotorns märkesplatser tillhör redigeraren. Ingen pixelstorlek, inget motornummer och ingen Qt-typ korsar gränsen.

Typer

Registrera varje typ av märke en gång, i initialize(), och behåll token:

enum class LineMarkerGlyph : std::int32_t {
    None, Circle, CircleHollow, CircleBar, Arrow, Strip
};

struct LineMarkerKind {
    LineMarkerGlyph glyph = LineMarkerGlyph::Circle;
    std::string     glyphColor;  // a theme category you registered
    std::string     lineTint;    // a theme category; empty = no tint
    std::int32_t    zOrder = 0;  // higher draws above lower
};

m_breakpoint = ctx.lineMarkers().registerKind(kind);   // LineMarkerKindToken
if (!m_breakpoint.valid()) { /* no slot left: see below */ }
  • Färger är id:n för temakategorier som du själv har registrerat med ctx.themePalette(), så märkena följer det aktiva temat och användaren kan färga om dem i Temaredigeraren; redigeraren tillämpar dem på nytt vid varje temabyte.
  • Symboler är en liten, fast uppsättning som varje värd kan rita i en smal marginal vid alla zoomnivåer: Circle (en brytpunkt), CircleHollow (en inaktiverad eller obekräftad brytpunkt), CircleBar (en villkorlig brytpunkt), Arrow (aktuell sats), Strip (en ändrad rad). None ger en typ som bara tonar.
  • zOrder avgör vilket märke som ritas överst när flera delar en rad (pilen för aktuell sats ovanför en brytpunkt, en brytpunkt ovanför en ändringsremsa); vid lika värden hamnar den senare registrerade typen överst. Ordningen gäller även mellan insticksmoduler.
  • Registreringen kan misslyckas. Redigeringsmotorn har ett litet, fast antal märkesplatser som alla insticksmoduler delar — nio; en typ med symbol och toning använder två. När de tar slut kommer token tillbaka tom, och loggen talar om varför. Registrera de typer du behöver en gång och behåll dem; registrera aldrig en typ per dokument eller per märke.
  • Att förstöra token tar bort alla märken av den typen från alla dokument och drar tillbaka typen.

Märken

using LineMarkerId = std::uint64_t;                 // 0 is never valid

LineMarkerId id = ctx.lineMarkers().addMarker(m_breakpoint.id(), doc, line);
std::int64_t now = ctx.lineMarkers().lineOf(id);     // -1 = gone
auto all = ctx.lineMarkers().markers(m_breakpoint.id(), doc); // {id, line} by line
ctx.lineMarkers().removeMarker(id);
ctx.lineMarkers().clearMarkers(m_breakpoint.id(), 0);           // 0 = every document

addMarker returnerar 0 — och placerar ingenting — när typen inte lever, dokumentet inte är öppet eller raden ligger utanför dokumentet. En rad begränsas inte: en brytpunkt på fel rad är värre än ingen. Ett handtag är ett enkelt värde, inte en token; behåll så många du behöver.

Vad som sedan händer med ett märke:

  • Redigeringar flyttar det med dess rad: rader som infogas eller tas bort ovanför förskjuter det, att skriva på raden behåller det — så följer en brytpunkt koden.
  • Dess rad tas bort (del av ett borttaget intervall): det överlever och hamnar på raden där det borttagna intervallet började. Två märken kan på så sätt hamna på samma rad.
  • Hela bufferten ersätts (en omläsning efter en ändring utifrån, IEditorService::setText): varje märke stannar på samma radnummer; ett märke vars rad inte längre finns tas bort. Redigeraren jämför inte gammal och ny text.
  • Dokumentet stängs: dess märken tas bort och deras handtag slutar gälla. Redigeraren sparar aldrig märken — en insticksmodul som behåller märken efter en stängning (brytpunkter) håller dem i sin egen modell och lägger till dem igen vid DocumentOpened (en återöppnad fil är ett nytt DocumentId).

Märken tillhör dokumentet: placerade en gång syns de i varje ruta i ett delat dokument. Minikartan, dess förhandsvisning vid hovring och jämförelsevyn visar dem aldrig. Bokmärken är inte märken i den här tjänsten; de behåller sina egna IEditorService-anrop.

Marginalklicket

Varje vänsterklick i en redigerares bokmärkeskolumn publiceras som events::MarginClicked:

struct MarginClicked {
    DocumentId   doc;
    std::int64_t line;       // 0-based, level with the click
    EditorMargin margin;     // EditorMargin::Symbol
    KeyModifiers modifiers;  // key_modifier::kShift | kControl | kAlt | kMeta
};

Modifierarnas namn följer de portabla kortkommandosträngarna: kControl är Ctrl i Windows och Linux och Kommando (⌘) i macOS, kAlt är Alt / Option (⌥), kMeta är Windows-tangenten / Kontroll (⌃) i macOS. Klick i vikningskolumnen viker och klick på radnumren markerar; inget av dem publiceras.

Som standard växlar redigeraren också ett bokmärke på den klickade raden, som den alltid har gjort. En insticksmodul som ger klicket en egen betydelse — en felsökare som växlar en brytpunkt med ett vanligt klick och redigerar dess villkor med ett klick med modifierare — gör anspråk på det:

m_marginClaim = ctx.lineMarkers().claimMarginClick();   // MarginClickToken
m_clicks = ctx.events().subscribe<events::MarginClicked>(
    [this](const events::MarginClicked& e) { onMarginClick(e); });

Så länge minst ett anspråk lever — från vilken modul som helst — lämnar redigeraren klicket åt insticksmodulerna och publicerar bara händelsen; att förstöra den sista token återställer bokmärkesväxlingen. Händelsen publiceras oavsett om någon har ett anspråk eller inte, så en modul som bara bevakar klick behöver inget. Bokmärkeskommandona och deras kortkommandon påverkas inte. Anspråk räknas, de köas inte: två moduler som båda gör anspråk på klicket får det båda och får inte båda agera på det.

Python

Samma tjänst är ctx.line_markers för Python-insticksmoduler, och klicket är @ctx.events.on_margin_clicked.