1 /*
2 Copyright © 2013 Free Software Foundation, Inc
3 See licensing in LICENSE file
4
5 File: examples/ex_6.rs
6 Author: Jesse 'Jeaye' Wilkerson
7 Description:
8 Extension of ex_3 that adds naive syntax
9 highlighting to showcase attributes.
10
11 Usage:
12 ./bin/ex_6 <rust file>
13 Example:
14 ./bin/ex_6 examples/ex_6.rs
15 */
16
17 extern crate ncurses;
18
19 use std::{ char, env, fs };
20 use std::path::Path;
21 use std::io::{Read, Bytes};
22 use std::iter::Peekable;
23 use ncurses::*;
24
25 /* Individual color handles. */
26 static COLOR_BACKGROUND: i16 = 16;
27 static COLOR_FOREGROUND: i16 = 17;
28 static COLOR_KEYWORD: i16 = 18;
29 static COLOR_TYPE: i16 = 19;
30 static COLOR_STORAGE: i16 = 20;
31 static COLOR_COMMENT: i16 = 21;
32 static COLOR_STRING: i16 = 22;
33 static COLOR_CHAR: i16 = 23;
34 static COLOR_NUMBER: i16 = 24;
35
36 /* Color pairs; foreground && background. */
37 static COLOR_PAIR_DEFAULT: i16 = 1;
38 static COLOR_PAIR_KEYWORD: i16 = 2;
39 static COLOR_PAIR_TYPE: i16 = 3;
40 static COLOR_PAIR_STORAGE: i16 = 4;
41 static COLOR_PAIR_COMMENT: i16 = 5;
42 static COLOR_PAIR_STRING: i16 = 6;
43 static COLOR_PAIR_CHAR: i16 = 7;
44 static COLOR_PAIR_NUMBER: i16 = 8;
45
46 /* Word delimiters. */
47 static WORD_LIMITS: &'static [u8] = &
48 [
49 ' ' as u8,
50 '(' as u8,
51 ')' as u8,
52 ':' as u8,
53 ';' as u8,
54 '&' as u8,
55 '+' as u8,
56 '-' as u8,
57 ',' as u8,
58 '.' as u8,
59 '@' as u8,
60 '~' as u8,
61 '\\' as u8,
62 '\n' as u8,
63 '\r' as u8,
64 '\0' as u8,
65 !0 as u8,
66 ];
67
68 struct Pager
69 {
70 file_reader: Peekable<Bytes<fs::File>>,
71
72 in_comment: bool,
73 in_string: bool,
74 in_char: bool,
75
76 screen_width: i32,
77 screen_height: i32,
78 curr_x: i32,
79 curr_y: i32,
80 }
81
82 impl Pager
83 {
new() -> Pager84 pub fn new() -> Pager
85 {
86 Pager
87 {
88 file_reader: open_file().bytes().peekable(),
89
90 in_comment: false,
91 in_string: false,
92 in_char: false,
93
94 screen_width: 0,
95 screen_height: 0,
96 curr_x: 0,
97 curr_y: 0,
98 }
99 }
100
initialize(&mut self)101 pub fn initialize(&mut self)
102 {
103 /* Start ncurses. */
104 initscr();
105 keypad(stdscr(), true);
106 noecho();
107
108 /* Start colors. */
109 start_color();
110 init_color(COLOR_BACKGROUND, 0, 43 * 4, 54 * 4);
111 init_color(COLOR_FOREGROUND, 142 * 4, 161 * 4, 161 * 4);
112 init_color(COLOR_KEYWORD, 130 * 4, 151 * 4, 0);
113 init_color(COLOR_TYPE, 197 * 4, 73 * 4, 27 * 4);
114 init_color(COLOR_STORAGE, 219 * 4, 51 * 4, 47 * 4);
115 init_color(COLOR_COMMENT, 33 * 4, 138 * 4, 206 * 4);
116 init_color(COLOR_STRING, 34 * 4, 154 * 4, 142 * 4);
117 init_color(COLOR_CHAR, 34 * 4, 154 * 4, 142 * 4);
118 init_color(COLOR_NUMBER, 236 * 4, 107 * 4, 83 * 4);
119
120 init_pair(COLOR_PAIR_DEFAULT, COLOR_FOREGROUND, COLOR_BACKGROUND);
121 init_pair(COLOR_PAIR_KEYWORD, COLOR_KEYWORD, COLOR_BACKGROUND);
122 init_pair(COLOR_PAIR_TYPE, COLOR_TYPE, COLOR_BACKGROUND);
123 init_pair(COLOR_PAIR_STORAGE, COLOR_STORAGE, COLOR_BACKGROUND);
124 init_pair(COLOR_PAIR_COMMENT, COLOR_COMMENT, COLOR_BACKGROUND);
125 init_pair(COLOR_PAIR_STRING, COLOR_STRING, COLOR_BACKGROUND);
126 init_pair(COLOR_PAIR_CHAR, COLOR_CHAR, COLOR_BACKGROUND);
127 init_pair(COLOR_PAIR_NUMBER, COLOR_NUMBER, COLOR_BACKGROUND);
128
129 /* Set the window's background color. */
130 bkgd(' ' as chtype | COLOR_PAIR(COLOR_PAIR_DEFAULT) as chtype);
131
132 /* Get the screen bounds. */
133 getmaxyx(stdscr(), &mut self.screen_height, &mut self.screen_width);
134 }
135
136 /* Returns the word and delimiter following it. */
read_word(&mut self) -> (String, char)137 pub fn read_word(&mut self) -> (String, char)
138 {
139 let mut s: Vec<u8> = vec![];
140 let mut ch : u8 = self.file_reader.next().unwrap().ok().expect("Unable to read byte");
141
142 /* Read until we hit a word delimiter. */
143 while !WORD_LIMITS.contains(&ch)
144 {
145 s.push(ch);
146 ch = self.file_reader.next().unwrap().ok().expect("Unable to read byte");
147 }
148
149 /* Return the word string and the terminating delimiter. */
150 match char::from_u32(ch as u32)
151 {
152 Some(ch) => (String::from_utf8(s).ok().expect("utf-8 conversion failed"), ch),
153 None => (String::from_utf8(s).ok().expect("utf-8 conversion failed"), ' '),
154 }
155 }
156
157 /* Retuns the attribute the given word requires. */
highlight_word(&mut self, word: &str) -> attr_t158 pub fn highlight_word(&mut self, word: &str) -> attr_t
159 {
160 /* Comments. */
161 if self.in_comment && !word.contains("*/")
162 { return COLOR_PAIR(COLOR_PAIR_COMMENT); }
163 else if self.in_comment && word.contains("*/")
164 {
165 self.in_comment = false;
166 return COLOR_PAIR(COLOR_PAIR_COMMENT);
167 }
168 else if !self.in_comment && word.contains("/*")
169 {
170 self.in_comment = true;
171 return COLOR_PAIR(COLOR_PAIR_COMMENT);
172 }
173
174 /* Strings. */
175 if !self.in_char
176 {
177 if self.in_string && !word.contains("\"")
178 { return COLOR_PAIR(COLOR_PAIR_STRING); }
179 else if self.in_string && word.contains("\"")
180 {
181 self.in_string = false;
182 return COLOR_PAIR(COLOR_PAIR_STRING);
183 }
184 else if !self.in_string && word.contains("\"")
185 {
186 /* If the same quote is found from either direction
187 * then it's the only quote in the string. */
188 if word.find('\"') == word.rfind('\"')
189 { self.in_string = true; }
190 return COLOR_PAIR(COLOR_PAIR_STRING);
191 }
192 }
193
194 /* Chars. */
195 if self.in_char && !word.contains("\'")
196 { return COLOR_PAIR(COLOR_PAIR_CHAR); }
197 else if self.in_char && word.contains("\'")
198 {
199 self.in_char = false;
200 return COLOR_PAIR(COLOR_PAIR_CHAR);
201 }
202 else if !self.in_char && word.contains("\'") && !word.contains("static")
203 {
204 /* If the same quote is found from either direction
205 * then it's the only quote in the string. */
206 if word.find('\'') == word.rfind('\'')
207 { self.in_char = true; }
208 return COLOR_PAIR(COLOR_PAIR_CHAR);
209 }
210
211 /* Trim the word of all delimiters. */
212 let word = word.trim_matches(|ch: char|
213 { WORD_LIMITS.contains(&(ch as u8)) });
214
215 if word.len() == 0
216 { return 0; }
217
218 /* If it starts with a number, it is a number. */
219 if word.as_bytes()[0] >= '0' as u8 && word.as_bytes()[0] <= '9' as u8
220 { return COLOR_PAIR(COLOR_PAIR_NUMBER); }
221
222 match word
223 {
224 /* Key words. */
225 "break" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
226 "continue" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
227 "do" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
228 "else" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
229 "extern" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
230 "in" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
231 "if" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
232 "impl" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
233 "let" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
234 "log" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
235 "loop" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
236 "match" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
237 "once" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
238 "priv" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
239 "pub" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
240 "return" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
241 "unsafe" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
242 "while" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
243 "use" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
244 "mod" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
245 "trait" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
246 "struct" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
247 "enum" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
248 "type" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
249 "fn" => { COLOR_PAIR(COLOR_PAIR_KEYWORD) },
250
251 /* Types. */
252 "int" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
253 "uint" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
254 "char" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
255 "bool" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
256 "u8" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
257 "u16" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
258 "u32" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
259 "u64" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
260 "i16" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
261 "i32" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
262 "i64" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
263 "f32" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
264 "f64" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
265 "str" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
266 "self" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
267 "Self" => { COLOR_PAIR(COLOR_PAIR_TYPE) },
268
269 /* Storage. */
270 "const" => { COLOR_PAIR(COLOR_PAIR_STORAGE) },
271 "mut" => { COLOR_PAIR(COLOR_PAIR_STORAGE) },
272 "ref" => { COLOR_PAIR(COLOR_PAIR_STORAGE) },
273 "static" => { COLOR_PAIR(COLOR_PAIR_STORAGE) },
274
275 /* Not something we need to highlight. */
276 _ => 0,
277 }
278 }
279
280 }
281
282 impl Drop for Pager
283 {
drop(&mut self)284 fn drop(&mut self)
285 {
286 /* Final prompt before closing. */
287 mv(self.screen_height - 1, 0);
288 prompt();
289 endwin();
290 }
291 }
292
main()293 fn main()
294 {
295 let mut pager = Pager::new();
296 pager.initialize();
297
298 /* Read the whole file. */
299 while pager.file_reader.peek().is_some()
300 {
301 /* Read a word at a time. */
302 let (word, leftover) = pager.read_word();
303 let attr = pager.highlight_word(word.as_ref());
304 let leftover_attr = pager.highlight_word(format!("{}", leftover).as_ref());
305
306 /* Get the current position on the screen. */
307 getyx(stdscr(), &mut pager.curr_y, &mut pager.curr_x);
308
309 if pager.curr_y == (pager.screen_height - 1)
310 {
311 /* Status bar at the bottom. */
312 prompt();
313
314 /* Once a key is pressed, clear the screen and continue. */
315 clear();
316 mv(0, 0);
317 }
318 else
319 {
320 attron(attr);
321 printw(word.as_ref());
322 attroff(attr);
323
324 attron(leftover_attr);
325 addch(leftover as chtype);
326 attroff(leftover_attr);
327 }
328 }
329 }
330
prompt()331 fn prompt()
332 {
333 attron(A_BOLD());
334 printw("<-Press Space->");
335 while getch() != ' ' as i32
336 { }
337 attroff(A_BOLD());
338 }
339
open_file() -> fs::File340 fn open_file() -> fs::File
341 {
342 let args : Vec<_> = env::args().collect();
343 if args.len() != 2
344 {
345 println!("Usage:\n\t{} <rust file>", args[0]);
346 println!("Example:\n\t{} examples/ex_5.rs", args[0]);
347 panic!("Exiting");
348 }
349
350 let reader = fs::File::open(Path::new(&args[1]));
351 reader.ok().expect("Unable to open file")
352 }
353