aboutsummaryrefslogtreecommitdiffstats
path: root/src/cell.rs
blob: ecc44b27ab3ebb2e187f2951e9247510be15634b (plain) (blame)
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use unicode_width::UnicodeWidthChar as _;

const CODEPOINTS_IN_CELL: usize = 6;

/// Represents a single terminal cell.
#[derive(Clone, Debug, Default, Eq)]
pub struct Cell {
    contents: [char; CODEPOINTS_IN_CELL],
    len: u8,
    attrs: crate::attrs::Attrs,
}

#[allow(clippy::collapsible_if)]
impl PartialEq<Cell> for Cell {
    fn eq(&self, other: &Self) -> bool {
        if self.len != other.len {
            return false;
        }
        if self.attrs != other.attrs {
            return false;
        }
        let len = self.len();
        self.contents[..len] == other.contents[..len]
    }
}

impl Cell {
    #[inline]
    fn len(&self) -> usize {
        (self.len & 0x0f) as usize
    }

    pub(crate) fn set(&mut self, c: char, a: crate::attrs::Attrs) {
        self.contents[0] = c;
        self.len = 1;
        // strings in this context should always be an arbitrary character
        // followed by zero or more zero-width characters, so we should only
        // have to look at the first character
        self.set_wide(c.width().unwrap_or(0) > 1);
        self.attrs = a;
    }

    pub(crate) fn append(&mut self, c: char) {
        if self.len() >= CODEPOINTS_IN_CELL {
            return;
        }

        self.contents[self.len()] = c;
        self.len += 1;

        self.normalize();
    }

    #[cfg(not(feature = "unicode-normalization"))]
    #[inline]
    fn normalize(&mut self) {}

    #[cfg(feature = "unicode-normalization")]
    #[inline]
    fn normalize(&mut self) {
        use unicode_normalization::UnicodeNormalization as _;

        // some fonts have combined characters but can't render combining
        // characters correctly, so try to prefer precombined characters when
        // possible
        if unicode_normalization::is_nfc_quick(
            self.contents.iter().copied().take(CODEPOINTS_IN_CELL),
        ) == unicode_normalization::IsNormalized::Yes
        {
            return;
        }

        let mut new_contents = ['\x00'; CODEPOINTS_IN_CELL];
        let mut new_len = 0;
        for c in self
            .contents
            .iter()
            .copied()
            .take(self.len())
            .nfc()
            .take(CODEPOINTS_IN_CELL)
        {
            new_contents[new_len as usize] = c;
            new_len += 1;
        }
        self.contents = new_contents;
        self.len = new_len;
        self.set_wide(new_contents[0].width().unwrap_or(0) > 1);
    }

    pub(crate) fn clear(&mut self, attrs: crate::attrs::Attrs) {
        self.len = 0;
        self.attrs = attrs;
    }

    /// Returns the text contents of the cell.
    ///
    /// Can include multiple unicode characters if combining characters are
    /// used, but will contain at most one character with a non-zero character
    /// width.
    pub fn contents(&self) -> String {
        self.contents.iter().take(self.len()).collect::<String>()
    }

    /// Returns whether the cell contains any text data.
    pub fn has_contents(&self) -> bool {
        self.len > 0
    }

    /// Returns whether the text data in the cell represents a wide character.
    pub fn is_wide(&self) -> bool {
        self.len & 0x80 == 0x80
    }

    fn set_wide(&mut self, wide: bool) {
        if wide {
            self.len |= 0x80;
        } else {
            self.len &= 0x7f;
        }
    }

    pub(crate) fn attrs(&self) -> &crate::attrs::Attrs {
        &self.attrs
    }

    /// Returns the foreground color of the cell.
    pub fn fgcolor(&self) -> crate::attrs::Color {
        self.attrs.fgcolor
    }

    /// Returns the background color of the cell.
    pub fn bgcolor(&self) -> crate::attrs::Color {
        self.attrs.bgcolor
    }

    /// Returns whether the cell should be rendered with the bold text
    /// attribute.
    pub fn bold(&self) -> bool {
        self.attrs.bold()
    }

    /// Returns whether the cell should be rendered with the italic text
    /// attribute.
    pub fn italic(&self) -> bool {
        self.attrs.italic()
    }

    /// Returns whether the cell should be rendered with the underlined text
    /// attribute.
    pub fn underline(&self) -> bool {
        self.attrs.underline()
    }

    /// Returns whether the cell should be rendered with the inverse text
    /// attribute.
    pub fn inverse(&self) -> bool {
        self.attrs.inverse()
    }
}