summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibVT/Line.cpp
blob: f1c817b82fbeeaa0d3c61f5197fe159be2ebab6f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <LibVT/Line.h>

namespace VT {

Line::Line(size_t length)
{
    set_length(length);
}

Line::~Line()
{
}

void Line::rewrap(size_t new_length, Line* next_line, CursorPosition* cursor, bool cursor_is_on_next_line)
{
    size_t old_length = length();
    if (old_length == new_length)
        return;

    // Drop the empty cells
    if (m_terminated_at.has_value() && m_cells.size() > m_terminated_at.value())
        m_cells.remove(m_terminated_at.value(), m_cells.size() - m_terminated_at.value());

    if (!next_line)
        return m_cells.resize(new_length);

    if (old_length < new_length)
        take_cells_from_next_line(new_length, next_line, cursor_is_on_next_line, cursor);
    else
        push_cells_into_next_line(new_length, next_line, cursor_is_on_next_line, cursor);
}

void Line::set_length(size_t new_length)
{
    m_cells.resize(new_length);
}

void Line::push_cells_into_next_line(size_t new_length, Line* next_line, bool cursor_is_on_next_line, CursorPosition* cursor)
{
    if (is_empty())
        return;

    if (length() <= new_length)
        return;

    // Push as many cells as _wouldn't_ fit into the next line.
    auto cells_to_preserve = !next_line->m_terminated_at.has_value() && next_line->is_empty() ? 0 : m_terminated_at.value_or(0);
    auto preserved_cells = max(new_length, cells_to_preserve);
    auto cells_to_push_into_next_line = length() - preserved_cells;
    if (!cells_to_push_into_next_line)
        return;

    if (next_line->m_terminated_at.has_value())
        next_line->m_terminated_at = next_line->m_terminated_at.value() + cells_to_push_into_next_line;

    if (m_terminated_at.has_value() && cells_to_preserve == 0) {
        m_terminated_at.clear();
        if (!next_line->m_terminated_at.has_value())
            next_line->m_terminated_at = cells_to_push_into_next_line;
    }

    if (cursor) {
        if (cursor_is_on_next_line) {
            cursor->column += cells_to_push_into_next_line;
        } else if (cursor->column >= preserved_cells) {
            cursor->row++;
            cursor->column = cursor->column - preserved_cells;
        }
    }

    MUST(next_line->m_cells.try_prepend(m_cells.span().slice_from_end(cells_to_push_into_next_line).data(), cells_to_push_into_next_line));
    m_cells.remove(m_cells.size() - cells_to_push_into_next_line, cells_to_push_into_next_line);
    if (m_terminated_at.has_value())
        m_terminated_at = m_terminated_at.value() - cells_to_push_into_next_line;
}

void Line::take_cells_from_next_line(size_t new_length, Line* next_line, bool cursor_is_on_next_line, CursorPosition* cursor)
{
    // Take as many cells as would fit from the next line
    if (m_terminated_at.has_value())
        return;

    if (length() >= new_length)
        return;

    auto cells_to_grab_from_next_line = min(new_length - length(), next_line->length());
    auto clear_next_line = false;
    if (next_line->m_terminated_at.has_value()) {
        if (cells_to_grab_from_next_line == *next_line->m_terminated_at) {
            m_terminated_at = length() + *next_line->m_terminated_at;
            next_line->m_terminated_at.clear();
            clear_next_line = true;
        } else {
            next_line->m_terminated_at = next_line->m_terminated_at.value() - cells_to_grab_from_next_line;
        }
    }

    if (cells_to_grab_from_next_line) {
        if (cursor && cursor_is_on_next_line) {
            if (cursor->column <= cells_to_grab_from_next_line) {
                cursor->row--;
                cursor->column += m_cells.size();
            } else {
                cursor->column -= cells_to_grab_from_next_line;
            }
        }
        MUST(m_cells.try_append(next_line->m_cells.data(), cells_to_grab_from_next_line));
        next_line->m_cells.remove(0, cells_to_grab_from_next_line);
    }

    if (clear_next_line)
        next_line->m_cells.clear();
}

void Line::clear_range(size_t first_column, size_t last_column, const Attribute& attribute)
{
    VERIFY(first_column <= last_column);
    VERIFY(last_column < m_cells.size());
    for (size_t i = first_column; i <= last_column; ++i) {
        auto& cell = m_cells[i];
        if (!m_dirty)
            m_dirty = cell.code_point != ' ' || cell.attribute != attribute;
        cell = Cell { .code_point = ' ', .attribute = attribute };
    }
}

bool Line::has_only_one_background_color() const
{
    if (!length())
        return true;
    // FIXME: Cache this result?
    auto color = attribute_at(0).effective_background_color();
    for (size_t i = 1; i < length(); ++i) {
        if (attribute_at(i).effective_background_color() != color)
            return false;
    }
    return true;
}

}