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.