package unisegimport// The bit masks used to extract boundary information returned by [Step].const (MaskLine = 3MaskWord = 4MaskSentence = 8)// The number of bits to shift the boundary information returned by [Step] to// obtain the monospace width of the grapheme cluster.constShiftWidth = 4// The bit positions by which boundary flags are shifted by the [Step] function.// These must correspond to the Mask constants.const ( shiftWord = 2 shiftSentence = 3// shiftwWidth is ShiftWidth above. No mask as these are always the remaining bits.)// The bit positions by which states are shifted by the [Step] function. These// values must ensure state values defined for each of the boundary algorithms// don't overlap (and that they all still fit in a single int). These must// correspond to the Mask constants.const ( shiftWordState = 4 shiftSentenceState = 9 shiftLineState = 13 shiftPropState = 21// No mask as these are always the remaining bits.)// The bit mask used to extract the state returned by the [Step] function, after// shifting. These values must correspond to the shift constants.const ( maskGraphemeState = 0xf maskWordState = 0x1f maskSentenceState = 0xf maskLineState = 0xff)// Step returns the first grapheme cluster (user-perceived character) found in// the given byte slice. It also returns information about the boundary between// that grapheme cluster and the one following it as well as the monospace width// of the grapheme cluster. There are three types of boundary information: word// boundaries, sentence boundaries, and line breaks. This function is therefore// a combination of [FirstGraphemeCluster], [FirstWord], [FirstSentence], and// [FirstLineSegment].//// The "boundaries" return value can be evaluated as follows://// - boundaries&MaskWord != 0: The boundary is a word boundary.// - boundaries&MaskWord == 0: The boundary is not a word boundary.// - boundaries&MaskSentence != 0: The boundary is a sentence boundary.// - boundaries&MaskSentence == 0: The boundary is not a sentence boundary.// - boundaries&MaskLine == LineDontBreak: You must not break the line at the// boundary.// - boundaries&MaskLine == LineMustBreak: You must break the line at the// boundary.// - boundaries&MaskLine == LineCanBreak: You may or may not break the line at// the boundary.// - boundaries >> ShiftWidth: The width of the grapheme cluster for most// monospace fonts where a value of 1 represents one character cell.//// This function can be called continuously to extract all grapheme clusters// from a byte slice, as illustrated in the examples below.//// If you don't know which state to pass, for example when calling the function// for the first time, you must pass -1. For consecutive calls, pass the state// and rest slice returned by the previous call.//// The "rest" slice is the sub-slice of the original byte slice "b" starting// after the last byte of the identified grapheme cluster. If the length of the// "rest" slice is 0, the entire byte slice "b" has been processed. The// "cluster" byte slice is the sub-slice of the input slice containing the// first identified grapheme cluster.//// Given an empty byte slice "b", the function returns nil values.//// While slightly less convenient than using the Graphemes class, this function// has much better performance and makes no allocations. It lends itself well to// large byte slices.//// Note that in accordance with [UAX #14 LB3], the final segment will end with// a mandatory line break (boundaries&MaskLine == LineMustBreak). You can choose// to ignore this by checking if the length of the "rest" slice is 0 and calling// [HasTrailingLineBreak] or [HasTrailingLineBreakInString] on the last rune.//// [UAX #14 LB3]: https://www.unicode.org/reports/tr14/#Algorithmfunc ( []byte, int) (, []byte, int, int) {// An empty byte slice returns nothing.iflen() == 0 {return }// Extract the first rune. , := utf8.DecodeRune()iflen() <= { // If we're already past the end, there is nothing else to parse.varintif < 0 { = property(graphemeCodePoints, ) } else { = >> shiftPropState }return , nil, LineMustBreak | (1 << shiftWord) | (1 << shiftSentence) | (runeWidth(, ) << ShiftWidth), grAny | (wbAny << shiftWordState) | (sbAny << shiftSentenceState) | (lbAny << shiftLineState) | ( << shiftPropState) }// If we don't know the state, determine it now.var , , , , int := [:]if < 0 { , , _ = transitionGraphemeState(, ) , _ = transitionWordBreakState(, , , "") , _ = transitionSentenceBreakState(, , , "") , _ = transitionLineBreakState(, , , "") } else { = & maskGraphemeState = ( >> shiftWordState) & maskWordState = ( >> shiftSentenceState) & maskSentenceState = ( >> shiftLineState) & maskLineState = >> shiftPropState }// Transition until we find a grapheme cluster boundary. := runeWidth(, )for {var ( , , bool , int ) , := utf8.DecodeRune() = [+:] , , = transitionGraphemeState(, ) , = transitionWordBreakState(, , , "") , = transitionSentenceBreakState(, , , "") , = transitionLineBreakState(, , , "")if { := | ( << ShiftWidth)if { |= 1 << shiftWord }if { |= 1 << shiftSentence }return [:], [:], , | ( << shiftWordState) | ( << shiftSentenceState) | ( << shiftLineState) | ( << shiftPropState) }if == vs16 { = 2 } elseif != prExtendedPictographic && != prRegionalIndicator && != prL { += runeWidth(, ) } elseif == prExtendedPictographic {if == vs15 { = 1 } else { = 2 } } += iflen() <= {return , nil, LineMustBreak | (1 << shiftWord) | (1 << shiftSentence) | ( << ShiftWidth), grAny | (wbAny << shiftWordState) | (sbAny << shiftSentenceState) | (lbAny << shiftLineState) | ( << shiftPropState) } }}// StepString is like [Step] but its input and outputs are strings.func ( string, int) (, string, int, int) {// An empty byte slice returns nothing.iflen() == 0 {return }// Extract the first rune. , := utf8.DecodeRuneInString()iflen() <= { // If we're already past the end, there is nothing else to parse. := property(graphemeCodePoints, )return , "", LineMustBreak | (1 << shiftWord) | (1 << shiftSentence) | (runeWidth(, ) << ShiftWidth), grAny | (wbAny << shiftWordState) | (sbAny << shiftSentenceState) | (lbAny << shiftLineState) }// If we don't know the state, determine it now.var , , , , int := [:]if < 0 { , , _ = transitionGraphemeState(, ) , _ = transitionWordBreakState(, , nil, ) , _ = transitionSentenceBreakState(, , nil, ) , _ = transitionLineBreakState(, , nil, ) } else { = & maskGraphemeState = ( >> shiftWordState) & maskWordState = ( >> shiftSentenceState) & maskSentenceState = ( >> shiftLineState) & maskLineState = >> shiftPropState }// Transition until we find a grapheme cluster boundary. := runeWidth(, )for {var ( , , bool , int ) , := utf8.DecodeRuneInString() = [+:] , , = transitionGraphemeState(, ) , = transitionWordBreakState(, , nil, ) , = transitionSentenceBreakState(, , nil, ) , = transitionLineBreakState(, , nil, )if { := | ( << ShiftWidth)if { |= 1 << shiftWord }if { |= 1 << shiftSentence }return [:], [:], , | ( << shiftWordState) | ( << shiftSentenceState) | ( << shiftLineState) | ( << shiftPropState) }if == vs16 { = 2 } elseif != prExtendedPictographic && != prRegionalIndicator && != prL { += runeWidth(, ) } elseif == prExtendedPictographic {if == vs15 { = 1 } else { = 2 } } += iflen() <= {return , "", LineMustBreak | (1 << shiftWord) | (1 << shiftSentence) | ( << ShiftWidth), grAny | (wbAny << shiftWordState) | (sbAny << shiftSentenceState) | (lbAny << shiftLineState) | ( << shiftPropState) } }}
The pages are generated with Goldsv0.6.7. (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 @Go100and1 (reachable from the left QR code) to get the latest news of Golds.