1 /*-
2 * Copyright (c) 2002 Jordan DeLong
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of contributors may be
14 * used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29 #include "vhighlight.h"
30
31 /* color to use to highlight numbers */
32 int vhighlight_numberclr;
33
34 /* keysym for setting vhighlight language */
35 static int vhighlight_setlang = KEYSYM_NOP;
36
37 /* set the line considered valid for highlighting state */
vhighlight_vstate(vhighlight_t * v,line_t * line,int linenum)38 static __inline void vhighlight_vstate(vhighlight_t *v, line_t *line,
39 int linenum) {
40 if (v->valid.linenum > linenum) {
41 v->valid.line = line;
42 v->valid.linenum = linenum;
43 }
44 }
45
vhighlight_drawpart(vhighlight_t * v,line_t ** line,int * linenum,int * col,int maxcols,int * y,int * viewcol)46 static void vhighlight_drawpart(vhighlight_t *v, line_t **line, int *linenum,
47 int *col, int maxcols, int *y, int *viewcol) {
48 anchor_t *anchor;
49 synitem_t *state;
50 int idx, start;
51 int color, topcol;
52 int low, len;
53
54 /* vhighlights without syntax information can't draw */
55 if (!v->syntax) {
56 VIEWCALL(v->hdr.under, drawpart, line, linenum, col,
57 maxcols, y, viewcol);
58 return;
59 }
60
61 topcol = *col + maxcols;
62 draw_setxy(v->vdef, *viewcol, *y);
63
64 /*
65 * find the last valid anchor, if the valid state line is in front
66 * of the current one, we have to scan up to this line in order
67 * to get the state to use.
68 */
69 anchor = anchor_findlast(&v->anchors, min(v->valid.linenum, *linenum));
70 state = anchor ? anchor->state : NULL;
71 while (v->valid.linenum < *linenum) {
72 for (idx = 0; idx < v->valid.line->length;)
73 syntax_highlight(v->syntax, v->valid.line,
74 &idx, &state);
75
76 /*
77 * now this line has a valid state, add an anchor if the
78 * state is different from the previous anchor.
79 */
80 v->valid.line = TAILQ_NEXT(v->valid.line, l_list);
81 v->valid.linenum++;
82 if (!anchor || state != anchor->state)
83 anchor = anchor_add(&v->anchors, v->valid.line,
84 v->valid.linenum, state);
85 }
86
87 /*
88 * draw each portions of the line as the highlighter returns
89 * colors for them.
90 */
91 idx = 0;
92 while (idx < (*line)->length) {
93 start = idx;
94 color = syntax_highlight(v->syntax, *line, &idx, &state);
95 assert(idx <= (*line)->length + 1);
96 if (idx > (*line)->length)
97 idx = (*line)->length;
98 screen_setcolor(color);
99
100 /* draw the section if it's in the part we're drawing */
101 low = max(start, *col);
102 if (low < idx) {
103 len = idx - low;
104 if (*col + len > topcol)
105 len = topcol - *col;
106 draw_string((*line)->text + low, v->hdr.geom.width,
107 len, v->vdef->view.col, viewcol);
108 *col += len;
109 if (*col >= topcol)
110 break;
111 }
112 }
113
114 /* set up new valid state information */
115 if (*col >= (*line)->length) {
116 if (TAILQ_NEXT(*line, l_list)) {
117 v->valid.line = TAILQ_NEXT(*line, l_list);
118 v->valid.linenum = *linenum + 1;
119 if (!anchor || state != anchor->state)
120 anchor_add(&v->anchors, v->valid.line,
121 v->valid.linenum, state);
122 }
123 } else
124 vhighlight_vstate(v, *line, *linenum);
125 }
126
vhighlight_input(vhighlight_t * v,int keysym)127 static void vhighlight_input(vhighlight_t *v, int keysym) {
128 u_char *lang;
129
130 /*
131 * XXX: this should be cleaned up a bit.
132 */
133 if (keysym == vhighlight_setlang) {
134 lang = minibuff_prompt("Language", NULL);
135 if (!lang)
136 goto callunder;
137 anchor_emptylist(&v->anchors);
138 v->valid.line = TAILQ_FIRST(&v->hdr.buffer->lines);
139 v->valid.linenum = 0;
140 v->syntax = syntax_findlang(lang);
141 v->vdef->redraw_text = 1;
142 free(lang);
143 return;
144 }
145
146 callunder:
147 VIEWCALL(v->hdr.under, input, keysym);
148 }
149
150 /*
151 * detect what language should be used to highlight for;
152 * use a few simple rules to detect most cases:
153 *
154 * - check file extensions for matches with the languages
155 * - check the first line for a #! /interpreter syntax
156 * - check for a Emacs-style -*- language -*- on the first line
157 */
detectlang(buffer_t * buffer)158 static syntax_t *detectlang(buffer_t *buffer) {
159 syntax_t *syntax;
160 u_char *lang, *s;
161 line_t *first;
162 int len, i;
163
164 /* extensions */
165 lang = strrchr(buffer->name, '.');
166 if (lang && (syntax = syntax_findlang(lang + 1)) != NULL)
167 return syntax;
168
169 /* #! interpreter */
170 syntax = NULL;
171 first = TAILQ_FIRST(&buffer->lines);
172 if (first->text[0] == '#' && first->text[1] == '!') {
173 lang = first->text;
174 len = first->length;
175 while ((s = memchr(lang, '/', len - 1)) != NULL) {
176 s++;
177 len -= s - lang;
178 lang = s;
179 }
180 if (lang == first->text)
181 goto out;
182
183 /* make a copy so we can play with it */
184 s = ckmalloc(len + 1);
185 memcpy(s, lang, len);
186 lang = s;
187 lang[len] = '\0';
188
189 /* special case for 'env', use what we're enving */
190 if (!memcmp("env", lang, min(3, len))) {
191 lang += 3;
192 lang += strspn(lang, " \t");
193 if (!*lang)
194 goto free;
195 }
196
197 /* put a null at the first whitespace */
198 for (i = 0; lang[i]; i++)
199 if (lang[i] == ' ' || lang[i] == '\t') {
200 lang[i] = '\0';
201 break;
202 }
203
204 syntax = syntax_findlang(lang);
205 free:
206 free(s);
207 if (syntax)
208 return syntax;
209 }
210 out:
211
212 /* emacs-style -*- language -*- on the first line */
213 syntax = NULL;
214 if ((lang = strnstr(first->text, "-*-", first->length)) != NULL) {
215 lang += 3;
216 len = first->length - (lang - first->text);
217 s = ckmalloc(len + 1);
218 memcpy(s, lang, len);
219 lang = strnstr(s, "-*-", len);
220 if (!lang)
221 goto free2;
222 *lang = '\0';
223 lang = s + strspn(s, " \t");
224
225 /* put a null at the first whitespace */
226 for (i = 0; lang[i]; i++)
227 if (lang[i] == ' ' || lang[i] == '\t') {
228 lang[i] = '\0';
229 break;
230 }
231
232 syntax = syntax_findlang(lang);
233 free2:
234 free(s);
235 if (syntax)
236 return syntax;
237 }
238
239 return NULL;
240 }
241
vhighlight_map(vhighlight_t * v)242 static void vhighlight_map(vhighlight_t *v) {
243 v->syntax = detectlang(v->hdr.buffer);
244 anchor_emptylist(&v->anchors);
245 v->valid.line = TAILQ_FIRST(&v->hdr.buffer->lines);
246 v->valid.linenum = 0;
247
248 v->vdef->redraw_header = 1;
249 v->vdef->redraw_text = 1;
250 }
251
vhighlight_rmline(vhighlight_t * v,line_t * line,int linenum)252 static void vhighlight_rmline(vhighlight_t *v, line_t *line, int linenum) {
253 anchor_findlast(&v->anchors, linenum);
254 if (TAILQ_PREV(line, linelist, l_list))
255 vhighlight_vstate(v, TAILQ_PREV(line, linelist,
256 l_list), linenum - 1);
257 else {
258 /*
259 * line being removed is the first line in the buffer,
260 * so set state to valid on the next line (which will
261 * become the first line) with number at 0.
262 */
263 v->valid.line = TAILQ_NEXT(line, l_list);
264 v->valid.linenum = 0;
265 }
266 VIEWCALL(v->hdr.under, rmline, line, linenum);
267 }
268
vhighlight_rmstr(vhighlight_t * v,line_t * line,int linenum,int loc,int slen)269 static void vhighlight_rmstr(vhighlight_t *v, line_t *line, int linenum,
270 int loc, int slen) {
271 anchor_findlast(&v->anchors, linenum);
272 vhighlight_vstate(v, line, linenum);
273 VIEWCALL(v->hdr.under, rmstr, line, linenum, loc, slen);
274 }
275
vhighlight_addline(vhighlight_t * v,line_t * line,int linenum)276 static void vhighlight_addline(vhighlight_t *v, line_t *line, int linenum) {
277 anchor_findlast(&v->anchors, linenum);
278 vhighlight_vstate(v, line, linenum);
279 VIEWCALL(v->hdr.under, addline, line, linenum);
280 }
281
vhighlight_stradd(vhighlight_t * v,line_t * line,int linenum,int loc,int slen)282 static void vhighlight_stradd(vhighlight_t *v, line_t *line, int linenum,
283 int loc, int slen) {
284 anchor_findlast(&v->anchors, linenum);
285 vhighlight_vstate(v, line, linenum);
286 VIEWCALL(v->hdr.under, stradd, line, linenum, loc, slen);
287 }
288
vhighlight_destroy(vhighlight_t * v)289 static void vhighlight_destroy(vhighlight_t *v) {
290 anchor_emptylist(&v->anchors);
291 free(v);
292 }
293
vhighlight_create(viewhdr_t * under)294 static viewhdr_t *vhighlight_create(viewhdr_t *under) {
295 vhighlight_t *vhighlight;
296
297 /* create the vhighlight structure */
298 vhighlight = VIEW_CREATE(vhighlight_t, "vhighlight", under);
299 vhighlight->vdef = view_findvdef((viewhdr_t *) vhighlight);
300 TAILQ_INIT(&vhighlight->anchors);
301
302 /* set view operations */
303 vhighlight->hdr.op.draw = NULL;
304 vhighlight->hdr.op.drawpart = VIEWOP(vhighlight_drawpart);
305 vhighlight->hdr.op.viswid = NULL;
306 vhighlight->hdr.op.drawhdr = NULL;
307 vhighlight->hdr.op.input = VIEWOP(vhighlight_input);
308 vhighlight->hdr.op.map = VIEWOP(vhighlight_map);
309 vhighlight->hdr.op.geomchange = NULL;
310 vhighlight->hdr.op.activate = NULL;
311 vhighlight->hdr.op.rmline = VIEWOP(vhighlight_rmline);
312 vhighlight->hdr.op.rmstr = VIEWOP(vhighlight_rmstr);
313 vhighlight->hdr.op.addline = VIEWOP(vhighlight_addline);
314 vhighlight->hdr.op.stradd = VIEWOP(vhighlight_stradd);
315 vhighlight->hdr.op.destroy = VIEWOP(vhighlight_destroy);
316
317 return (viewhdr_t *) vhighlight;
318 }
319
320 /* type operations for the highlighter view */
321 static viewtypeops_t vhighlight_typeops = {
322 vhighlight_create
323 };
324
325 /* register the view type for the highlighter view */
vhighlight_init()326 int vhighlight_init() {
327 vhighlight_setlang = input_allocsyms(1);
328 options_add("vhighlight_numberclr", OPT_COLOR,
329 &vhighlight_numberclr, 1);
330 vhighlight_numberclr = color_lookup("number");
331 if (vhighlight_numberclr == COLOR_NULL)
332 vhighlight_numberclr = COLOR_DEFAULT;
333 command_add("vhighlight_setlang", vhighlight_setlang);
334 VIEW_ADDTYPE(vhighlight_typeops, "vhighlight");
335 syntax_init();
336
337 return 0;
338 }
339
340 /* free up memory */
vhighlight_shutdown()341 void vhighlight_shutdown() {
342 syntax_shutdown();
343 anchor_shutdown();
344 command_remove("vhighlight_setlang");
345 view_rmtype("vhighlight");
346 }
347