Diagnostic.h header

#include <ew/app/Diagnostic.h>

Namespace ew::app

Diagnostic struct

struct ew::app::Diagnostic

One finding, whatever produced it.

The single record every validator emits and every surface renders. Before this there were three: a timeline conflict had a description and some event ids and no rule, no severity and no way to suppress it; a branch problem had a stable token and no severity; a style diagnostic had a rule id, a severity concept, fixes and suppression at two granularities. So whether a finding could be ranked, silenced or explained depended on which validator happened to notice it, and the Conflicts dock could only ever show one of the three.

The model is a compiler diagnostic: a stable code, a severity, a primary subject, a message, and optional notes and fix-its – rendered differently by an editor squiggle, a dock and a build log, but the same record underneath.

Members

QString ew::app::diagnostics::Diagnostic::ruleId

Which rule produced it. Stable across releases; it is what a suppression names and what a build log reports.

Severity ew::app::diagnostics::Diagnostic::severity =

How much it matters. Taken from the rule unless the project overrode it.

std::vector<ew::core::foundation::ContentId> ew::app::diagnostics::Diagnostic::subjects

The objects it is about, most relevant first.

A vector because a finding is frequently about a RELATIONSHIP: "is in two places at once" names two events, and reporting it against either one alone would send the author to fix half of it. The first is the one a jump-to-fix opens.

DiagnosticAnchor ew::app::diagnostics::Diagnostic::anchor

Where inside subjects.front() it sits, when it sits anywhere in particular.

QString ew::app::diagnostics::Diagnostic::message

One sentence, in the author's language, naming what is wrong and what it is wrong about.

QString ew::app::diagnostics::Diagnostic::explanation

The longer form: why this is a problem, and what would resolve it. May be empty, and a surface with no room for it shows message alone.

std::vector<DiagnosticFix> ew::app::diagnostics::Diagnostic::fixes

The corrections offered, best first, each applicable by plain substitution.

Empty when the finding has no mechanical fix – a passive construction or a character in two places at once needs a person, and offering a wrong one-click edit is worse than offering none.

QString ew::app::diagnostics::Diagnostic::fingerprint

The identity a suppression is keyed on. See fingerprintOf.

Stored rather than recomputed on demand so every consumer agrees about it without every consumer having to know how it is built.

bool operator==(const Diagnostic &, const Diagnostic &)=default

Diagnostics compare equal when every field matches.

DiagnosticAnchor struct

struct ew::app::DiagnosticAnchor

Where inside its subject a finding sits.

The stable half and the presentational half are deliberately separate fields. part and quoted identify the thing being complained about and go into the fingerprint; offset and length say where to draw it and do not. Fingerprinting an offset would mean a suppression died the moment the author typed a word higher up the document – which is the one thing a suppression must survive.

Members

AnchorKind ew::app::diagnostics::DiagnosticAnchor::kind =

Which of the three shapes this anchor is.

QString ew::app::diagnostics::DiagnosticAnchor::part

The named part: a branch node id, a field key, a cell reference. Empty for the other kinds.

QString ew::app::diagnostics::DiagnosticAnchor::quoted

The flagged words exactly as they appear. Identity for a TextSpan, and what re-anchoring matches on when the text around it has moved.

qsizetype ew::app::diagnostics::DiagnosticAnchor::offset = 0

Code-unit offset of the flagged span. Presentation only – see the class note.

qsizetype ew::app::diagnostics::DiagnosticAnchor::length = 0

Length of the flagged span in code units. Presentation only – see the class note.

bool operator==(const DiagnosticAnchor &, const DiagnosticAnchor &)=default

Anchors compare equal when every field matches.

DiagnosticFix struct

struct ew::app::DiagnosticFix

One correction a rule offers: replace the code-unit span [offset, offset + length) of the analyzed text with replacement. Applying it is a plain substitution, so a caller needs no knowledge of what the fix means.

