Przejdź do treści

Znaczniki wierszy i kliknięcie marginesu

Znacznik wiersza to glif w kolumnie zakładek edytora i/lub zabarwienie całego wiersza, przypięte do wiersza dokumentu i przenoszone razem z nim, gdy użytkownik edytuje. Punkty przerwania (pełne, puste, warunkowe), strzałka bieżącej instrukcji i paski zmian z kontroli wersji to wszystko znaczniki wierszy. Dostęp do usługi daje IPluginContext::lineMarkers() (PluginApi/ILineMarkerService.h).

Wtyczka opisuje, jak wygląda dany rodzaj znacznika i gdzie stoją znaczniki tego rodzaju; rysowanie, margines i miejsca na znaczniki w silniku edycji należą do edytora. Przez granicę nie przechodzi żaden rozmiar w pikselach, numer silnika ani typ Qt.

Rodzaje

Każdy rodzaj znacznika zarejestruj raz, w initialize(), i zachowaj 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 */ }
  • Kolory to identyfikatory kategorii motywu, które zarejestrowałeś sam przez ctx.themePalette(), więc znaczniki podążają za aktywnym motywem, a użytkownik może je przekolorować w Edytorze motywów; edytor stosuje je ponownie przy każdej zmianie motywu.
  • Glify to mały, stały zestaw, który każdy host potrafi narysować na wąskim marginesie w dowolnym powiększeniu: Circle (punkt przerwania), CircleHollow (wyłączony lub niepotwierdzony punkt przerwania), CircleBar (warunkowy punkt przerwania), Arrow (bieżąca instrukcja), Strip (zmieniony wiersz). None tworzy rodzaj z samym zabarwieniem.
  • zOrder decyduje, który znacznik jest rysowany na wierzchu, gdy kilka dzieli jeden wiersz (strzałka bieżącej instrukcji nad punktem przerwania, punkt przerwania nad paskiem zmian); przy równych wartościach na wierzchu jest rodzaj zarejestrowany później. Kolejność obowiązuje także między wtyczkami.
  • Rejestracja może się nie udać. Silnik edycji ma małą, stałą liczbę miejsc na znaczniki, wspólną dla wszystkich wtyczek — dziewięć; rodzaj z glifem i zabarwieniem zajmuje dwa. Gdy się skończą, token wraca pusty, a log mówi dlaczego. Rejestruj potrzebne rodzaje raz i zachowuj je; nigdy nie rejestruj rodzaju per dokument ani per znacznik.
  • Zniszczenie tokenu usuwa wszystkie znaczniki tego rodzaju ze wszystkich dokumentów i wycofuje rodzaj.

Znaczniki

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 zwraca 0 — i niczego nie stawia — gdy rodzaj nie żyje, dokument nie jest otwarty albo wiersz leży poza dokumentem. Wiersz nie jest przycinany: punkt przerwania w złym wierszu jest gorszy niż żaden. Uchwyt to zwykła wartość, a nie token; trzymaj ich tyle, ile potrzebujesz.

Co dzieje się ze znacznikiem potem:

  • Edycje przesuwają go razem z jego wierszem: wiersze wstawione lub usunięte powyżej go przesuwają, pisanie w tym wierszu go nie rusza — tak punkt przerwania podąża za kodem.
  • Jego wiersz zostaje usunięty (jest częścią usuniętego zakresu): znacznik przeżywa i dołącza do wiersza, w którym zaczynał się usunięty zakres. Dwa znaczniki mogą w ten sposób trafić do jednego wiersza.
  • Cały bufor zostaje zastąpiony (ponowne wczytanie po zmianie z zewnątrz, IEditorService::setText): każdy znacznik zostaje na tym samym numerze wiersza; ten, którego wiersz już nie istnieje, jest usuwany. Edytor nie porównuje starego i nowego tekstu.
  • Dokument zostaje zamknięty: jego znaczniki są usuwane, a ich uchwyty przestają działać. Edytor nigdy nie zapisuje znaczników — wtyczka, która zachowuje oznaczenia po zamknięciu (punkty przerwania), trzyma je we własnym modelu i dodaje ponownie przy DocumentOpened (ponownie otwarty plik to nowy DocumentId).

Znaczniki należą do dokumentu: postawione raz, pokazują się w każdym panelu podzielonego dokumentu. Minimapa, jej podgląd po najechaniu i widok porównania nigdy ich nie pokazują. Zakładki nie są znacznikami tej usługi; zachowują własne wywołania IEditorService.

Kliknięcie marginesu

Każde kliknięcie lewym przyciskiem w kolumnie zakładek edytora jest publikowane jako 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
};

Nazwy modyfikatorów odpowiadają przenośnym zapisom skrótów: kControl to Ctrl w Windows i Linuksie, a Command (⌘) w macOS, kAlt to Alt / Option (⌥), kMeta to klawisz Windows / Control (⌃) w macOS. Kliknięcia w kolumnie zwijania zwijają, a kliknięcia w numerach wierszy zaznaczają; żadne z nich nie jest publikowane.

Domyślnie edytor dodatkowo przełącza zakładkę w klikniętym wierszu, jak zawsze. Wtyczka, która nadaje kliknięciu własne znaczenie — debugger przełączający punkt przerwania zwykłym kliknięciem i edytujący jego warunek kliknięciem z modyfikatorem — przejmuje je:

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

Dopóki żyje choć jedno przejęcie — dowolnej wtyczki — edytor zostawia kliknięcie wtyczkom i tylko publikuje zdarzenie; zniszczenie ostatniego tokenu przywraca przełączanie zakładki. Zdarzenie jest publikowane niezależnie od tego, czy ktoś trzyma przejęcie, więc wtyczka, która tylko obserwuje kliknięcia, żadnego nie potrzebuje. Polecenia zakładek i ich skróty działają bez zmian. Przejęcia są liczone, a nie kolejkowane: dwie wtyczki, które obie przejmą kliknięcie, obie je dostaną i nie mogą obie na nie reagować.

Python

Ta sama usługa to ctx.line_markers dla wtyczek w Pythonie, a kliknięcie to @ctx.events.on_margin_clicked.