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}