// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:generate go run gen.go gen_trieval.go gen_ranges.go

// Package bidi contains functionality for bidirectional text support. // // See https://www.unicode.org/reports/tr9. // // NOTE: UNDER CONSTRUCTION. This API may change in backwards incompatible ways // and without notice.
package bidi // import "golang.org/x/text/unicode/bidi" // TODO // - Transformer for reordering? // - Transformer (validator, really) for Bidi Rule. import ( ) // This API tries to avoid dealing with embedding levels for now. Under the hood // these will be computed, but the question is to which extent the user should // know they exist. We should at some point allow the user to specify an // embedding hierarchy, though. // A Direction indicates the overall flow of text. type Direction int const ( // 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 // RightToLeft indicates the text contains no left-to-right characters and // that either there are some right-to-left characters or the option // DefaultDirection(RightToLeft) was passed. RightToLeft // 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 { defaultDirection Direction } // An Option is an option for Bidi processing. type Option func(*options) // ICU allows the user to define embedding levels. This may be used, for example, // to use hierarchical structure of markup languages to define embeddings. // The following option may be a way to expose this functionality in this API. // // LevelFunc sets a function that associates nesting levels with the given text. // // The levels function will be called with monotonically increasing values for p. // func LevelFunc(levels func(p int) int) Option { // panic("unimplemented") // } // DefaultDirection sets the default direction for a Paragraph. The direction is // overridden if the text contains directional characters. func ( Direction) Option { return func( *options) { .defaultDirection = } } // A Paragraph holds a single Paragraph for Bidi processing. type Paragraph struct { p []byte o Ordering opts []Option types []Class pairTypes []bracketType pairValues []rune runes []rune options options } // Initialize the p.pairTypes, p.pairValues and p.types from the input previously // set by p.SetBytes() or p.SetString(). Also limit the input up to (and including) a paragraph // separator (bidi class B). // // The function p.Order() needs these values to be set, so this preparation could be postponed. // But since the SetBytes and SetStrings functions return the length of the input up to the paragraph // separator, the whole input needs to be processed anyway and should not be done twice. // // The function has the same return values as SetBytes() / SetString() func ( *Paragraph) () ( int, error) { .runes = bytes.Runes(.p) := 0 // clear slices from previous SetString or SetBytes .pairTypes = nil .pairValues = nil .types = nil for , := range .runes { , := LookupRune() += := .Class() if == B { return , nil } .types = append(.types, ) if .IsOpeningBracket() { .pairTypes = append(.pairTypes, bpOpen) .pairValues = append(.pairValues, ) } else if .IsBracket() { // this must be a closing bracket, // since IsOpeningBracket is not true .pairTypes = append(.pairTypes, bpClose) .pairValues = append(.pairValues, ) } else { .pairTypes = append(.pairTypes, bpNone) .pairValues = append(.pairValues, 0) } } return , nil } // SetBytes configures p for the given paragraph text. It replaces text // previously set by SetBytes or SetString. If b contains a paragraph separator // it will only process the first paragraph and report the number of bytes // consumed from b including this separator. Error may be non-nil if options are // given. func ( *Paragraph) ( []byte, ...Option) ( int, error) { .p = .opts = return .prepareInput() } // SetString configures s for the given paragraph text. It replaces text // previously set by SetBytes or SetString. If s contains a paragraph separator // it will only process the first paragraph and report the number of bytes // consumed from s including this separator. Error may be non-nil if options are // given. func ( *Paragraph) ( string, ...Option) ( int, error) { .p = []byte() .opts = return .prepareInput() } // IsLeftToRight reports whether the principle direction of rendering for this // paragraphs is left-to-right. If this returns false, the principle direction // of rendering is right-to-left. func ( *Paragraph) () bool { return .Direction() == LeftToRight } // Direction returns the direction of the text of this paragraph. // // The direction may be LeftToRight, RightToLeft, Mixed, or Neutral. func ( *Paragraph) () Direction { return .o.Direction() } // TODO: what happens if the position is > len(input)? This should return an error. // RunAt reports the Run at the given position of the input text. // // This method can be used for computing line breaks on paragraphs. func ( *Paragraph) ( int) Run { := 0 := 0 for , := range .o.runes { += len() if < { = } } return .o.Run() } func calculateOrdering( []level, []rune) Ordering { var Direction := Neutral := 0 := Ordering{} // lvl = 0,2,4,...: left to right // lvl = 1,3,5,...: right to left for , := range { if %2 == 0 { = LeftToRight } else { = RightToLeft } if != { if > 0 { .runes = append(.runes, [:]) .directions = append(.directions, ) .startpos = append(.startpos, ) } = = } } .runes = append(.runes, [:]) .directions = append(.directions, ) .startpos = append(.startpos, ) return } // Order computes the visual ordering of all the runs in a Paragraph. func ( *Paragraph) () (Ordering, error) { if len(.types) == 0 { return Ordering{}, nil } for , := range .opts { (&.options) } := level(-1) if .options.defaultDirection == RightToLeft { = 1 } , := newParagraph(.types, .pairTypes, .pairValues, ) if != nil { return Ordering{}, } := .getLevels([]int{len(.types)}) .o = calculateOrdering(, .runes) return .o, nil } // Line computes the visual ordering of runs for a single line starting and // ending at the given positions in the original text. func ( *Paragraph) (, int) (Ordering, error) { := .types[:] , := newParagraph(, .pairTypes[:], .pairValues[:], -1) if != nil { return Ordering{}, } := .getLevels([]int{len()}) := calculateOrdering(, .runes[:]) return , nil } // An Ordering holds the computed visual order of runs of a Paragraph. Calling // SetBytes or SetString on the originating Paragraph invalidates an Ordering. // The methods of an Ordering should only be called by one goroutine at a time. type Ordering struct { runes [][]rune directions []Direction startpos []int } // Direction reports the directionality of the runs. // // The direction may be LeftToRight, RightToLeft, Mixed, or Neutral. func ( *Ordering) () Direction { return .directions[0] } // NumRuns returns the number of runs. func ( *Ordering) () int { return len(.runes) } // Run returns the ith run within the ordering. func ( *Ordering) ( int) Run { := Run{ runes: .runes[], direction: .directions[], startpos: .startpos[], } return } // TODO: perhaps with options. // // Reorder creates a reader that reads the runes in visual order per character. // // Modifiers remain after the runes they modify. // func (l *Runs) Reorder() io.Reader { // panic("unimplemented") // } // A Run is a continuous sequence of characters of a single direction. type Run struct { runes []rune direction Direction startpos int } // String returns the text of the run in its original order. func ( *Run) () string { return string(.runes) } // Bytes returns the text of the run in its original order. func ( *Run) () []byte { return []byte(.String()) } // TODO: methods for // - Display order // - headers and footers // - bracket replacement. // Direction reports the direction of the run. func ( *Run) () Direction { return .direction } // Pos returns the position of the Run within the text passed to SetBytes or SetString of the // originating Paragraph value. func ( *Run) () (, int) { return .startpos, .startpos + len(.runes) - 1 } // AppendReverse reverses the order of characters of in, appends them to out, // and returns the result. Modifiers will still follow the runes they modify. // Brackets are replaced with their counterparts. func (, []byte) []byte { := make([]byte, len()+len()) copy(, ) := bytes.Runes() for , := range { , := LookupRune() if .IsBracket() { [] = .reverseBracket() } } for , := 0, len()-1; < ; , = +1, -1 { [], [] = [], [] } copy([len():], string()) return } // ReverseString reverses the order of characters in s and returns a new string. // Modifiers will still follow the runes they modify. Brackets are replaced with // their counterparts. func ( string) string { := []rune() := len() := make([]rune, ) for , := range { , := LookupRune() if .IsBracket() { [--1] = .reverseBracket() } else { [--1] = } } return string() }