deriveTextPresentationPolicy function

ResolvedTextPresentationPolicy deriveTextPresentationPolicy({
  1. WidthMeasurements measurements = const WidthMeasurements.empty(),
  2. Map<String, String> environment = const <String, String>{},
})

Folds probe measurements and environment overrides into the one derived ResolvedTextPresentationPolicy every geometry consumer shares (RFC 0019 §6.2).

Per axis: an explicit FLEURY_* override wins outright; else agreeing probe measurements decide; else the spec default stands and the axis is recorded as WidthDecisionSource.spec — the unknown state, which conservative consumers treat as "keep the safety net engaged".

Lowering follows RFC 0019 §6.4's two-step authorization: the observation must be summed for every probed sequence (the measured inequality componentSum ≤ advance ≤ componentSum + zwjCount), AND the width policy must predict every measured component — otherwise splitting would trade the joiner error for a component near-miss, so the action stays ClusterLowering.preserve and the pin covers the sequence. A confidently joined observation also records probe provenance: "this terminal clusters" is evidence, not a default.

Implementation

ResolvedTextPresentationPolicy deriveTextPresentationPolicy({
  WidthMeasurements measurements = const WidthMeasurements.empty(),
  Map<String, String> environment = const <String, String>{},
}) {
  final decisions = <WidthAxis, WidthDecisionSource>{};

  CellWidth? agreement(WidthProbeClass probeClass) {
    final widths = measurements.widthsIn(probeClass);
    if (widths.isEmpty || widths.any((w) => w == null)) return null;
    if (widths.every((w) => w == 1)) return CellWidth.one;
    if (widths.every((w) => w! >= 2)) return CellWidth.two;
    return null; // Disagreement — unknown, never a guess.
  }

  CellWidth resolveAxis(
    WidthAxis axis,
    CellWidth? override,
    WidthProbeClass probeClass,
    CellWidth specDefault,
  ) {
    if (override != null) {
      decisions[axis] = WidthDecisionSource.environment;
      return override;
    }
    final measured = agreement(probeClass);
    if (measured != null) {
      decisions[axis] = WidthDecisionSource.probe;
      return measured;
    }
    return specDefault;
  }

  final ambiguousOverride = switch (detectAmbiguousCharWidthFromEnvironment(
    environment,
  )) {
    AmbiguousCharWidth.narrow => CellWidth.one,
    AmbiguousCharWidth.wide => CellWidth.two,
    null => null,
  };
  final widths = CellWidthPolicy(
    ambiguous: resolveAxis(
      WidthAxis.ambiguous,
      ambiguousOverride,
      WidthProbeClass.ambiguous,
      CellWidth.one,
    ),
    emojiPresentation: resolveAxis(
      WidthAxis.emojiPresentation,
      detectEmojiWidthFromEnvironment(environment),
      WidthProbeClass.emojiPresentation,
      CellWidth.two,
    ),
    emojiVariationSequence: resolveAxis(
      WidthAxis.emojiVariationSequence,
      detectVs16WidthFromEnvironment(environment),
      WidthProbeClass.emojiVariationSequence,
      CellWidth.two,
    ),
  );

  var lowering = ClusterLowering.preserve;
  final loweringOverride = detectClusterModeFromEnvironment(environment);
  if (loweringOverride != null) {
    lowering = loweringOverride;
    decisions[WidthAxis.lowering] = WidthDecisionSource.environment;
  } else {
    final derived = _deriveLowering(measurements, widths);
    if (derived != null) {
      lowering = derived;
      decisions[WidthAxis.lowering] = WidthDecisionSource.probe;
    }
  }

  return ResolvedTextPresentationPolicy(
    policy: TextPresentationPolicy(widths: widths, lowering: lowering),
    decisions: Map.unmodifiable(decisions),
  );
}