1cb93a386Sopenharmony_ci// Copyright 2019 Google LLC.
2cb93a386Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3cb93a386Sopenharmony_ci
4cb93a386Sopenharmony_ci#include "modules/skplaintexteditor/include/editor.h"
5cb93a386Sopenharmony_ci
6cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
7cb93a386Sopenharmony_ci#include "include/core/SkExecutor.h"
8cb93a386Sopenharmony_ci#include "include/core/SkPath.h"
9cb93a386Sopenharmony_ci#include "src/utils/SkUTF.h"
10cb93a386Sopenharmony_ci
11cb93a386Sopenharmony_ci#include "modules/skplaintexteditor/src/shape.h"
12cb93a386Sopenharmony_ci
13cb93a386Sopenharmony_ci#include <algorithm>
14cb93a386Sopenharmony_ci
15cb93a386Sopenharmony_ciusing namespace SkPlainTextEditor;
16cb93a386Sopenharmony_ci
17cb93a386Sopenharmony_cistatic inline SkRect offset(SkRect r, SkIPoint p) {
18cb93a386Sopenharmony_ci    return r.makeOffset((float)p.x(), (float)p.y());
19cb93a386Sopenharmony_ci}
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_cistatic constexpr SkRect kUnsetRect{-FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX};
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_cistatic bool valid_utf8(const char* ptr, size_t size) { return SkUTF::CountUTF8(ptr, size) >= 0; }
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_ci// Kind of like Python's readlines(), but without any allocation.
26cb93a386Sopenharmony_ci// Calls f() on each line.
27cb93a386Sopenharmony_ci// F is [](const char*, size_t) -> void
28cb93a386Sopenharmony_citemplate <typename F>
29cb93a386Sopenharmony_cistatic void readlines(const void* data, size_t size, F f) {
30cb93a386Sopenharmony_ci    const char* start = (const char*)data;
31cb93a386Sopenharmony_ci    const char* end = start + size;
32cb93a386Sopenharmony_ci    const char* ptr = start;
33cb93a386Sopenharmony_ci    while (ptr < end) {
34cb93a386Sopenharmony_ci        while (*ptr++ != '\n' && ptr < end) {}
35cb93a386Sopenharmony_ci        size_t len = ptr - start;
36cb93a386Sopenharmony_ci        SkASSERT(len > 0);
37cb93a386Sopenharmony_ci        f(start, len);
38cb93a386Sopenharmony_ci        start = ptr;
39cb93a386Sopenharmony_ci    }
40cb93a386Sopenharmony_ci}
41cb93a386Sopenharmony_ci
42cb93a386Sopenharmony_cistatic StringSlice remove_newline(const char* str, size_t len) {
43cb93a386Sopenharmony_ci    return SkASSERT((str != nullptr) || (len == 0)),
44cb93a386Sopenharmony_ci           StringSlice(str, (len > 0 && str[len - 1] == '\n') ? len - 1 : len);
45cb93a386Sopenharmony_ci}
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_civoid Editor::markDirty(TextLine* line) {
48cb93a386Sopenharmony_ci    line->fBlob = nullptr;
49cb93a386Sopenharmony_ci    line->fShaped = false;
50cb93a386Sopenharmony_ci    line->fWordBoundaries = std::vector<bool>();
51cb93a386Sopenharmony_ci}
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_civoid Editor::setFont(SkFont font) {
54cb93a386Sopenharmony_ci    if (font != fFont) {
55cb93a386Sopenharmony_ci        fFont = std::move(font);
56cb93a386Sopenharmony_ci        fNeedsReshape = true;
57cb93a386Sopenharmony_ci        for (auto& l : fLines) { this->markDirty(&l); }
58cb93a386Sopenharmony_ci    }
59cb93a386Sopenharmony_ci}
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_civoid Editor::setWidth(int w) {
62cb93a386Sopenharmony_ci    if (fWidth != w) {
63cb93a386Sopenharmony_ci        fWidth = w;
64cb93a386Sopenharmony_ci        fNeedsReshape = true;
65cb93a386Sopenharmony_ci        for (auto& l : fLines) { this->markDirty(&l); }
66cb93a386Sopenharmony_ci    }
67cb93a386Sopenharmony_ci}
68cb93a386Sopenharmony_cistatic SkPoint to_point(SkIPoint p) { return {(float)p.x(), (float)p.y()}; }
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ciEditor::TextPosition Editor::getPosition(SkIPoint xy) {
71cb93a386Sopenharmony_ci    Editor::TextPosition approximatePosition;
72cb93a386Sopenharmony_ci    this->reshapeAll();
73cb93a386Sopenharmony_ci    for (size_t j = 0; j < fLines.size(); ++j) {
74cb93a386Sopenharmony_ci        const TextLine& line = fLines[j];
75cb93a386Sopenharmony_ci        SkIRect lineRect = {0,
76cb93a386Sopenharmony_ci                            line.fOrigin.y(),
77cb93a386Sopenharmony_ci                            fWidth,
78cb93a386Sopenharmony_ci                            j + 1 < fLines.size() ? fLines[j + 1].fOrigin.y() : INT_MAX};
79cb93a386Sopenharmony_ci        if (const SkTextBlob* b = line.fBlob.get()) {
80cb93a386Sopenharmony_ci            SkIRect r = b->bounds().roundOut();
81cb93a386Sopenharmony_ci            r.offset(line.fOrigin);
82cb93a386Sopenharmony_ci            lineRect.join(r);
83cb93a386Sopenharmony_ci        }
84cb93a386Sopenharmony_ci        if (!lineRect.contains(xy.x(), xy.y())) {
85cb93a386Sopenharmony_ci            continue;
86cb93a386Sopenharmony_ci        }
87cb93a386Sopenharmony_ci        SkPoint pt = to_point(xy - line.fOrigin);
88cb93a386Sopenharmony_ci        const std::vector<SkRect>& pos = line.fCursorPos;
89cb93a386Sopenharmony_ci        for (size_t i = 0; i < pos.size(); ++i) {
90cb93a386Sopenharmony_ci            if (pos[i] != kUnsetRect && pos[i].contains(pt.x(), pt.y())) {
91cb93a386Sopenharmony_ci                return Editor::TextPosition{i, j};
92cb93a386Sopenharmony_ci            }
93cb93a386Sopenharmony_ci        }
94cb93a386Sopenharmony_ci        approximatePosition = {xy.x() <= line.fOrigin.x() ? 0 : line.fText.size(), j};
95cb93a386Sopenharmony_ci    }
96cb93a386Sopenharmony_ci    return approximatePosition;
97cb93a386Sopenharmony_ci}
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_cistatic inline bool is_utf8_continuation(char v) {
100cb93a386Sopenharmony_ci    return ((unsigned char)v & 0b11000000) ==
101cb93a386Sopenharmony_ci                               0b10000000;
102cb93a386Sopenharmony_ci}
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_cistatic const char* next_utf8(const char* p, const char* end) {
105cb93a386Sopenharmony_ci    if (p < end) {
106cb93a386Sopenharmony_ci        do {
107cb93a386Sopenharmony_ci            ++p;
108cb93a386Sopenharmony_ci        } while (p < end && is_utf8_continuation(*p));
109cb93a386Sopenharmony_ci    }
110cb93a386Sopenharmony_ci    return p;
111cb93a386Sopenharmony_ci}
112cb93a386Sopenharmony_ci
113cb93a386Sopenharmony_cistatic const char* align_utf8(const char* p, const char* begin) {
114cb93a386Sopenharmony_ci    while (p > begin && is_utf8_continuation(*p)) {
115cb93a386Sopenharmony_ci        --p;
116cb93a386Sopenharmony_ci    }
117cb93a386Sopenharmony_ci    return p;
118cb93a386Sopenharmony_ci}
119cb93a386Sopenharmony_ci
120cb93a386Sopenharmony_cistatic const char* prev_utf8(const char* p, const char* begin) {
121cb93a386Sopenharmony_ci    return p > begin ? align_utf8(p - 1, begin) : begin;
122cb93a386Sopenharmony_ci}
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ciSkRect Editor::getLocation(Editor::TextPosition cursor) {
125cb93a386Sopenharmony_ci    this->reshapeAll();
126cb93a386Sopenharmony_ci    cursor = this->move(Editor::Movement::kNowhere, cursor);
127cb93a386Sopenharmony_ci    if (fLines.size() > 0) {
128cb93a386Sopenharmony_ci        const TextLine& cLine = fLines[cursor.fParagraphIndex];
129cb93a386Sopenharmony_ci        SkRect pos = {0, 0, 0, 0};
130cb93a386Sopenharmony_ci        if (cursor.fTextByteIndex < cLine.fCursorPos.size()) {
131cb93a386Sopenharmony_ci            pos = cLine.fCursorPos[cursor.fTextByteIndex];
132cb93a386Sopenharmony_ci        }
133cb93a386Sopenharmony_ci        pos.fRight = pos.fLeft + 1;
134cb93a386Sopenharmony_ci        pos.fLeft -= 1;
135cb93a386Sopenharmony_ci        return offset(pos, cLine.fOrigin);
136cb93a386Sopenharmony_ci    }
137cb93a386Sopenharmony_ci    return SkRect{0, 0, 0, 0};
138cb93a386Sopenharmony_ci}
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_cistatic size_t count_char(const StringSlice& string, char value) {
141cb93a386Sopenharmony_ci    size_t count = 0;
142cb93a386Sopenharmony_ci    for (char c : string) { if (c == value) { ++count; } }
143cb93a386Sopenharmony_ci    return count;
144cb93a386Sopenharmony_ci}
145cb93a386Sopenharmony_ci
146cb93a386Sopenharmony_ciEditor::TextPosition Editor::insert(TextPosition pos, const char* utf8Text, size_t byteLen) {
147cb93a386Sopenharmony_ci    if (!valid_utf8(utf8Text, byteLen) || 0 == byteLen) {
148cb93a386Sopenharmony_ci        return pos;
149cb93a386Sopenharmony_ci    }
150cb93a386Sopenharmony_ci    pos = this->move(Editor::Movement::kNowhere, pos);
151cb93a386Sopenharmony_ci    fNeedsReshape = true;
152cb93a386Sopenharmony_ci    if (pos.fParagraphIndex < fLines.size()) {
153cb93a386Sopenharmony_ci        fLines[pos.fParagraphIndex].fText.insert(pos.fTextByteIndex, utf8Text, byteLen);
154cb93a386Sopenharmony_ci        this->markDirty(&fLines[pos.fParagraphIndex]);
155cb93a386Sopenharmony_ci    } else {
156cb93a386Sopenharmony_ci        SkASSERT(pos.fParagraphIndex == fLines.size());
157cb93a386Sopenharmony_ci        SkASSERT(pos.fTextByteIndex == 0);
158cb93a386Sopenharmony_ci        fLines.push_back(Editor::TextLine(StringSlice(utf8Text, byteLen)));
159cb93a386Sopenharmony_ci    }
160cb93a386Sopenharmony_ci    pos = Editor::TextPosition{pos.fTextByteIndex + byteLen, pos.fParagraphIndex};
161cb93a386Sopenharmony_ci    size_t newlinecount = count_char(fLines[pos.fParagraphIndex].fText, '\n');
162cb93a386Sopenharmony_ci    if (newlinecount > 0) {
163cb93a386Sopenharmony_ci        StringSlice src = std::move(fLines[pos.fParagraphIndex].fText);
164cb93a386Sopenharmony_ci        std::vector<TextLine>::const_iterator next = fLines.begin() + pos.fParagraphIndex + 1;
165cb93a386Sopenharmony_ci        fLines.insert(next, newlinecount, TextLine());
166cb93a386Sopenharmony_ci        TextLine* line = &fLines[pos.fParagraphIndex];
167cb93a386Sopenharmony_ci        readlines(src.begin(), src.size(), [&line](const char* str, size_t l) {
168cb93a386Sopenharmony_ci            (line++)->fText = remove_newline(str, l);
169cb93a386Sopenharmony_ci        });
170cb93a386Sopenharmony_ci    }
171cb93a386Sopenharmony_ci    return pos;
172cb93a386Sopenharmony_ci}
173cb93a386Sopenharmony_ci
174cb93a386Sopenharmony_ciEditor::TextPosition Editor::remove(TextPosition pos1, TextPosition pos2) {
175cb93a386Sopenharmony_ci    pos1 = this->move(Editor::Movement::kNowhere, pos1);
176cb93a386Sopenharmony_ci    pos2 = this->move(Editor::Movement::kNowhere, pos2);
177cb93a386Sopenharmony_ci    auto cmp = [](const Editor::TextPosition& u, const Editor::TextPosition& v) { return u < v; };
178cb93a386Sopenharmony_ci    Editor::TextPosition start = std::min(pos1, pos2, cmp);
179cb93a386Sopenharmony_ci    Editor::TextPosition end = std::max(pos1, pos2, cmp);
180cb93a386Sopenharmony_ci    if (start == end || start.fParagraphIndex == fLines.size()) {
181cb93a386Sopenharmony_ci        return start;
182cb93a386Sopenharmony_ci    }
183cb93a386Sopenharmony_ci    fNeedsReshape = true;
184cb93a386Sopenharmony_ci    if (start.fParagraphIndex == end.fParagraphIndex) {
185cb93a386Sopenharmony_ci        SkASSERT(end.fTextByteIndex > start.fTextByteIndex);
186cb93a386Sopenharmony_ci        fLines[start.fParagraphIndex].fText.remove(
187cb93a386Sopenharmony_ci                start.fTextByteIndex, end.fTextByteIndex - start.fTextByteIndex);
188cb93a386Sopenharmony_ci        this->markDirty(&fLines[start.fParagraphIndex]);
189cb93a386Sopenharmony_ci    } else {
190cb93a386Sopenharmony_ci        SkASSERT(end.fParagraphIndex < fLines.size());
191cb93a386Sopenharmony_ci        auto& line = fLines[start.fParagraphIndex];
192cb93a386Sopenharmony_ci        line.fText.remove(start.fTextByteIndex,
193cb93a386Sopenharmony_ci                          line.fText.size() - start.fTextByteIndex);
194cb93a386Sopenharmony_ci        line.fText.insert(start.fTextByteIndex,
195cb93a386Sopenharmony_ci                          fLines[end.fParagraphIndex].fText.begin() + end.fTextByteIndex,
196cb93a386Sopenharmony_ci                          fLines[end.fParagraphIndex].fText.size() - end.fTextByteIndex);
197cb93a386Sopenharmony_ci        this->markDirty(&line);
198cb93a386Sopenharmony_ci        fLines.erase(fLines.begin() + start.fParagraphIndex + 1,
199cb93a386Sopenharmony_ci                     fLines.begin() + end.fParagraphIndex + 1);
200cb93a386Sopenharmony_ci    }
201cb93a386Sopenharmony_ci    return start;
202cb93a386Sopenharmony_ci}
203cb93a386Sopenharmony_ci
204cb93a386Sopenharmony_cistatic void append(char** dst, size_t* count, const char* src, size_t n) {
205cb93a386Sopenharmony_ci    if (*dst) {
206cb93a386Sopenharmony_ci        ::memcpy(*dst, src, n);
207cb93a386Sopenharmony_ci        *dst += n;
208cb93a386Sopenharmony_ci    }
209cb93a386Sopenharmony_ci    *count += n;
210cb93a386Sopenharmony_ci}
211cb93a386Sopenharmony_ci
212cb93a386Sopenharmony_cisize_t Editor::copy(TextPosition pos1, TextPosition pos2, char* dst) const {
213cb93a386Sopenharmony_ci    size_t size = 0;
214cb93a386Sopenharmony_ci    pos1 = this->move(Editor::Movement::kNowhere, pos1);
215cb93a386Sopenharmony_ci    pos2 = this->move(Editor::Movement::kNowhere, pos2);
216cb93a386Sopenharmony_ci    auto cmp = [](const Editor::TextPosition& u, const Editor::TextPosition& v) { return u < v; };
217cb93a386Sopenharmony_ci    Editor::TextPosition start = std::min(pos1, pos2, cmp);
218cb93a386Sopenharmony_ci    Editor::TextPosition end = std::max(pos1, pos2, cmp);
219cb93a386Sopenharmony_ci    if (start == end || start.fParagraphIndex == fLines.size()) {
220cb93a386Sopenharmony_ci        return size;
221cb93a386Sopenharmony_ci    }
222cb93a386Sopenharmony_ci    if (start.fParagraphIndex == end.fParagraphIndex) {
223cb93a386Sopenharmony_ci        SkASSERT(end.fTextByteIndex > start.fTextByteIndex);
224cb93a386Sopenharmony_ci        auto& str = fLines[start.fParagraphIndex].fText;
225cb93a386Sopenharmony_ci        append(&dst, &size, str.begin() + start.fTextByteIndex,
226cb93a386Sopenharmony_ci               end.fTextByteIndex - start.fTextByteIndex);
227cb93a386Sopenharmony_ci        return size;
228cb93a386Sopenharmony_ci    }
229cb93a386Sopenharmony_ci    SkASSERT(end.fParagraphIndex < fLines.size());
230cb93a386Sopenharmony_ci    const std::vector<TextLine>::const_iterator firstP = fLines.begin() + start.fParagraphIndex;
231cb93a386Sopenharmony_ci    const std::vector<TextLine>::const_iterator lastP  = fLines.begin() + end.fParagraphIndex;
232cb93a386Sopenharmony_ci    const auto& first = firstP->fText;
233cb93a386Sopenharmony_ci    const auto& last  = lastP->fText;
234cb93a386Sopenharmony_ci
235cb93a386Sopenharmony_ci    append(&dst, &size, first.begin() + start.fTextByteIndex, first.size() - start.fTextByteIndex);
236cb93a386Sopenharmony_ci    for (auto line = firstP + 1; line < lastP; ++line) {
237cb93a386Sopenharmony_ci        append(&dst, &size, "\n", 1);
238cb93a386Sopenharmony_ci        append(&dst, &size, line->fText.begin(), line->fText.size());
239cb93a386Sopenharmony_ci    }
240cb93a386Sopenharmony_ci    append(&dst, &size, "\n", 1);
241cb93a386Sopenharmony_ci    append(&dst, &size, last.begin(), end.fTextByteIndex);
242cb93a386Sopenharmony_ci    return size;
243cb93a386Sopenharmony_ci}
244cb93a386Sopenharmony_ci
245cb93a386Sopenharmony_cistatic inline const char* begin(const StringSlice& s) { return s.begin(); }
246cb93a386Sopenharmony_ci
247cb93a386Sopenharmony_cistatic inline const char* end(const StringSlice& s) { return s.end(); }
248cb93a386Sopenharmony_ci
249cb93a386Sopenharmony_cistatic size_t align_column(const StringSlice& str, size_t p) {
250cb93a386Sopenharmony_ci    if (p >= str.size()) {
251cb93a386Sopenharmony_ci        return str.size();
252cb93a386Sopenharmony_ci    }
253cb93a386Sopenharmony_ci    return align_utf8(begin(str) + p, begin(str)) - begin(str);
254cb93a386Sopenharmony_ci}
255cb93a386Sopenharmony_ci
256cb93a386Sopenharmony_ci// returns smallest i such that list[i] > value.  value > list[i-1]
257cb93a386Sopenharmony_ci// Use a binary search since list is monotonic
258cb93a386Sopenharmony_citemplate <typename T>
259cb93a386Sopenharmony_cistatic size_t find_first_larger(const std::vector<T>& list, T value) {
260cb93a386Sopenharmony_ci    return (size_t)(std::upper_bound(list.begin(), list.end(), value) - list.begin());
261cb93a386Sopenharmony_ci}
262cb93a386Sopenharmony_ci
263cb93a386Sopenharmony_cistatic size_t find_closest_x(const std::vector<SkRect>& bounds, float x, size_t b, size_t e) {
264cb93a386Sopenharmony_ci    if (b >= e) {
265cb93a386Sopenharmony_ci        return b;
266cb93a386Sopenharmony_ci    }
267cb93a386Sopenharmony_ci    SkASSERT(e <= bounds.size());
268cb93a386Sopenharmony_ci    size_t best_index = b;
269cb93a386Sopenharmony_ci    float best_diff = ::fabsf(bounds[best_index].x() - x);
270cb93a386Sopenharmony_ci    for (size_t i = b + 1; i < e; ++i) {
271cb93a386Sopenharmony_ci        float d = ::fabsf(bounds[i].x() - x);
272cb93a386Sopenharmony_ci        if (d < best_diff) {
273cb93a386Sopenharmony_ci            best_diff = d;
274cb93a386Sopenharmony_ci            best_index = i;
275cb93a386Sopenharmony_ci        }
276cb93a386Sopenharmony_ci    }
277cb93a386Sopenharmony_ci    return best_index;
278cb93a386Sopenharmony_ci}
279cb93a386Sopenharmony_ci
280cb93a386Sopenharmony_ciEditor::TextPosition Editor::move(Editor::Movement move, Editor::TextPosition pos) const {
281cb93a386Sopenharmony_ci    if (fLines.empty()) {
282cb93a386Sopenharmony_ci        return {0, 0};
283cb93a386Sopenharmony_ci    }
284cb93a386Sopenharmony_ci    // First thing: fix possible bad input values.
285cb93a386Sopenharmony_ci    if (pos.fParagraphIndex >= fLines.size()) {
286cb93a386Sopenharmony_ci        pos.fParagraphIndex = fLines.size() - 1;
287cb93a386Sopenharmony_ci        pos.fTextByteIndex = fLines[pos.fParagraphIndex].fText.size();
288cb93a386Sopenharmony_ci    } else {
289cb93a386Sopenharmony_ci        pos.fTextByteIndex = align_column(fLines[pos.fParagraphIndex].fText, pos.fTextByteIndex);
290cb93a386Sopenharmony_ci    }
291cb93a386Sopenharmony_ci
292cb93a386Sopenharmony_ci    SkASSERT(pos.fParagraphIndex < fLines.size());
293cb93a386Sopenharmony_ci    SkASSERT(pos.fTextByteIndex <= fLines[pos.fParagraphIndex].fText.size());
294cb93a386Sopenharmony_ci
295cb93a386Sopenharmony_ci    SkASSERT(pos.fTextByteIndex == fLines[pos.fParagraphIndex].fText.size() ||
296cb93a386Sopenharmony_ci             !is_utf8_continuation(fLines[pos.fParagraphIndex].fText.begin()[pos.fTextByteIndex]));
297cb93a386Sopenharmony_ci
298cb93a386Sopenharmony_ci    switch (move) {
299cb93a386Sopenharmony_ci        case Editor::Movement::kNowhere:
300cb93a386Sopenharmony_ci            break;
301cb93a386Sopenharmony_ci        case Editor::Movement::kLeft:
302cb93a386Sopenharmony_ci            if (0 == pos.fTextByteIndex) {
303cb93a386Sopenharmony_ci                if (pos.fParagraphIndex > 0) {
304cb93a386Sopenharmony_ci                    --pos.fParagraphIndex;
305cb93a386Sopenharmony_ci                    pos.fTextByteIndex = fLines[pos.fParagraphIndex].fText.size();
306cb93a386Sopenharmony_ci                }
307cb93a386Sopenharmony_ci            } else {
308cb93a386Sopenharmony_ci                const auto& str = fLines[pos.fParagraphIndex].fText;
309cb93a386Sopenharmony_ci                pos.fTextByteIndex =
310cb93a386Sopenharmony_ci                    prev_utf8(begin(str) + pos.fTextByteIndex, begin(str)) - begin(str);
311cb93a386Sopenharmony_ci            }
312cb93a386Sopenharmony_ci            break;
313cb93a386Sopenharmony_ci        case Editor::Movement::kRight:
314cb93a386Sopenharmony_ci            if (fLines[pos.fParagraphIndex].fText.size() == pos.fTextByteIndex) {
315cb93a386Sopenharmony_ci                if (pos.fParagraphIndex + 1 < fLines.size()) {
316cb93a386Sopenharmony_ci                    ++pos.fParagraphIndex;
317cb93a386Sopenharmony_ci                    pos.fTextByteIndex = 0;
318cb93a386Sopenharmony_ci                }
319cb93a386Sopenharmony_ci            } else {
320cb93a386Sopenharmony_ci                const auto& str = fLines[pos.fParagraphIndex].fText;
321cb93a386Sopenharmony_ci                pos.fTextByteIndex =
322cb93a386Sopenharmony_ci                    next_utf8(begin(str) + pos.fTextByteIndex, end(str)) - begin(str);
323cb93a386Sopenharmony_ci            }
324cb93a386Sopenharmony_ci            break;
325cb93a386Sopenharmony_ci        case Editor::Movement::kHome:
326cb93a386Sopenharmony_ci            {
327cb93a386Sopenharmony_ci                const std::vector<size_t>& list = fLines[pos.fParagraphIndex].fLineEndOffsets;
328cb93a386Sopenharmony_ci                size_t f = find_first_larger(list, pos.fTextByteIndex);
329cb93a386Sopenharmony_ci                pos.fTextByteIndex = f > 0 ? list[f - 1] : 0;
330cb93a386Sopenharmony_ci            }
331cb93a386Sopenharmony_ci            break;
332cb93a386Sopenharmony_ci        case Editor::Movement::kEnd:
333cb93a386Sopenharmony_ci            {
334cb93a386Sopenharmony_ci                const std::vector<size_t>& list = fLines[pos.fParagraphIndex].fLineEndOffsets;
335cb93a386Sopenharmony_ci                size_t f = find_first_larger(list, pos.fTextByteIndex);
336cb93a386Sopenharmony_ci                if (f < list.size()) {
337cb93a386Sopenharmony_ci                    pos.fTextByteIndex = list[f] > 0 ? list[f] - 1 : 0;
338cb93a386Sopenharmony_ci                } else {
339cb93a386Sopenharmony_ci                    pos.fTextByteIndex = fLines[pos.fParagraphIndex].fText.size();
340cb93a386Sopenharmony_ci                }
341cb93a386Sopenharmony_ci            }
342cb93a386Sopenharmony_ci            break;
343cb93a386Sopenharmony_ci        case Editor::Movement::kUp:
344cb93a386Sopenharmony_ci            {
345cb93a386Sopenharmony_ci                SkASSERT(pos.fTextByteIndex < fLines[pos.fParagraphIndex].fCursorPos.size());
346cb93a386Sopenharmony_ci                float x = fLines[pos.fParagraphIndex].fCursorPos[pos.fTextByteIndex].left();
347cb93a386Sopenharmony_ci                const std::vector<size_t>& list = fLines[pos.fParagraphIndex].fLineEndOffsets;
348cb93a386Sopenharmony_ci                size_t f = find_first_larger(list, pos.fTextByteIndex);
349cb93a386Sopenharmony_ci                // list[f] > value.  value > list[f-1]
350cb93a386Sopenharmony_ci                if (f > 0) {
351cb93a386Sopenharmony_ci                    // not the first line in paragraph.
352cb93a386Sopenharmony_ci                    pos.fTextByteIndex = find_closest_x(fLines[pos.fParagraphIndex].fCursorPos, x,
353cb93a386Sopenharmony_ci                                                        (f == 1) ? 0 : list[f - 2],
354cb93a386Sopenharmony_ci                                                        list[f - 1]);
355cb93a386Sopenharmony_ci                } else if (pos.fParagraphIndex > 0) {
356cb93a386Sopenharmony_ci                    --pos.fParagraphIndex;
357cb93a386Sopenharmony_ci                    const auto& newLine = fLines[pos.fParagraphIndex];
358cb93a386Sopenharmony_ci                    size_t r = newLine.fLineEndOffsets.size();
359cb93a386Sopenharmony_ci                    if (r > 0) {
360cb93a386Sopenharmony_ci                        pos.fTextByteIndex = find_closest_x(newLine.fCursorPos, x,
361cb93a386Sopenharmony_ci                                                            newLine.fLineEndOffsets[r - 1],
362cb93a386Sopenharmony_ci                                                            newLine.fCursorPos.size());
363cb93a386Sopenharmony_ci                    } else {
364cb93a386Sopenharmony_ci                        pos.fTextByteIndex = find_closest_x(newLine.fCursorPos, x, 0,
365cb93a386Sopenharmony_ci                                                            newLine.fCursorPos.size());
366cb93a386Sopenharmony_ci                    }
367cb93a386Sopenharmony_ci                }
368cb93a386Sopenharmony_ci                pos.fTextByteIndex =
369cb93a386Sopenharmony_ci                    align_column(fLines[pos.fParagraphIndex].fText, pos.fTextByteIndex);
370cb93a386Sopenharmony_ci            }
371cb93a386Sopenharmony_ci            break;
372cb93a386Sopenharmony_ci        case Editor::Movement::kDown:
373cb93a386Sopenharmony_ci            {
374cb93a386Sopenharmony_ci                const std::vector<size_t>& list = fLines[pos.fParagraphIndex].fLineEndOffsets;
375cb93a386Sopenharmony_ci                float x = fLines[pos.fParagraphIndex].fCursorPos[pos.fTextByteIndex].left();
376cb93a386Sopenharmony_ci
377cb93a386Sopenharmony_ci                size_t f = find_first_larger(list, pos.fTextByteIndex);
378cb93a386Sopenharmony_ci                if (f < list.size()) {
379cb93a386Sopenharmony_ci                    const auto& bounds = fLines[pos.fParagraphIndex].fCursorPos;
380cb93a386Sopenharmony_ci                    pos.fTextByteIndex = find_closest_x(bounds, x, list[f],
381cb93a386Sopenharmony_ci                                                        f + 1 < list.size() ? list[f + 1]
382cb93a386Sopenharmony_ci                                                                            : bounds.size());
383cb93a386Sopenharmony_ci                } else if (pos.fParagraphIndex + 1 < fLines.size()) {
384cb93a386Sopenharmony_ci                    ++pos.fParagraphIndex;
385cb93a386Sopenharmony_ci                    const auto& bounds = fLines[pos.fParagraphIndex].fCursorPos;
386cb93a386Sopenharmony_ci                    const std::vector<size_t>& l2 = fLines[pos.fParagraphIndex].fLineEndOffsets;
387cb93a386Sopenharmony_ci                    pos.fTextByteIndex = find_closest_x(bounds, x, 0,
388cb93a386Sopenharmony_ci                                                        l2.size() > 0 ? l2[0] : bounds.size());
389cb93a386Sopenharmony_ci                } else {
390cb93a386Sopenharmony_ci                    pos.fTextByteIndex = fLines[pos.fParagraphIndex].fText.size();
391cb93a386Sopenharmony_ci                }
392cb93a386Sopenharmony_ci                pos.fTextByteIndex =
393cb93a386Sopenharmony_ci                    align_column(fLines[pos.fParagraphIndex].fText, pos.fTextByteIndex);
394cb93a386Sopenharmony_ci            }
395cb93a386Sopenharmony_ci            break;
396cb93a386Sopenharmony_ci        case Editor::Movement::kWordLeft:
397cb93a386Sopenharmony_ci            {
398cb93a386Sopenharmony_ci                if (pos.fTextByteIndex == 0) {
399cb93a386Sopenharmony_ci                    pos = this->move(Editor::Movement::kLeft, pos);
400cb93a386Sopenharmony_ci                    break;
401cb93a386Sopenharmony_ci                }
402cb93a386Sopenharmony_ci                const std::vector<bool>& words = fLines[pos.fParagraphIndex].fWordBoundaries;
403cb93a386Sopenharmony_ci                SkASSERT(words.size() == fLines[pos.fParagraphIndex].fText.size());
404cb93a386Sopenharmony_ci                do {
405cb93a386Sopenharmony_ci                    --pos.fTextByteIndex;
406cb93a386Sopenharmony_ci                } while (pos.fTextByteIndex > 0 && !words[pos.fTextByteIndex]);
407cb93a386Sopenharmony_ci            }
408cb93a386Sopenharmony_ci            break;
409cb93a386Sopenharmony_ci        case Editor::Movement::kWordRight:
410cb93a386Sopenharmony_ci            {
411cb93a386Sopenharmony_ci                const StringSlice& text = fLines[pos.fParagraphIndex].fText;
412cb93a386Sopenharmony_ci                if (pos.fTextByteIndex == text.size()) {
413cb93a386Sopenharmony_ci                    pos = this->move(Editor::Movement::kRight, pos);
414cb93a386Sopenharmony_ci                    break;
415cb93a386Sopenharmony_ci                }
416cb93a386Sopenharmony_ci                const std::vector<bool>& words = fLines[pos.fParagraphIndex].fWordBoundaries;
417cb93a386Sopenharmony_ci                SkASSERT(words.size() == text.size());
418cb93a386Sopenharmony_ci                do {
419cb93a386Sopenharmony_ci                    ++pos.fTextByteIndex;
420cb93a386Sopenharmony_ci                } while (pos.fTextByteIndex < text.size() && !words[pos.fTextByteIndex]);
421cb93a386Sopenharmony_ci            }
422cb93a386Sopenharmony_ci            break;
423cb93a386Sopenharmony_ci
424cb93a386Sopenharmony_ci    }
425cb93a386Sopenharmony_ci    return pos;
426cb93a386Sopenharmony_ci}
427cb93a386Sopenharmony_ci
428cb93a386Sopenharmony_civoid Editor::paint(SkCanvas* c, PaintOpts options) {
429cb93a386Sopenharmony_ci    this->reshapeAll();
430cb93a386Sopenharmony_ci    if (!c) {
431cb93a386Sopenharmony_ci        return;
432cb93a386Sopenharmony_ci    }
433cb93a386Sopenharmony_ci
434cb93a386Sopenharmony_ci    c->drawPaint(SkPaint(options.fBackgroundColor));
435cb93a386Sopenharmony_ci
436cb93a386Sopenharmony_ci    SkPaint selection = SkPaint(options.fSelectionColor);
437cb93a386Sopenharmony_ci    auto cmp = [](const Editor::TextPosition& u, const Editor::TextPosition& v) { return u < v; };
438cb93a386Sopenharmony_ci    for (TextPosition pos = std::min(options.fSelectionBegin, options.fSelectionEnd, cmp),
439cb93a386Sopenharmony_ci                      end = std::max(options.fSelectionBegin, options.fSelectionEnd, cmp);
440cb93a386Sopenharmony_ci         pos < end;
441cb93a386Sopenharmony_ci         pos = this->move(Editor::Movement::kRight, pos))
442cb93a386Sopenharmony_ci    {
443cb93a386Sopenharmony_ci        SkASSERT(pos.fParagraphIndex < fLines.size());
444cb93a386Sopenharmony_ci        const TextLine& l = fLines[pos.fParagraphIndex];
445cb93a386Sopenharmony_ci        c->drawRect(offset(l.fCursorPos[pos.fTextByteIndex], l.fOrigin), selection);
446cb93a386Sopenharmony_ci    }
447cb93a386Sopenharmony_ci
448cb93a386Sopenharmony_ci    if (fLines.size() > 0) {
449cb93a386Sopenharmony_ci        c->drawRect(Editor::getLocation(options.fCursor), SkPaint(options.fCursorColor));
450cb93a386Sopenharmony_ci    }
451cb93a386Sopenharmony_ci
452cb93a386Sopenharmony_ci    SkPaint foreground = SkPaint(options.fForegroundColor);
453cb93a386Sopenharmony_ci    for (const TextLine& line : fLines) {
454cb93a386Sopenharmony_ci        if (line.fBlob) {
455cb93a386Sopenharmony_ci            c->drawTextBlob(line.fBlob.get(), line.fOrigin.x(), line.fOrigin.y(), foreground);
456cb93a386Sopenharmony_ci        }
457cb93a386Sopenharmony_ci    }
458cb93a386Sopenharmony_ci}
459cb93a386Sopenharmony_ci
460cb93a386Sopenharmony_civoid Editor::reshapeAll() {
461cb93a386Sopenharmony_ci    if (fNeedsReshape) {
462cb93a386Sopenharmony_ci        if (fLines.empty()) {
463cb93a386Sopenharmony_ci            fLines.push_back(TextLine());
464cb93a386Sopenharmony_ci        }
465cb93a386Sopenharmony_ci        float shape_width = (float)(fWidth);
466cb93a386Sopenharmony_ci        #ifdef SK_EDITOR_GO_FAST
467cb93a386Sopenharmony_ci        SkSemaphore semaphore;
468cb93a386Sopenharmony_ci        std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool(100);
469cb93a386Sopenharmony_ci        int jobCount = 0;
470cb93a386Sopenharmony_ci        for (TextLine& line : fLines) {
471cb93a386Sopenharmony_ci            if (!line.fShaped) {
472cb93a386Sopenharmony_ci                executor->add([&]() {
473cb93a386Sopenharmony_ci                    ShapeResult result = Shape(line.fText.begin(), line.fText.size(),
474cb93a386Sopenharmony_ci                                               fFont, fLocale, shape_width);
475cb93a386Sopenharmony_ci                    line.fBlob           = std::move(result.blob);
476cb93a386Sopenharmony_ci                    line.fLineEndOffsets = std::move(result.lineBreakOffsets);
477cb93a386Sopenharmony_ci                    line.fCursorPos      = std::move(result.glyphBounds);
478cb93a386Sopenharmony_ci                    line.fWordBoundaries = std::move(result.wordBreaks);
479cb93a386Sopenharmony_ci                    line.fHeight         = result.verticalAdvance;
480cb93a386Sopenharmony_ci                    line.fShaped = true;
481cb93a386Sopenharmony_ci                    semaphore.signal();
482cb93a386Sopenharmony_ci                }
483cb93a386Sopenharmony_ci                ++jobCount;
484cb93a386Sopenharmony_ci            });
485cb93a386Sopenharmony_ci        }
486cb93a386Sopenharmony_ci        while (jobCount-- > 0) { semaphore.wait(); }
487cb93a386Sopenharmony_ci        #else
488cb93a386Sopenharmony_ci        int i = 0;
489cb93a386Sopenharmony_ci        for (TextLine& line : fLines) {
490cb93a386Sopenharmony_ci            if (!line.fShaped) {
491cb93a386Sopenharmony_ci                ShapeResult result = Shape(line.fText.begin(), line.fText.size(),
492cb93a386Sopenharmony_ci                                           fFont, fLocale, shape_width);
493cb93a386Sopenharmony_ci                line.fBlob           = std::move(result.blob);
494cb93a386Sopenharmony_ci                line.fLineEndOffsets = std::move(result.lineBreakOffsets);
495cb93a386Sopenharmony_ci                line.fCursorPos      = std::move(result.glyphBounds);
496cb93a386Sopenharmony_ci                line.fWordBoundaries = std::move(result.wordBreaks);
497cb93a386Sopenharmony_ci                line.fHeight         = result.verticalAdvance;
498cb93a386Sopenharmony_ci                line.fShaped = true;
499cb93a386Sopenharmony_ci            }
500cb93a386Sopenharmony_ci            ++i;
501cb93a386Sopenharmony_ci        }
502cb93a386Sopenharmony_ci        #endif
503cb93a386Sopenharmony_ci        int y = 0;
504cb93a386Sopenharmony_ci        for (TextLine& line : fLines) {
505cb93a386Sopenharmony_ci            line.fOrigin = {0, y};
506cb93a386Sopenharmony_ci            y += line.fHeight;
507cb93a386Sopenharmony_ci        }
508cb93a386Sopenharmony_ci        fHeight = y;
509cb93a386Sopenharmony_ci        fNeedsReshape = false;
510cb93a386Sopenharmony_ci    }
511cb93a386Sopenharmony_ci}
512cb93a386Sopenharmony_ci
513