• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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