The span is the FIX'S OWN, not necessarily the finding's flagged span, because a correction that reads as clean prose usually has to touch a little more than the words at fault: deleting a filler word must also close the gap it leaves.

It carries no menu label – kind and subject are the parts a caller needs to write one, so the wording (and its translation) stays in the UI where it belongs.

Members

FixKind ew::app::diagnostics::DiagnosticFix::kind =

Whether the fix cuts the words or swaps them.

QString ew::app::diagnostics::DiagnosticFix::subject

The flagged words this fix acts on, exactly as they appear – the phrase to name in a menu item. It is the rule's own span, never the widened one, so it reads as what the writer sees.

qsizetype ew::app::diagnostics::DiagnosticFix::offset = 0

Code-unit offset of the span this fix rewrites, within the analyzed text.

qsizetype ew::app::diagnostics::DiagnosticFix::length = 0

Length in code units of the span this fix rewrites; may be zero (a pure insertion).

QString ew::app::diagnostics::DiagnosticFix::replacement

The text that replaces the span. For a Replace fix it is the new wording; for a Delete fix it holds only what repairs the seam – a single space, a re-capitalised letter, or nothing.

bool operator==(const DiagnosticFix &, const DiagnosticFix &)=default

Fixes compare equal when every field matches.

Enumerations

enum class AnchorKind { Whole, TextSpan, Part }

What part of a subject a finding is about.

enum class FixKind { Delete, Replace }

What a fix does to the text it names, so a UI can phrase it in the reader's language.

enum class Severity { Error, Warning, Suggestion }

How much a finding matters.

Carried by the finding rather than inferred from its rule, because a project may raise or lower a rule for itself: "every Item has a price" is an error in a shipping game and a suggestion in a draft, and the same rule has to be able to say both.

Functions

std::optional< AnchorKind > ew::app::diagnostics::anchorKindFromToken(QStringView token)

Parses an anchor kind from its serialization token; std::nullopt if unrecognized or absent.

QString ew::app::diagnostics::applyFix(const QString &text, const DiagnosticFix &fix)

text with fix applied.

The fix's span is clamped to text first, so a fix built against text that has since changed can only ever rewrite less than it meant to – never read out of bounds.

std::vector< Diagnostic > ew::app::diagnostics::diagnosticsCovering(const std::vector< Diagnostic > &diagnostics, qsizetype offset)

Those of diagnostics whose text span covers offset, narrowest first.

The span is half-open, so the offset just past a flagged word belongs to what follows rather than to it. Several findings can share one point – an adverb inside a passive clause inside an over-long sentence – and the narrowest is the one a reader pointing there meant.

QString ew::app::diagnostics::fingerprintOf(const QString &ruleId, std::vector< ew::core::foundation::ContentId > subjects, const DiagnosticAnchor &anchor)

The stable identity of the finding ruleId + subjects + anchor describes.

What it must survive is an unrelated edit to the same object. A writer who silences "very" in chapter nine has not silenced it for the sentence it happens to sit in – they have silenced it for that word in that document, and adding a paragraph above it must not bring it back. So the hash is taken over the rule, the subjects, and the anchor's stable half only: its kind, its part, and its quoted text. The offset is deliberately excluded.

Subjects are sorted before hashing, so "A and B are in two places at once" fingerprints the same however the pair was ordered when it was found.

std::optional< Severity > ew::app::diagnostics::severityFromToken(QStringView token)

Parses a severity from its serialization token; std::nullopt if unrecognized or absent.

QString ew::app::diagnostics::toToken(Severity severity)

Returns the stable serialization token for severity ("error", "warning", "suggestion").

QString ew::app::diagnostics::toToken(AnchorKind kind)

Returns the stable serialization token for kind.

Diagnostic ew::app::diagnostics::withFingerprint(Diagnostic diagnostic)

diagnostic with its Diagnostic::fingerprint filled in from its own other fields.

Every validator ends by calling this, so no validator has to know how a fingerprint is built and none of them can build one differently.