From a74a5b30de2f8effbdb3f4a8b1b9c1813aab9af0 Mon Sep 17 00:00:00 2001 From: Patrick Gundlach Date: Sun, 5 Apr 2026 11:09:32 +0200 Subject: [PATCH] unicode/bidi: fix Direction zero value to Neutral The Direction constants had LeftToRight as iota (value 0), making it indistinguishable from the zero value of an unset Direction field. This caused DefaultDirection(LeftToRight) to be silently ignored in Paragraph.Order(), as the code could not distinguish between "user explicitly set LTR" and "no direction was set." Reorder the Direction constants so that Neutral is the zero value, and add an explicit case for LeftToRight in Order() to set the paragraph embedding level to 0. Fixes golang/go#71809 --- unicode/bidi/bidi.go | 15 +++++++++------ unicode/bidi/bidi_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/unicode/bidi/bidi.go b/unicode/bidi/bidi.go index fd057601..701aac46 100644 --- a/unicode/bidi/bidi.go +++ b/unicode/bidi/bidi.go @@ -29,10 +29,14 @@ import ( type Direction int const ( + // Neutral means that text contains no left-to-right and right-to-left + // characters and that no default direction has been set. + Neutral Direction = iota + // LeftToRight indicates the text contains no right-to-left characters and // that either there are some left-to-right characters or the option // DefaultDirection(LeftToRight) was passed. - LeftToRight Direction = iota + LeftToRight // RightToLeft indicates the text contains no left-to-right characters and // that either there are some right-to-left characters or the option @@ -42,10 +46,6 @@ const ( // Mixed indicates text contains both left-to-right and right-to-left // characters. Mixed - - // Neutral means that text contains no left-to-right and right-to-left - // characters and that no default direction has been set. - Neutral ) type options struct { @@ -219,8 +219,11 @@ func (p *Paragraph) Order() (Ordering, error) { fn(&p.options) } lvl := level(-1) - if p.options.defaultDirection == RightToLeft { + switch p.options.defaultDirection { + case RightToLeft: lvl = 1 + case LeftToRight: + lvl = 0 } para, err := newParagraph(p.types, p.pairTypes, p.pairValues, lvl) if err != nil { diff --git a/unicode/bidi/bidi_test.go b/unicode/bidi/bidi_test.go index 88572f56..c5b79bda 100644 --- a/unicode/bidi/bidi_test.go +++ b/unicode/bidi/bidi_test.go @@ -254,6 +254,38 @@ func TestMixedSimple(t *testing.T) { } } +func TestDirectionLTR(t *testing.T) { + str := "ع a" + p := Paragraph{} + p.SetString(str, DefaultDirection(LeftToRight)) + order, err := p.Order() + if err != nil { + log.Fatal(err) + } + + expectedRuns := []runInformation{ + {"ع", RightToLeft, 0, 0}, + {" a", LeftToRight, 1, 2}, + } + + if nr, want := order.NumRuns(), len(expectedRuns); nr != want { + t.Errorf("order.NumRuns() = %d; want %d", nr, want) + } + + for i, want := range expectedRuns { + r := order.Run(i) + if got := r.String(); got != want.str { + t.Errorf("Run(%d) = %q; want %q", i, got, want.str) + } + if s, e := r.Pos(); s != want.start || e != want.end { + t.Errorf("Run(%d).start = %d, .end = %d; want start = %d, end = %d", i, s, e, want.start, want.end) + } + if d := r.Direction(); d != want.dir { + t.Errorf("Run(%d).Direction = %d; want %d", i, d, want.dir) + } + } +} + func TestDefaultDirection(t *testing.T) { str := "+" p := Paragraph{}