Repository: LadybirdBrowser/ladybird
File: Libraries/LibWeb/HTML/FormAssociatedElement.cpp (C++)

Code before:
    return result;
}

void FormAssociatedTextControlElement::increment_cursor_position_to_next_line(CollapseSelection collapse)
{
    auto const text_node = form_associated_element_to_text_node();
    if (!text_node)
        return;

    auto const& code_points = text_node->data().utf16_view();

    PositionInLine position_in_line = find_cursor_position_in_line(m_selection_end);

    auto it = code_points.iterator_at_code_unit_offset(m_selection_end);
    for (; !it.done(); ++it) {
        if (*it == '\n') {
            ++it;
            for (auto j = 1; j <= position_in_line.column_code_point; j++) {
                if (it.done() || *it == '\n') {
                    break;
                }
                ++it;
            }
            break;
        }
    }
    size_t new_offset = code_points.iterator_offset(it);

    if (collapse == CollapseSelection::Yes) {
        collapse_selection_to_offset(new_offset);
    } else {
        m_selection_end = new_offset;
    }
    selection_was_changed();
}

void FormAssociatedTextControlElement::decrement_cursor_position_to_previous_line(CollapseSelection collapse)
{
    auto const text_node = form_associated_element_to_text_node();
    if (!text_node)
        return;

    auto const& code_points = text_node->data().utf16_view();
    PositionInLine current_position = find_cursor_position_in_line(m_selection_end);
    size_t new_offset = 0;
    if (current_position.previous_newline_index != 0) {

        PositionInLine previous_position = find_cursor_position_in_line(current_position.previous_newline_index - 1);
        auto it = code_points.iterator_at_code_unit_offset(previous_position.previous_newline_index);
        if (previous_position.previous_newline_index != 0) {
            ++it;

Review comment (refactor):
Looking at these algorithms, they feel a bit over-complicated. I think we can simplify this quite a bit and avoid the iterator changes altogether. For example, if we just look at `decrement_cursor_position_to_previous_line`, we essentially want to:
1. Find the start of the current line
2. Find the position of the offset in the current line
3. Find the start of the previous line
4. Translate the position from (2) to that line

Could we get by with something like this?
```c++
static constexpr size_t find_line_start(Utf16View const& view, size_t offset)
{
    while (offset != 0 && view.code_unit_at(offset - 1) != '\n')
        --offset;
    return offset;
}

void FormAssociatedTextControlElement::decrement_cursor_position_to_previous_line(CollapseSelection collapse)
{
    auto const text_node = form_associated_element_to_text_node();
    if (!text_node)
        return;

    auto code_points = text_node->data().utf16_view();
    size_t new_offset = 0;

    if (auto current_line_start = find_line_start(code_points, m_selection_end); current_line_start != 0) {
        auto position_within_line = m_selection_end - current_line_start;

        auto previous_line_start = find_line_start(code_points, current_line_start - 1);
        auto previous_line_length = current_line_start - previous_line_start - 1;

        new_offset = previous_line_start + min(position_within_line, previous_line_length);

        if (new_offset > 0 && AK::UnicodeUtils::is_utf16_low_surrogate(code_points.code_unit_at(new_offset))) {
            if (AK::UnicodeUtils::is_utf16_high_surrogate(code_points.code_unit_at(new_offset - 1)))
                --new_offset;
        }
    }

    if (collapse == CollapseSelection::Yes) {
        collapse_selection_to_offset(new_offset);
    } else {
        m_selection_end = new_offset;
    }

    selection_was_changed();
}
```

I didn't test it super extensively, but it feels quite a bit cleaner to me this way. We can just operate on code unit indices until we need to check if we ended up in the middle of a code point.

Code after:
    size_t new_offset = current_line_end;

    if (current_line_end < length) {
        auto next_line_start = current_line_end + 1;
        auto position_within_line = m_selection_end - find_line_start(code_points, m_selection_end);
        auto next_line_end = find_line_end(code_points, next_line_start);
        auto next_line_length = next_line_end - next_line_start;

        new_offset = next_line_start + min(position_within_line, next_line_length);

        if (new_offset > 0 && new_offset < length && AK::UnicodeUtils::is_utf16_low_surrogate(code_points.code_unit_at(new_offset))) {
            if (AK::UnicodeUtils::is_utf16_high_surrogate(code_points.code_unit_at(new_offset - 1)))
                --new_offset;
        }
    }

    if (collapse == CollapseSelection::Yes) {
        collapse_selection_to_offset(new_offset);
    } else {
        m_selection_end = new_offset;
    }

    selection_was_changed();
}

void FormAssociatedTextControlElement::decrement_cursor_position_to_previous_line(CollapseSelection collapse)
{
    auto const text_node = form_associated_element_to_text_node();
    if (!text_node)
        return;

    auto code_points = text_node->data().utf16_view();
    size_t new_offset = 0;

    if (auto current_line_start = find_line_start(code_points, m_selection_end); current_line_start != 0) {
        auto position_within_line = m_selection_end - current_line_start;

        auto previous_line_start = find_line_start(code_points, current_line_start - 1);
        auto previous_line_length = current_line_start - previous_line_start - 1;

        new_offset = previous_line_start + min(position_within_line, previous_line_length);

        if (new_offset > 0 && AK::UnicodeUtils::is_utf16_low_surrogate(code_points.code_unit_at(new_offset))) {
            if (AK::UnicodeUtils::is_utf16_high_surrogate(code_points.code_unit_at(new_offset - 1)))
                --new_offset;
        }
    }

    if (collapse == CollapseSelection::Yes) {
        collapse_selection_to_offset(new_offset);
    } else {