Source File
bidi.go
Belonging Package
vendor/golang.org/x/text/unicode/bidi
// 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()
}
The pages are generated with Golds v0.7.0-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |