Marcas de linha e clique na margem¶
Uma marca de linha é um glifo na coluna de marcadores do editor e/ou um tom
sobre a linha inteira, preso a uma linha de um documento e levado junto com ela
enquanto o usuário edita. Pontos de interrupção (cheios, vazados,
condicionais), uma seta da instrução atual e as faixas de alterações do
controle de versão são todos marcas de linha. Acesse o serviço por
IPluginContext::lineMarkers() (PluginApi/ILineMarkerService.h).
O plugin descreve como é um tipo de marca e onde ficam as marcas desse tipo; o desenho, a margem e os espaços de marcas do mecanismo de edição pertencem ao editor. Nenhum tamanho em pixels, número do mecanismo ou tipo do Qt atravessa a fronteira.
Tipos¶
Registre cada tipo de marca uma única vez, em initialize(), e guarde o 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 */ }
- As cores são ids de categorias de tema que você mesmo registrou com
ctx.themePalette(), então as marcas acompanham o tema ativo e o usuário pode mudar a cor delas no Editor de temas; o editor as reaplica a cada troca de tema. - Os glifos são um conjunto pequeno e fixo que qualquer host consegue
desenhar numa margem estreita com qualquer zoom:
Circle(um ponto de interrupção),CircleHollow(um ponto de interrupção desativado ou não verificado),CircleBar(um ponto de interrupção condicional),Arrow(a instrução atual),Strip(uma linha alterada).Nonecria um tipo que só aplica o tom. - zOrder decide qual marca é desenhada por cima quando várias dividem uma linha (a seta da instrução atual sobre um ponto de interrupção, um ponto de interrupção sobre uma faixa de alterações); com valores iguais, o tipo registrado depois fica por cima. A ordem vale também entre plugins.
- O registro pode falhar. O mecanismo de edição tem um número pequeno e fixo de espaços de marcas compartilhados por todos os plugins — nove; um tipo com glifo e tom usa dois. Quando eles acabam, o token volta vazio e o log diz por quê. Registre uma vez os tipos de que precisa e guarde-os; nunca registre um tipo por documento ou por marca.
- Destruir o token remove todas as marcas desse tipo de todos os documentos e aposenta o tipo.
Marcas¶
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 devolve 0 — e não posiciona nada — quando o tipo não está vivo, o
documento não está aberto ou a linha está fora do documento. A linha não é
limitada: um ponto de interrupção na linha errada é pior do que nenhum. Um
identificador é um valor simples, não um token; guarde quantos precisar.
O que acontece depois com uma marca:
- As edições a movem junto com a linha: linhas inseridas ou excluídas acima a deslocam, digitar na linha a mantém — é assim que um ponto de interrupção acompanha o código.
- A linha dela é excluída (faz parte de um intervalo removido): ela sobrevive e passa para a linha onde começava o intervalo excluído. Duas marcas podem acabar na mesma linha.
- O buffer inteiro é substituído (um recarregamento após uma mudança
externa,
IEditorService::setText): cada marca fica no mesmo número de linha; a que ficar sem linha é removida. O editor não compara o texto antigo com o novo. - O documento é fechado: as marcas dele são removidas e os identificadores
deixam de valer. O editor nunca guarda marcas — um plugin que mantém marcas
depois de um fechamento (pontos de interrupção) as guarda no próprio modelo e
as acrescenta de novo em
DocumentOpened(um arquivo reaberto é umDocumentIdnovo).
As marcas pertencem ao documento: posicionadas uma vez, aparecem em todos os
painéis de um documento dividido. O minimapa,
a pré-visualização dele ao passar o mouse e a visão de comparação nunca as
mostram. Os marcadores não são marcas deste serviço; eles mantêm as próprias
chamadas de IEditorService.
O clique na margem¶
Todo clique esquerdo na coluna de marcadores de um editor é publicado como
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
};
Os nomes dos modificadores seguem as cadeias portáteis de atalhos: kControl é
Ctrl no Windows e no Linux e Command (⌘) no macOS, kAlt é Alt / Option (⌥),
kMeta é a tecla Windows / Control (⌃) no macOS. Cliques na coluna de dobra
dobram e cliques nos números de linha selecionam; nenhum deles é publicado.
Por padrão, o editor também alterna um marcador na linha clicada, como sempre fez. Um plugin que dá ao clique um significado próprio — um depurador que alterna um ponto de interrupção com um clique simples e edita a condição dele com um clique com modificador — o reivindica:
m_marginClaim = ctx.lineMarkers().claimMarginClick(); // MarginClickToken
m_clicks = ctx.events().subscribe<events::MarginClicked>(
[this](const events::MarginClicked& e) { onMarginClick(e); });
Enquanto houver pelo menos uma reivindicação viva — de qualquer plugin —, o editor deixa o clique para os plugins e só publica o evento; destruir o último token traz de volta a alternância de marcadores. O evento é publicado haja ou não uma reivindicação, então um plugin que só observa cliques não precisa de nenhuma. Os comandos de marcadores e os atalhos deles não são afetados. As reivindicações são contadas, não enfileiradas: dois plugins que reivindicam o clique o recebem ambos e não devem ambos reagir a ele.
Python¶
O mesmo serviço é ctx.line_markers para
plugins Python, e o clique é
@ctx.events.on_margin_clicked.