nvim_gtk/
highlight.rs

1use std::collections::HashMap;
2use std::rc::Rc;
3
4use fnv::FnvHashMap;
5
6use crate::color::*;
7use crate::ui_model::Cell;
8use neovim_lib::Value;
9
10pub struct HighlightMap {
11    highlights: FnvHashMap<u64, Rc<Highlight>>,
12    default_hl: Rc<Highlight>,
13    bg_color: Color,
14    fg_color: Color,
15    sp_color: Color,
16
17    cterm_bg_color: Color,
18    cterm_fg_color: Color,
19    cterm_color: bool,
20
21    pmenu: Rc<Highlight>,
22    pmenu_sel: Rc<Highlight>,
23    cursor: Rc<Highlight>,
24}
25
26impl HighlightMap {
27    pub fn new() -> Self {
28        let default_hl = Rc::new(Highlight::new());
29        HighlightMap {
30            highlights: FnvHashMap::default(),
31            bg_color: COLOR_BLACK,
32            fg_color: COLOR_WHITE,
33            sp_color: COLOR_RED,
34
35            cterm_bg_color: COLOR_BLACK,
36            cterm_fg_color: COLOR_WHITE,
37            cterm_color: false,
38
39            pmenu: default_hl.clone(),
40            pmenu_sel: default_hl.clone(),
41            cursor: default_hl.clone(),
42
43            default_hl,
44        }
45    }
46
47    pub fn default_hl(&self) -> Rc<Highlight> {
48        self.default_hl.clone()
49    }
50
51    pub fn set_defaults(
52        &mut self,
53        fg: Color,
54        bg: Color,
55        sp: Color,
56        cterm_fg: Color,
57        cterm_bg: Color,
58    ) {
59        self.fg_color = fg;
60        self.bg_color = bg;
61        self.sp_color = sp;
62        self.cterm_fg_color = cterm_fg;
63        self.cterm_bg_color = cterm_bg;
64    }
65
66    pub fn set_use_cterm(&mut self, cterm_color: bool) {
67        self.cterm_color = cterm_color;
68    }
69
70    pub fn bg(&self) -> &Color {
71        if self.cterm_color {
72            &self.cterm_bg_color
73        } else {
74            &self.bg_color
75        }
76    }
77
78    pub fn fg(&self) -> &Color {
79        if self.cterm_color {
80            &self.cterm_fg_color
81        } else {
82            &self.fg_color
83        }
84    }
85
86    pub fn get(&self, idx: Option<u64>) -> Rc<Highlight> {
87        idx.and_then(|idx| self.highlights.get(&idx))
88            .map(Rc::clone)
89            .unwrap_or_else(|| {
90                self.highlights
91                    .get(&0)
92                    .map(Rc::clone)
93                    .unwrap_or_else(|| self.default_hl.clone())
94            })
95    }
96
97    pub fn set(&mut self, idx: u64, hl: &HashMap<String, Value>, info: &[HashMap<String, Value>]) {
98        let hl = Rc::new(Highlight::from_value_map(&hl));
99
100        for item in info {
101            match item.get("hi_name").and_then(Value::as_str) {
102                Some("Pmenu") => self.pmenu = hl.clone(),
103                Some("PmenuSel") => self.pmenu_sel = hl.clone(),
104                Some("Cursor") => self.cursor = hl.clone(),
105                _ => (),
106            }
107        }
108
109        self.highlights.insert(idx, hl);
110    }
111
112    pub fn cell_fg<'a>(&'a self, cell: &'a Cell) -> Option<&'a Color> {
113        if !cell.hl.reverse {
114            cell.hl.foreground.as_ref()
115        } else {
116            cell.hl.background.as_ref().or_else(|| Some(self.bg()))
117        }
118    }
119
120    pub fn actual_cell_fg<'a>(&'a self, cell: &'a Cell) -> &'a Color {
121        if !cell.hl.reverse {
122            cell.hl.foreground.as_ref().unwrap_or_else(|| self.fg())
123        } else {
124            cell.hl.background.as_ref().unwrap_or_else(|| self.bg())
125        }
126    }
127
128    pub fn cell_bg<'a>(&'a self, cell: &'a Cell) -> Option<&'a Color> {
129        if !cell.hl.reverse {
130            cell.hl.background.as_ref()
131        } else {
132            cell.hl.foreground.as_ref().or_else(|| Some(self.fg()))
133        }
134    }
135
136    #[inline]
137    pub fn actual_cell_sp<'a>(&'a self, cell: &'a Cell) -> &'a Color {
138        cell.hl.special.as_ref().unwrap_or(&self.sp_color)
139    }
140
141    pub fn pmenu_bg(&self) -> &Color {
142        if !self.pmenu.reverse {
143            self.pmenu.background.as_ref().unwrap_or_else(|| self.bg())
144        } else {
145            self.pmenu.foreground.as_ref().unwrap_or_else(|| self.fg())
146        }
147    }
148
149    pub fn pmenu_fg(&self) -> &Color {
150        if !self.pmenu.reverse {
151            self.pmenu.foreground.as_ref().unwrap_or_else(|| self.fg())
152        } else {
153            self.pmenu.background.as_ref().unwrap_or_else(|| self.bg())
154        }
155    }
156
157    pub fn pmenu_bg_sel(&self) -> &Color {
158        if !self.pmenu_sel.reverse {
159            self.pmenu_sel
160                .background
161                .as_ref()
162                .unwrap_or_else(|| self.bg())
163        } else {
164            self.pmenu_sel
165                .foreground
166                .as_ref()
167                .unwrap_or_else(|| self.fg())
168        }
169    }
170
171    pub fn pmenu_fg_sel(&self) -> &Color {
172        if !self.pmenu_sel.reverse {
173            self.pmenu_sel
174                .foreground
175                .as_ref()
176                .unwrap_or_else(|| self.fg())
177        } else {
178            self.pmenu_sel
179                .background
180                .as_ref()
181                .unwrap_or_else(|| self.bg())
182        }
183    }
184
185    pub fn cursor_bg(&self) -> &Color {
186        if !self.cursor.reverse {
187            self.cursor.background.as_ref().unwrap_or_else(|| self.bg())
188        } else {
189            self.cursor.foreground.as_ref().unwrap_or_else(|| self.fg())
190        }
191    }
192}
193
194#[derive(Clone)]
195pub struct Highlight {
196    pub italic: bool,
197    pub bold: bool,
198    pub underline: bool,
199    pub undercurl: bool,
200    pub strikethrough: bool,
201    pub foreground: Option<Color>,
202    pub background: Option<Color>,
203    pub special: Option<Color>,
204    pub reverse: bool,
205}
206
207impl Highlight {
208    pub fn new() -> Self {
209        Highlight {
210            foreground: None,
211            background: None,
212            special: None,
213            italic: false,
214            bold: false,
215            underline: false,
216            undercurl: false,
217            strikethrough: false,
218            reverse: false,
219        }
220    }
221
222    pub fn from_value_map(attrs: &HashMap<String, Value>) -> Self {
223        let mut model_attrs = Highlight::new();
224
225        for (ref key, ref val) in attrs {
226            match key.as_ref() {
227                "foreground" => {
228                    if let Some(fg) = val.as_u64() {
229                        model_attrs.foreground = Some(Color::from_indexed_color(fg));
230                    }
231                }
232                "background" => {
233                    if let Some(bg) = val.as_u64() {
234                        model_attrs.background = Some(Color::from_indexed_color(bg));
235                    }
236                }
237                "special" => {
238                    if let Some(bg) = val.as_u64() {
239                        model_attrs.special = Some(Color::from_indexed_color(bg));
240                    }
241                }
242                "reverse" => model_attrs.reverse = true,
243                "bold" => model_attrs.bold = true,
244                "italic" => model_attrs.italic = true,
245                "underline" => model_attrs.underline = true,
246                "undercurl" => model_attrs.undercurl = true,
247                "strikethrough" => model_attrs.strikethrough = true,
248                attr_key => error!("unknown attribute {}", attr_key),
249            };
250        }
251
252        model_attrs
253    }
254}