1// Copyright 2013 The Flutter Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5part of engine; 6 7/// Various types of line breaks as defined by the Unicode spec. 8enum LineBreakType { 9 /// Indicates that a line break is possible but not mandatory. 10 opportunity, 11 12 /// Indicates that this is a hard line break that can't be skipped. 13 mandatory, 14 15 /// Indicates the end of the text (which is also considered a line break in 16 /// the Unicode spec). This is the same as [mandatory] but it's needed in our 17 /// implementation to distinguish between the universal [endOfText] and the 18 /// line break caused by "\n" at the end of the text. 19 endOfText, 20} 21 22class CharCode { 23 // New line characters. 24 static const int lf = 0x0A; 25 static const int bk1 = 0x0B; 26 static const int bk2 = 0x0C; 27 static const int cr = 0x0D; 28 static const int nl = 0x85; 29 30 // Space characters. 31 static const int tab = 0x09; 32 static const int space = 0x20; 33 34 static const int hyphen = 0x2D; 35} 36 37/// Acts as a tuple that encapsulates information about a line break. 38class LineBreakResult { 39 LineBreakResult(this.index, this.type); 40 41 final int index; 42 final LineBreakType type; 43} 44 45/// Finds the next line break in the given [text] starting from [index]. 46/// 47/// Useful resources: 48/// 49/// * http://www.unicode.org/reports/tr14/#Algorithm 50/// * https://www.unicode.org/Public/11.0.0/ucd/LineBreak.txt 51LineBreakResult nextLineBreak(String text, int index) { 52 // TODO(flutter_web): https://github.com/flutter/flutter/issues/33523 53 // This is a hacky/temporary/throw-away implementation to enable us to move fast 54 // with the rest of the line-splitting project. 55 56 // Always break at the end of text. 57 // LB3: ÷ eot 58 while (index++ < text.length) { 59 final int curr = index < text.length ? text.codeUnitAt(index) : null; 60 final int prev = index > 0 ? text.codeUnitAt(index - 1) : null; 61 62 // Always break after hard line breaks. 63 // LB4: BK ! 64 if (prev == CharCode.bk1 || prev == CharCode.bk2) { 65 return LineBreakResult(index, LineBreakType.mandatory); 66 } 67 68 // Treat CR followed by LF, as well as CR, LF, and NL as hard line breaks. 69 // LB5: CR × LF 70 // CR ! 71 // LF ! 72 // NL ! 73 if (prev == CharCode.cr && curr == CharCode.lf) { 74 continue; 75 } 76 if (prev == CharCode.cr || prev == CharCode.lf || prev == CharCode.nl) { 77 return LineBreakResult(index, LineBreakType.mandatory); 78 } 79 80 // Do not break before hard line breaks. 81 // LB6: × ( BK | CR | LF | NL ) 82 if (curr == CharCode.bk1 || 83 curr == CharCode.bk2 || 84 curr == CharCode.cr || 85 curr == CharCode.lf || 86 curr == CharCode.nl) { 87 continue; 88 } 89 90 if (index >= text.length) { 91 return LineBreakResult(text.length, LineBreakType.endOfText); 92 } 93 94 if (curr == CharCode.space || curr == CharCode.tab) { 95 continue; 96 } 97 98 if (prev == CharCode.space || 99 prev == CharCode.tab || 100 prev == CharCode.hyphen) { 101 return LineBreakResult(index, LineBreakType.opportunity); 102 } 103 } 104 return LineBreakResult(text.length, LineBreakType.endOfText); 105} 106