1 /*******************************************************************************
2 * *
3 * highlight.c -- Nirvana Editor syntax highlighting (text coloring and font *
4 * selected by file content *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
13 * *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * *
23 * Nirvana Text Editor *
24 * June 24, 1996 *
25 * *
26 * Written by Mark Edel *
27 * *
28 *******************************************************************************/
29
30 #ifdef HAVE_CONFIG_H
31 #include "../config.h"
32 #endif
33
34 #include "highlight.h"
35 #include "textBuf.h"
36 #include "textDisp.h"
37 #include "text.h"
38 #include "textP.h"
39 #include "nedit.h"
40 #include "regularExp.h"
41 #include "highlightData.h"
42 #include "preferences.h"
43 #include "window.h"
44 #include "../util/misc.h"
45 #include "../util/DialogF.h"
46 #include "../util/nedit_malloc.h"
47
48 #include <stdio.h>
49 #include <limits.h>
50 #include <math.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #ifdef VMS
54 #include "../util/VMSparam.h"
55 #else
56 #ifndef __MVS__
57 #include <sys/param.h>
58 #endif
59 #endif /*VMS*/
60 #include <inttypes.h>
61
62 #include <Xm/Xm.h>
63 #include <Xm/XmP.h>
64 #if XmVersion >= 1002
65 #include <Xm/PrimitiveP.h>
66 #endif
67
68 #ifdef HAVE_DEBUG_H
69 #include "../debug.h"
70 #endif
71
72 /* How much re-parsing to do when an unfinished style is encountered */
73 #define PASS_2_REPARSE_CHUNK_SIZE 1000
74
75 /* Initial forward expansion of parsing region in incremental reparsing,
76 when style changes propagate forward beyond the original modification.
77 This distance is increased by a factor of two for each subsequent step. */
78 #define REPARSE_CHUNK_SIZE 80
79
80 /* Meanings of style buffer characters (styles). Don't use plain 'A' or 'B';
81 it causes problems with EBCDIC coding (possibly negative offsets when
82 subtracting 'A'). */
83 #define UNFINISHED_STYLE ASCII_A
84 #define PLAIN_STYLE (ASCII_A+1)
85 #define IS_PLAIN(style) (style == PLAIN_STYLE || style == UNFINISHED_STYLE)
86 #define IS_STYLED(style) (style != PLAIN_STYLE && style != UNFINISHED_STYLE)
87
88 /* Compare two styles where one of the styles may not yet have been processed
89 with pass2 patterns */
90 #define EQUIVALENT_STYLE(style1, style2, firstPass2Style) (style1 == style2 || \
91 (style1 == UNFINISHED_STYLE && \
92 (style2 == PLAIN_STYLE || (unsigned char)style2 >= firstPass2Style)) || \
93 (style2 == UNFINISHED_STYLE && \
94 (style1 == PLAIN_STYLE || (unsigned char)style1 >= firstPass2Style)))
95
96 /* Scanning context can be reduced (with big efficiency gains) if we
97 know that patterns can't cross line boundaries, which is implied
98 by a context requirement of 1 line and 0 characters */
99 #define CAN_CROSS_LINE_BOUNDARIES(contextRequirements) \
100 (contextRequirements->nLines != 1 || contextRequirements->nChars != 0)
101
102 /* "Compiled" version of pattern specification */
103 typedef struct _highlightDataRec {
104 regexp *startRE;
105 regexp *endRE;
106 regexp *errorRE;
107 regexp *subPatternRE;
108 char style;
109 int colorOnly;
110 signed char startSubexprs[NSUBEXP+1];
111 signed char endSubexprs[NSUBEXP+1];
112 int flags;
113 int nSubPatterns;
114 int nSubBranches; /* Number of top-level branches of subPatternRE */
115 int userStyleIndex;
116 struct _highlightDataRec **subPatterns;
117 } highlightDataRec;
118
119 /* Context requirements for incremental reparsing of a pattern set */
120 typedef struct {
121 int nLines;
122 int nChars;
123 } reparseContext;
124
125 /* Data structure attached to window to hold all syntax highlighting
126 information (for both drawing and incremental reparsing) */
127 typedef struct {
128 highlightDataRec *pass1Patterns;
129 highlightDataRec *pass2Patterns;
130 char *parentStyles;
131 reparseContext contextRequirements;
132 styleTableEntry *styleTable;
133 int nStyles;
134 textBuffer *styleBuffer;
135 patternSet *patternSetForWindow;
136 } windowHighlightData;
137
138 static windowHighlightData *createHighlightData(WindowInfo *window,
139 patternSet *patSet);
140 static void freeHighlightData(windowHighlightData *hd);
141 static patternSet *findPatternsForWindow(WindowInfo *window, int warn);
142 static highlightDataRec *compilePatterns(Widget dialogParent,
143 highlightPattern *patternSrc, int nPatterns);
144 static void freePatterns(highlightDataRec *patterns);
145 static void handleUnparsedRegion(const WindowInfo* win, textBuffer* styleBuf,
146 int pos);
147 static void handleUnparsedRegionCB(const textDisp* textD, int pos,
148 const void* cbArg);
149 static void incrementalReparse(windowHighlightData *highlightData,
150 textBuffer *buf, int pos, int nInserted, const char *delimiters);
151 static int parseBufferRange(highlightDataRec *pass1Patterns,
152 highlightDataRec *pass2Patterns, textBuffer *buf, textBuffer *styleBuf,
153 reparseContext *contextRequirements, int beginParse, int endParse,
154 const char *delimiters);
155 static int parseString(highlightDataRec *pattern, const char **string,
156 char **styleString, int length, char *prevChar, int anchored,
157 const char *delimiters, const char* lookBehindTo, const char* match_till);
158 static void passTwoParseString(highlightDataRec *pattern, char *string,
159 char *styleString, int length, char *prevChar, const char *delimiters,
160 const char* lookBehindTo, const char* match_till);
161 static void fillStyleString(const char **stringPtr, char **stylePtr,
162 const char *toPtr, char style, char *prevChar);
163 static void modifyStyleBuf(textBuffer *styleBuf, char *styleString,
164 int startPos, int endPos, int firstPass2Style);
165 static int lastModified(textBuffer *styleBuf);
166 static int max(int i1, int i2);
167 static int min(int i1, int i2);
168 static char getPrevChar(textBuffer *buf, int pos);
169 static regexp *compileREAndWarn(Widget parent, const char *re);
170 static int parentStyleOf(const char *parentStyles, int style);
171 static int isParentStyle(const char *parentStyles, int style1, int style2);
172 static int findSafeParseRestartPos(textBuffer *buf,
173 windowHighlightData *highlightData, int *pos);
174 static int backwardOneContext(textBuffer *buf, reparseContext *context,
175 int fromPos);
176 static int forwardOneContext(textBuffer *buf, reparseContext *context,
177 int fromPos);
178 static void recolorSubexpr(regexp *re, int subexpr, int style,
179 const char *string, char *styleString);
180 static int indexOfNamedPattern(highlightPattern *patList, int nPats,
181 const char *patName);
182 static int findTopLevelParentIndex(highlightPattern *patList, int nPats,
183 int index);
184 static highlightDataRec *patternOfStyle(highlightDataRec *patterns, int style);
185 static void updateWindowHeight(WindowInfo *window, int oldFontHeight);
186 static int getFontHeight(WindowInfo *window);
187 static styleTableEntry *styleTableEntryOfCode(WindowInfo *window, int hCode);
188
189 /*
190 ** Buffer modification callback for triggering re-parsing of modified
191 ** text and keeping the style buffer synchronized with the text buffer.
192 ** This must be attached to the the text buffer BEFORE any widget text
193 ** display callbacks, so it can get the style buffer ready to be used
194 ** by the text display routines.
195 **
196 ** Update the style buffer for changes to the text, and mark any style
197 ** changes by selecting the region in the style buffer. This strange
198 ** protocol of informing the text display to redraw style changes by
199 ** making selections in the style buffer is used because this routine
200 ** is intended to be called BEFORE the text display callback paints the
201 ** text (to minimize redraws and, most importantly, to synchronize the
202 ** style buffer with the text buffer). If we redraw now, the text
203 ** display hasn't yet processed the modification, redrawing later is
204 ** not only complicated, it will double-draw almost everything typed.
205 **
206 ** Note: This routine must be kept efficient. It is called for every
207 ** character typed.
208 */
SyntaxHighlightModifyCB(int pos,int nInserted,int nDeleted,int nRestyled,const char * deletedText,void * cbArg)209 void SyntaxHighlightModifyCB(int pos, int nInserted, int nDeleted,
210 int nRestyled, const char *deletedText, void *cbArg)
211 {
212 WindowInfo *window = (WindowInfo *)cbArg;
213 windowHighlightData
214 *highlightData = (windowHighlightData *)window->highlightData;
215
216 if (highlightData == NULL)
217 return;
218
219 /* Restyling-only modifications (usually a primary or secondary selection)
220 don't require any processing, but clear out the style buffer selection
221 so the widget doesn't think it has to keep redrawing the old area */
222 if (nInserted == 0 && nDeleted == 0) {
223 BufUnselect(highlightData->styleBuffer);
224 return;
225 }
226
227 /* First and foremost, the style buffer must track the text buffer
228 accurately and correctly */
229 if (nInserted > 0) {
230 char *insStyle;
231 int i;
232
233 insStyle = (char*)NEditMalloc(nInserted + 1);
234 for (i=0; i<nInserted; i++)
235 insStyle[i] = UNFINISHED_STYLE;
236 insStyle[i] = '\0';
237 BufReplace(highlightData->styleBuffer, pos, pos+nDeleted, insStyle);
238 NEditFree(insStyle);
239 } else {
240 BufRemove(highlightData->styleBuffer, pos, pos+nDeleted);
241 }
242
243 /* Mark the changed region in the style buffer as requiring redraw. This
244 is not necessary for getting it redrawn, it will be redrawn anyhow by
245 the text display callback, but it clears the previous selection and
246 saves the modifyStyleBuf routine from unnecessary work in tracking
247 changes that are already scheduled for redraw */
248 BufSelect(highlightData->styleBuffer, pos, pos+nInserted);
249
250 /* Re-parse around the changed region */
251 if (highlightData->pass1Patterns)
252 incrementalReparse(highlightData, window->buffer, pos, nInserted,
253 GetWindowDelimiters(window));
254 }
255
256 /*
257 ** Turn on syntax highlighting. If "warn" is true, warn the user when it
258 ** can't be done, otherwise, just return.
259 */
StartHighlighting(WindowInfo * window,int warn)260 void StartHighlighting(WindowInfo *window, int warn)
261 {
262 patternSet *patterns;
263 windowHighlightData *highlightData;
264 char *stylePtr, *styleString;
265 const char *stringPtr, *bufString;
266 char prevChar = '\0';
267 int i, oldFontHeight;
268
269 /* Find the pattern set matching the window's current
270 language mode, tell the user if it can't be done */
271 patterns = findPatternsForWindow(window, warn);
272 if (patterns == NULL)
273 return;
274
275 /* Compile the patterns */
276 highlightData = createHighlightData(window, patterns);
277 if (highlightData == NULL)
278 return;
279
280 /* Prepare for a long delay, refresh display and put up a watch cursor */
281 BeginWait(window->shell);
282 XmUpdateDisplay(window->shell);
283
284 /* Parse the buffer with pass 1 patterns. If there are none, initialize
285 the style buffer to all UNFINISHED_STYLE to trigger parsing later */
286 stylePtr = styleString = (char*)NEditMalloc(window->buffer->length + 1);
287 if (highlightData->pass1Patterns == NULL) {
288 for (i=0; i<window->buffer->length; i++)
289 *stylePtr++ = UNFINISHED_STYLE;
290 } else {
291 stringPtr = bufString = BufAsString(window->buffer);
292 parseString(highlightData->pass1Patterns, &stringPtr, &stylePtr,
293 window->buffer->length, &prevChar, False,
294 GetWindowDelimiters(window), bufString, NULL);
295 }
296 *stylePtr = '\0';
297 BufSetAll(highlightData->styleBuffer, styleString);
298 NEditFree(styleString);
299
300 /* install highlight pattern data in the window data structure */
301 window->highlightData = highlightData;
302
303 /* Get the height of the current font in the window, to be used after
304 highlighting is turned on to resize the window to make room for
305 additional highlight fonts which may be sized differently */
306 oldFontHeight = getFontHeight(window);
307
308 /* Attach highlight information to text widgets in each pane */
309 AttachHighlightToWidget(window->textArea, window);
310 for (i=0; i<window->nPanes; i++)
311 AttachHighlightToWidget(window->textPanes[i], window);
312
313 /* Re-size the window to fit the highlight fonts properly & tell the
314 window manager about the potential line-height change as well */
315 updateWindowHeight(window, oldFontHeight);
316 UpdateWMSizeHints(window);
317 UpdateMinPaneHeights(window);
318
319 /* Make sure that if the window has grown, the additional area gets
320 repainted. Otherwise, it is possible that the area gets moved before a
321 repaint event is received and the area doesn't get repainted at all
322 (eg. because of a -line command line argument that moves the text). */
323 XmUpdateDisplay(window->shell);
324 EndWait(window->shell);
325 }
326
327 /*
328 ** Turn off syntax highlighting and free style buffer, compiled patterns, and
329 ** related data.
330 */
StopHighlighting(WindowInfo * window)331 void StopHighlighting(WindowInfo *window)
332 {
333 int i, oldFontHeight;
334
335 if (window->highlightData==NULL)
336 return;
337
338 /* Get the line height being used by the highlight fonts in the window,
339 to be used after highlighting is turned off to resize the window
340 back to the line height of the primary font */
341 oldFontHeight = getFontHeight(window);
342
343 /* Free and remove the highlight data from the window */
344 freeHighlightData((windowHighlightData *)window->highlightData);
345 window->highlightData = NULL;
346
347 /* Remove and detach style buffer and style table from all text
348 display(s) of window, and redisplay without highlighting */
349 RemoveWidgetHighlight(window->textArea);
350 for (i=0; i<window->nPanes; i++)
351 RemoveWidgetHighlight(window->textPanes[i]);
352
353 /* Re-size the window to fit the primary font properly & tell the window
354 manager about the potential line-height change as well */
355 updateWindowHeight(window, oldFontHeight);
356 UpdateWMSizeHints(window);
357 UpdateMinPaneHeights(window);
358 }
359
360 /*
361 ** Free highlighting data from a window destined for destruction, without
362 ** redisplaying.
363 */
FreeHighlightingData(WindowInfo * window)364 void FreeHighlightingData(WindowInfo *window)
365 {
366 int i;
367
368 if (window->highlightData == NULL)
369 return;
370
371 /* Free and remove the highlight data from the window */
372 freeHighlightData((windowHighlightData *)window->highlightData);
373 window->highlightData = NULL;
374
375 /* The text display may make a last desperate attempt to access highlight
376 information when it is destroyed, which would be a disaster. */
377 ((TextWidget)window->textArea)->text.textD->styleBuffer = NULL;
378 for (i=0; i<window->nPanes; i++)
379 ((TextWidget)window->textPanes[i])->text.textD->styleBuffer = NULL;
380 }
381
382 /*
383 ** Attach style information from a window's highlight data to a
384 ** text widget and redisplay.
385 */
AttachHighlightToWidget(Widget widget,WindowInfo * window)386 void AttachHighlightToWidget(Widget widget, WindowInfo *window)
387 {
388 windowHighlightData *highlightData =
389 (windowHighlightData *)window->highlightData;
390
391 TextDAttachHighlightData(((TextWidget)widget)->text.textD,
392 highlightData->styleBuffer, highlightData->styleTable,
393 highlightData->nStyles, UNFINISHED_STYLE, handleUnparsedRegionCB,
394 window);
395 }
396
397 /*
398 ** Remove style information from a text widget and redisplay it.
399 */
RemoveWidgetHighlight(Widget widget)400 void RemoveWidgetHighlight(Widget widget)
401 {
402 TextDAttachHighlightData(((TextWidget)widget)->text.textD,
403 NULL, NULL, 0, UNFINISHED_STYLE, NULL, NULL);
404 }
405
406 /*
407 ** Change highlight fonts and/or styles in a highlighted window, without
408 ** re-parsing.
409 */
UpdateHighlightStyles(WindowInfo * window)410 void UpdateHighlightStyles(WindowInfo *window)
411 {
412 patternSet *patterns;
413 windowHighlightData *highlightData;
414 windowHighlightData *oldHighlightData =
415 (windowHighlightData *)window->highlightData;
416 textBuffer *styleBuffer;
417 int i;
418
419 /* Do nothing if window not highlighted */
420 if (window->highlightData == NULL)
421 return;
422
423 /* Find the pattern set for the window's current language mode */
424 patterns = findPatternsForWindow(window, False);
425 if (patterns == NULL) {
426 StopHighlighting(window);
427 return;
428 }
429
430 /* Build new patterns */
431 highlightData = createHighlightData(window, patterns);
432 if (highlightData == NULL) {
433 StopHighlighting(window);
434 return;
435 }
436
437 /* Update highlight pattern data in the window data structure, but
438 preserve all of the effort that went in to parsing the buffer
439 by swapping it with the empty one in highlightData (which is then
440 freed in freeHighlightData) */
441 styleBuffer = oldHighlightData->styleBuffer;
442 oldHighlightData->styleBuffer = highlightData->styleBuffer;
443 freeHighlightData(oldHighlightData);
444 highlightData->styleBuffer = styleBuffer;
445 window->highlightData = highlightData;
446
447 /* Attach new highlight information to text widgets in each pane
448 (and redraw) */
449 AttachHighlightToWidget(window->textArea, window);
450 for (i=0; i<window->nPanes; i++)
451 AttachHighlightToWidget(window->textPanes[i], window);
452 }
453
454 /*
455 ** Do a test compile of patterns in "patSet" and report problems to the
456 ** user via dialog. Returns True if patterns are ok.
457 **
458 ** This is somewhat kludgy in that it uses createHighlightData, which
459 ** requires a window to find the fonts to use, and just uses a random
460 ** window from the window list. Since the window is used to get the
461 ** dialog parent as well, in non-popups-under-pointer mode, these dialogs
462 ** will appear in odd places on the screen.
463 */
TestHighlightPatterns(patternSet * patSet)464 int TestHighlightPatterns(patternSet *patSet)
465 {
466 windowHighlightData *highlightData;
467
468 /* Compile the patterns (passing a random window as a source for fonts, and
469 parent for dialogs, since we really don't care what fonts are used) */
470 highlightData = createHighlightData(WindowList, patSet);
471 if (highlightData == NULL)
472 return False;
473 freeHighlightData(highlightData);
474 return True;
475 }
476
477 /*
478 ** Returns the highlight style of the character at a given position of a
479 ** window. To avoid breaking encapsulation, the highlight style is converted
480 ** to a void* pointer (no other module has to know that characters are used
481 ** to represent highlight styles; that would complicate future extensions).
482 ** Returns NULL if the window has highlighting turned off.
483 ** The only guarantee that this function offers, is that when the same
484 ** pointer is returned for two positions, the corresponding characters have
485 ** the same highlight style.
486 **/
GetHighlightInfo(WindowInfo * window,int pos)487 void* GetHighlightInfo(WindowInfo *window, int pos)
488 {
489 int style;
490 highlightDataRec *pattern = NULL;
491 windowHighlightData *highlightData =
492 (windowHighlightData *)window->highlightData;
493 if (!highlightData)
494 return NULL;
495
496 /* Be careful with signed/unsigned conversions. NO conversion here! */
497 style = (int)BufGetCharacter(highlightData->styleBuffer, pos);
498
499 /* Beware of unparsed regions. */
500 if (style == UNFINISHED_STYLE) {
501 handleUnparsedRegion(window, highlightData->styleBuffer, pos);
502 style = (int)BufGetCharacter(highlightData->styleBuffer, pos);
503 }
504
505 if (highlightData->pass1Patterns) {
506 pattern = patternOfStyle(highlightData->pass1Patterns, style);
507 }
508
509 if (!pattern && highlightData->pass2Patterns) {
510 pattern = patternOfStyle(highlightData->pass2Patterns, style);
511 }
512
513 if (!pattern) {
514 return NULL;
515 }
516 return (void*)(intptr_t)pattern->userStyleIndex;
517 }
518
519 /*
520 ** Free allocated memory associated with highlight data, including compiled
521 ** regular expressions, style buffer and style table. Note: be sure to
522 ** NULL out the widget references to the objects in this structure before
523 ** calling this. Because of the slow, multi-phase destruction of
524 ** widgets, this data can be referenced even AFTER destroying the widget.
525 */
freeHighlightData(windowHighlightData * hd)526 static void freeHighlightData(windowHighlightData *hd)
527 {
528 if (hd == NULL)
529 return;
530 if (hd->pass1Patterns != NULL)
531 freePatterns(hd->pass1Patterns);
532 if (hd->pass2Patterns != NULL)
533 freePatterns(hd->pass2Patterns);
534 NEditFree(hd->parentStyles);
535 BufFree(hd->styleBuffer);
536 NEditFree(hd->styleTable);
537 NEditFree(hd);
538 }
539
540 /*
541 ** Find the pattern set matching the window's current language mode, or
542 ** tell the user if it can't be done (if warn is True) and return NULL.
543 */
findPatternsForWindow(WindowInfo * window,int warn)544 static patternSet *findPatternsForWindow(WindowInfo *window, int warn)
545 {
546 patternSet *patterns;
547 char *modeName;
548
549 /* Find the window's language mode. If none is set, warn user */
550 modeName = LanguageModeName(window->languageMode);
551 if (modeName == NULL) {
552 if (warn)
553 DialogF(DF_WARN, window->shell, 1, "Language Mode",
554 "No language-specific mode has been set for this file.\n\n"
555 "To use syntax highlighting in this window, please select a\n"
556 "language from the Preferences -> Language Modes menu.\n\n"
557 "New language modes and syntax highlighting patterns can be\n"
558 "added via Preferences -> Default Settings -> Language Modes,\n"
559 "and Preferences -> Default Settings -> Syntax Highlighting.",
560 "OK");
561 return NULL;
562 }
563
564 /* Look up the appropriate pattern for the language */
565 patterns = FindPatternSet(modeName);
566 if (patterns == NULL)
567 {
568 if (warn)
569 {
570 DialogF(DF_WARN, window->shell, 1, "Language Mode",
571 "Syntax highlighting is not available in language\n"
572 "mode %s.\n\n"
573 "You can create new syntax highlight patterns in the\n"
574 "Preferences -> Default Settings -> Syntax Highlighting\n"
575 "dialog, or choose a different language mode from:\n"
576 "Preferences -> Language Mode.", "OK", modeName);
577 return NULL;
578 }
579 }
580
581 return patterns;
582 }
583
584 /*
585 ** Create complete syntax highlighting information from "patternSrc", using
586 ** highlighting fonts from "window", includes pattern compilation. If errors
587 ** are encountered, warns user with a dialog and returns NULL. To free the
588 ** allocated components of the returned data structure, use freeHighlightData.
589 */
createHighlightData(WindowInfo * window,patternSet * patSet)590 static windowHighlightData *createHighlightData(WindowInfo *window,
591 patternSet *patSet)
592 {
593 highlightPattern *patternSrc = patSet->patterns;
594 int nPatterns = patSet->nPatterns;
595 int contextLines = patSet->lineContext;
596 int contextChars = patSet->charContext;
597 int i, nPass1Patterns, nPass2Patterns;
598 int noPass1, noPass2;
599 char *parentStyles, *parentStylesPtr, *parentName;
600 highlightPattern *pass1PatternSrc, *pass2PatternSrc, *p1Ptr, *p2Ptr;
601 styleTableEntry *styleTable, *styleTablePtr;
602 textBuffer *styleBuf;
603 highlightDataRec *pass1Pats, *pass2Pats;
604 windowHighlightData *highlightData;
605
606 /* The highlighting code can't handle empty pattern sets, quietly say no */
607 if (nPatterns == 0)
608 {
609 return NULL;
610 }
611
612 /* Check that the styles and parent pattern names actually exist */
613 if (!NamedStyleExists("Plain"))
614 {
615 DialogF(DF_WARN, window->shell, 1, "Highlight Style",
616 "Highlight style \"Plain\" is missing", "OK");
617 return NULL;
618 }
619
620 for (i=0; i<nPatterns; i++)
621 {
622 if (patternSrc[i].subPatternOf != NULL
623 && indexOfNamedPattern(patternSrc, nPatterns,
624 patternSrc[i].subPatternOf) == -1)
625 {
626 DialogF(DF_WARN, window->shell, 1, "Parent Pattern",
627 "Parent field \"%s\" in pattern \"%s\"\n"
628 "does not match any highlight patterns in this set",
629 "OK", patternSrc[i].subPatternOf, patternSrc[i].name);
630 return NULL;
631 }
632 }
633
634 for (i=0; i<nPatterns; i++)
635 {
636 if (!NamedStyleExists(patternSrc[i].style))
637 {
638 DialogF(DF_WARN, window->shell, 1, "Highlight Style",
639 "Style \"%s\" named in pattern \"%s\"\n"
640 "does not match any existing style", "OK",
641 patternSrc[i].style, patternSrc[i].name);
642 return NULL;
643 }
644 }
645
646 /* Make DEFER_PARSING flags agree with top level patterns (originally,
647 individual flags had to be correct and were checked here, but dialog now
648 shows this setting only on top patterns which is much less confusing) */
649 for (i = 0; i < nPatterns; i++)
650 {
651 if (patternSrc[i].subPatternOf != NULL)
652 {
653 int parentindex;
654
655 parentindex=findTopLevelParentIndex(patternSrc, nPatterns, i);
656 if (parentindex==-1)
657 {
658 DialogF(DF_WARN, window->shell, 1, "Parent Pattern",
659 "Pattern \"%s\" does not have valid parent", "OK",
660 patternSrc[i].name);
661 return NULL;
662 }
663
664 if (patternSrc[parentindex].flags & DEFER_PARSING)
665 {
666 patternSrc[i].flags |= DEFER_PARSING;
667 } else
668 {
669 patternSrc[i].flags &= ~DEFER_PARSING;
670 }
671 }
672 }
673
674 /* Sort patterns into those to be used in pass 1 parsing, and those to
675 be used in pass 2, and add default pattern (0) to each list */
676 nPass1Patterns = 1;
677 nPass2Patterns = 1;
678 for (i=0; i<nPatterns; i++)
679 if (patternSrc[i].flags & DEFER_PARSING)
680 nPass2Patterns++;
681 else
682 nPass1Patterns++;
683 p1Ptr = pass1PatternSrc = (highlightPattern *)NEditMalloc(
684 sizeof(highlightPattern) * nPass1Patterns);
685 p2Ptr = pass2PatternSrc = (highlightPattern *)NEditMalloc(
686 sizeof(highlightPattern) * nPass2Patterns);
687 p1Ptr->name = p2Ptr->name = "";
688 p1Ptr->startRE = p2Ptr->startRE = NULL;
689 p1Ptr->endRE = p2Ptr->endRE = NULL;
690 p1Ptr->errorRE = p2Ptr->errorRE = NULL;
691 p1Ptr->style = p2Ptr->style = "Plain";
692 p1Ptr->subPatternOf = p2Ptr->subPatternOf = NULL;
693 p1Ptr->flags = p2Ptr->flags = 0;
694 p1Ptr++; p2Ptr++;
695 for (i=0; i<nPatterns; i++) {
696 if (patternSrc[i].flags & DEFER_PARSING)
697 *p2Ptr++ = patternSrc[i];
698 else
699 *p1Ptr++ = patternSrc[i];
700 }
701
702 /* If a particular pass is empty except for the default pattern, don't
703 bother compiling it or setting up styles */
704 if (nPass1Patterns == 1)
705 nPass1Patterns = 0;
706 if (nPass2Patterns == 1)
707 nPass2Patterns = 0;
708
709 /* Compile patterns */
710 if (nPass1Patterns == 0)
711 pass1Pats = NULL;
712 else {
713 pass1Pats = compilePatterns(window->shell, pass1PatternSrc,
714 nPass1Patterns);
715 if (pass1Pats == NULL)
716 return NULL;
717 }
718 if (nPass2Patterns == 0)
719 pass2Pats = NULL;
720 else {
721 pass2Pats = compilePatterns(window->shell, pass2PatternSrc,
722 nPass2Patterns);
723 if (pass2Pats == NULL)
724 return NULL;
725 }
726
727 /* Set pattern styles. If there are pass 2 patterns, pass 1 pattern
728 0 should have a default style of UNFINISHED_STYLE. With no pass 2
729 patterns, unstyled areas of pass 1 patterns should be PLAIN_STYLE
730 to avoid triggering re-parsing every time they are encountered */
731 noPass1 = nPass1Patterns == 0;
732 noPass2 = nPass2Patterns == 0;
733 if (noPass2)
734 pass1Pats[0].style = PLAIN_STYLE;
735 else if (noPass1)
736 pass2Pats[0].style = PLAIN_STYLE;
737 else {
738 pass1Pats[0].style = UNFINISHED_STYLE;
739 pass2Pats[0].style = PLAIN_STYLE;
740 }
741 for (i=1; i<nPass1Patterns; i++)
742 pass1Pats[i].style = PLAIN_STYLE + i;
743 for (i=1; i<nPass2Patterns; i++)
744 pass2Pats[i].style = PLAIN_STYLE + (noPass1 ? 0 : nPass1Patterns-1) + i;
745
746 /* Create table for finding parent styles */
747 parentStylesPtr = parentStyles = (char*)NEditMalloc(nPass1Patterns+nPass2Patterns+2);
748 *parentStylesPtr++ = '\0';
749 *parentStylesPtr++ = '\0';
750 for (i=1; i<nPass1Patterns; i++) {
751 parentName = pass1PatternSrc[i].subPatternOf;
752 *parentStylesPtr++ = parentName == NULL ? PLAIN_STYLE :
753 pass1Pats[indexOfNamedPattern(pass1PatternSrc,
754 nPass1Patterns, parentName)].style;
755 }
756 for (i=1; i<nPass2Patterns; i++) {
757 parentName = pass2PatternSrc[i].subPatternOf;
758 *parentStylesPtr++ = parentName == NULL ? PLAIN_STYLE :
759 pass2Pats[indexOfNamedPattern(pass2PatternSrc,
760 nPass2Patterns, parentName)].style;
761 }
762
763 /* Set up table for mapping colors and fonts to syntax */
764 styleTablePtr = styleTable = (styleTableEntry *)NEditMalloc(
765 sizeof(styleTableEntry) * (nPass1Patterns + nPass2Patterns + 1));
766 #define setStyleTablePtr(styleTablePtr, patternSrc) \
767 do { \
768 styleTableEntry *p = styleTablePtr; \
769 highlightPattern *pat = patternSrc; \
770 int r, g, b; \
771 \
772 p->highlightName = pat->name; \
773 p->styleName = pat->style; \
774 p->colorName = ColorOfNamedStyle(pat->style); \
775 p->bgColorName = BgColorOfNamedStyle(pat->style); \
776 p->isBold = FontOfNamedStyleIsBold(pat->style); \
777 p->isItalic = FontOfNamedStyleIsItalic(pat->style); \
778 /* And now for the more physical stuff */ \
779 p->color = AllocColor(window->textArea, p->colorName, &r, &g, &b); \
780 p->red = r; \
781 p->green = g; \
782 p->blue = b; \
783 if (p->bgColorName) { \
784 p->bgColor = AllocColor(window->textArea, p->bgColorName, &r, &g, &b); \
785 p->bgRed = r; \
786 p->bgGreen = g; \
787 p->bgBlue = b; \
788 } \
789 else { \
790 p->bgColor = p->color; \
791 p->bgRed = r; \
792 p->bgGreen = g; \
793 p->bgBlue = b; \
794 } \
795 p->font = FontOfNamedStyle(window, pat->style); \
796 } while (0)
797
798 /* PLAIN_STYLE (pass 1) */
799 styleTablePtr->underline = FALSE;
800 setStyleTablePtr(styleTablePtr++,
801 noPass1 ? &pass2PatternSrc[0] : &pass1PatternSrc[0]);
802 /* PLAIN_STYLE (pass 2) */
803 styleTablePtr->underline = FALSE;
804 setStyleTablePtr(styleTablePtr++,
805 noPass2 ? &pass1PatternSrc[0] : &pass2PatternSrc[0]);
806 /* explicit styles (pass 1) */
807 for (i=1; i<nPass1Patterns; i++) {
808 styleTablePtr->underline = FALSE;
809 setStyleTablePtr(styleTablePtr++, &pass1PatternSrc[i]);
810 }
811 /* explicit styles (pass 2) */
812 for (i=1; i<nPass2Patterns; i++) {
813 styleTablePtr->underline = FALSE;
814 setStyleTablePtr(styleTablePtr++, &pass2PatternSrc[i]);
815 }
816
817 /* Free the temporary sorted pattern source list */
818 NEditFree(pass1PatternSrc);
819 NEditFree(pass2PatternSrc);
820
821 /* Create the style buffer */
822 styleBuf = BufCreate();
823
824 /* Collect all of the highlighting information in a single structure */
825 highlightData =(windowHighlightData *)NEditMalloc(sizeof(windowHighlightData));
826 highlightData->pass1Patterns = pass1Pats;
827 highlightData->pass2Patterns = pass2Pats;
828 highlightData->parentStyles = parentStyles;
829 highlightData->styleTable = styleTable;
830 highlightData->nStyles = styleTablePtr - styleTable;
831 highlightData->styleBuffer = styleBuf;
832 highlightData->contextRequirements.nLines = contextLines;
833 highlightData->contextRequirements.nChars = contextChars;
834 highlightData->patternSetForWindow = patSet;
835
836 return highlightData;
837 }
838
839 /*
840 ** Transform pattern sources into the compiled highlight information
841 ** actually used by the code. Output is a tree of highlightDataRec structures
842 ** containing compiled regular expressions and style information.
843 */
compilePatterns(Widget dialogParent,highlightPattern * patternSrc,int nPatterns)844 static highlightDataRec *compilePatterns(Widget dialogParent,
845 highlightPattern *patternSrc, int nPatterns)
846 {
847 int i, nSubExprs, patternNum, length, subPatIndex, subExprNum, charsRead;
848 int parentIndex;
849 char *ptr, *bigPattern, *compileMsg;
850 highlightDataRec *compiledPats;
851
852 /* Allocate memory for the compiled patterns. The list is terminated
853 by a record with style == 0. */
854 compiledPats = (highlightDataRec *)NEditMalloc(sizeof(highlightDataRec) *
855 (nPatterns + 1));
856 compiledPats[nPatterns].style = 0;
857
858 /* Build the tree of parse expressions */
859 for (i=0; i<nPatterns; i++) {
860 compiledPats[i].nSubPatterns = 0;
861 compiledPats[i].nSubBranches = 0;
862 }
863 for (i=1; i<nPatterns; i++)
864 if (patternSrc[i].subPatternOf == NULL)
865 compiledPats[0].nSubPatterns++;
866 else
867 compiledPats[indexOfNamedPattern(patternSrc, nPatterns,
868 patternSrc[i].subPatternOf)].nSubPatterns++;
869 for (i=0; i<nPatterns; i++)
870 compiledPats[i].subPatterns = compiledPats[i].nSubPatterns == 0 ?
871 NULL : (highlightDataRec **)NEditMalloc(
872 sizeof(highlightDataRec *) * compiledPats[i].nSubPatterns);
873 for (i=0; i<nPatterns; i++)
874 compiledPats[i].nSubPatterns = 0;
875 for (i=1; i<nPatterns; i++) {
876 if (patternSrc[i].subPatternOf == NULL) {
877 compiledPats[0].subPatterns[compiledPats[0].nSubPatterns++] =
878 &compiledPats[i];
879 } else {
880 parentIndex = indexOfNamedPattern(patternSrc,
881 nPatterns, patternSrc[i].subPatternOf);
882 compiledPats[parentIndex].subPatterns[compiledPats[parentIndex].
883 nSubPatterns++] = &compiledPats[i];
884 }
885 }
886
887 /* Process color-only sub patterns (no regular expressions to match,
888 just colors and fonts for sub-expressions of the parent pattern */
889 for (i=0; i<nPatterns; i++) {
890 compiledPats[i].colorOnly = patternSrc[i].flags & COLOR_ONLY;
891 compiledPats[i].userStyleIndex = IndexOfNamedStyle(patternSrc[i].style);
892 if (compiledPats[i].colorOnly && compiledPats[i].nSubPatterns != 0)
893 {
894 DialogF(DF_WARN, dialogParent, 1, "Color-only Pattern",
895 "Color-only pattern \"%s\" may not have subpatterns",
896 "OK", patternSrc[i].name);
897 return NULL;
898 }
899 nSubExprs = 0;
900 if (patternSrc[i].startRE != NULL) {
901 ptr = patternSrc[i].startRE;
902 while(TRUE) {
903 if (*ptr == '&') {
904 compiledPats[i].startSubexprs[nSubExprs++] = 0;
905 ptr++;
906 } else if (sscanf(ptr, "\\%d%n", &subExprNum, &charsRead)==1) {
907 compiledPats[i].startSubexprs[nSubExprs++] = subExprNum;
908 ptr += charsRead;
909 } else
910 break;
911 }
912 }
913 compiledPats[i].startSubexprs[nSubExprs] = -1;
914 nSubExprs = 0;
915 if (patternSrc[i].endRE != NULL) {
916 ptr = patternSrc[i].endRE;
917 while(TRUE) {
918 if (*ptr == '&') {
919 compiledPats[i].endSubexprs[nSubExprs++] = 0;
920 ptr++;
921 } else if (sscanf(ptr, "\\%d%n", &subExprNum, &charsRead)==1) {
922 compiledPats[i].endSubexprs[nSubExprs++] = subExprNum;
923 ptr += charsRead;
924 } else
925 break;
926 }
927 }
928 compiledPats[i].endSubexprs[nSubExprs] = -1;
929 }
930
931 /* Compile regular expressions for all highlight patterns */
932 for (i=0; i<nPatterns; i++) {
933 if (patternSrc[i].startRE == NULL || compiledPats[i].colorOnly)
934 compiledPats[i].startRE = NULL;
935 else {
936 if ((compiledPats[i].startRE = compileREAndWarn(dialogParent,
937 patternSrc[i].startRE)) == NULL)
938 return NULL;
939 }
940 if (patternSrc[i].endRE == NULL || compiledPats[i].colorOnly)
941 compiledPats[i].endRE = NULL;
942 else {
943 if ((compiledPats[i].endRE = compileREAndWarn(dialogParent,
944 patternSrc[i].endRE)) == NULL)
945 return NULL;
946 }
947 if (patternSrc[i].errorRE == NULL)
948 compiledPats[i].errorRE = NULL;
949 else {
950 if ((compiledPats[i].errorRE = compileREAndWarn(dialogParent,
951 patternSrc[i].errorRE)) == NULL)
952 return NULL;
953 }
954 }
955
956 /* Construct and compile the great hairy pattern to match the OR of the
957 end pattern, the error pattern, and all of the start patterns of the
958 sub-patterns */
959 for (patternNum=0; patternNum<nPatterns; patternNum++) {
960 if (patternSrc[patternNum].endRE == NULL &&
961 patternSrc[patternNum].errorRE == NULL &&
962 compiledPats[patternNum].nSubPatterns == 0) {
963 compiledPats[patternNum].subPatternRE = NULL;
964 continue;
965 }
966 length = (compiledPats[patternNum].colorOnly ||
967 patternSrc[patternNum].endRE == NULL) ? 0 :
968 strlen(patternSrc[patternNum].endRE) + 5;
969 length += (compiledPats[patternNum].colorOnly ||
970 patternSrc[patternNum].errorRE == NULL) ? 0 :
971 strlen(patternSrc[patternNum].errorRE) + 5;
972 for (i=0; i<compiledPats[patternNum].nSubPatterns; i++) {
973 subPatIndex = compiledPats[patternNum].subPatterns[i]-compiledPats;
974 length += compiledPats[subPatIndex].colorOnly ? 0 :
975 strlen(patternSrc[subPatIndex].startRE) + 5;
976 }
977 if (length == 0) {
978 compiledPats[patternNum].subPatternRE = NULL;
979 continue;
980 }
981 bigPattern = (char*)NEditMalloc(length+1);
982 ptr=bigPattern;
983 if (patternSrc[patternNum].endRE != NULL) {
984 *ptr++ = '('; *ptr++ = '?'; *ptr++ = ':';
985 strcpy(ptr, patternSrc[patternNum].endRE);
986 ptr += strlen(patternSrc[patternNum].endRE);
987 *ptr++ = ')';
988 *ptr++ = '|';
989 compiledPats[patternNum].nSubBranches++;
990 }
991 if (patternSrc[patternNum].errorRE != NULL) {
992 *ptr++ = '('; *ptr++ = '?'; *ptr++ = ':';
993 strcpy(ptr, patternSrc[patternNum].errorRE);
994 ptr += strlen(patternSrc[patternNum].errorRE);
995 *ptr++ = ')';
996 *ptr++ = '|';
997 compiledPats[patternNum].nSubBranches++;
998 }
999 for (i=0; i<compiledPats[patternNum].nSubPatterns; i++) {
1000 subPatIndex = compiledPats[patternNum].subPatterns[i]-compiledPats;
1001 if (compiledPats[subPatIndex].colorOnly)
1002 continue;
1003 *ptr++ = '('; *ptr++ = '?'; *ptr++ = ':';
1004 strcpy(ptr, patternSrc[subPatIndex].startRE);
1005 ptr += strlen(patternSrc[subPatIndex].startRE);
1006 *ptr++ = ')';
1007 *ptr++ = '|';
1008 compiledPats[patternNum].nSubBranches++;
1009 }
1010 *(ptr-1) = '\0';
1011 compiledPats[patternNum].subPatternRE = CompileRE(bigPattern,
1012 &compileMsg, REDFLT_STANDARD);
1013 if (compiledPats[patternNum].subPatternRE == NULL) {
1014 fprintf(stderr, "Error compiling syntax highlight patterns:\n%s",
1015 compileMsg);
1016 return NULL;
1017 }
1018 NEditFree(bigPattern);
1019 }
1020
1021 /* Copy remaining parameters from pattern template to compiled tree */
1022 for (i=0; i<nPatterns; i++)
1023 compiledPats[i].flags = patternSrc[i].flags;
1024
1025 return compiledPats;
1026 }
1027
1028 /*
1029 ** Free a pattern list and all of its allocated components
1030 */
freePatterns(highlightDataRec * patterns)1031 static void freePatterns(highlightDataRec *patterns)
1032 {
1033 int i;
1034
1035 for (i=0; patterns[i].style!=0; i++) {
1036 if (patterns[i].startRE != NULL)
1037 free((char *)patterns[i].startRE);
1038 if (patterns[i].endRE != NULL)
1039 free((char *)patterns[i].endRE);
1040 if (patterns[i].errorRE != NULL)
1041 free((char *)patterns[i].errorRE);
1042 if (patterns[i].subPatternRE != NULL)
1043 free((char *)patterns[i].subPatternRE);
1044 }
1045
1046 for (i=0; patterns[i].style!=0; i++) {
1047 NEditFree(patterns[i].subPatterns);
1048 }
1049
1050 NEditFree(patterns);
1051 }
1052
1053 /*
1054 ** Find the highlightPattern structure with a given name in the window.
1055 */
FindPatternOfWindow(WindowInfo * window,char * name)1056 highlightPattern *FindPatternOfWindow(WindowInfo *window, char *name)
1057 {
1058 windowHighlightData *hData = (windowHighlightData *)window->highlightData;
1059 patternSet *set;
1060 int i;
1061
1062 if (hData && (set = hData->patternSetForWindow)) {
1063 for (i = 0; i < set->nPatterns; i++)
1064 if (strcmp(set->patterns[i].name, name) == 0)
1065 return &set->patterns[i];
1066 }
1067 return NULL;
1068 }
1069
1070 /*
1071 ** Picks up the entry in the style buffer for the position (if any). Rather
1072 ** like styleOfPos() in textDisp.c. Returns the style code or zero.
1073 */
HighlightCodeOfPos(WindowInfo * window,int pos)1074 int HighlightCodeOfPos(WindowInfo *window, int pos)
1075 {
1076 windowHighlightData *highlightData =
1077 (windowHighlightData *)window->highlightData;
1078 textBuffer *styleBuf =
1079 highlightData ? highlightData->styleBuffer : NULL;
1080 int hCode = 0;
1081
1082 if (styleBuf != NULL) {
1083 hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1084 if (hCode == UNFINISHED_STYLE) {
1085 /* encountered "unfinished" style, trigger parsing */
1086 handleUnparsedRegion(window, highlightData->styleBuffer, pos);
1087 hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1088 }
1089 }
1090 return hCode;
1091 }
1092
1093 /*
1094 ** Returns the length over which a particular highlight code applies, starting
1095 ** at pos. If the initial code value *checkCode is zero, the highlight code of
1096 ** pos is used.
1097 */
1098 /* YOO: This is called form only one other function, which uses a constant
1099 for checkCode and never evaluates it after the call. */
HighlightLengthOfCodeFromPos(WindowInfo * window,int pos,int * checkCode)1100 int HighlightLengthOfCodeFromPos(WindowInfo *window, int pos, int *checkCode)
1101 {
1102 windowHighlightData *highlightData =
1103 (windowHighlightData *)window->highlightData;
1104 textBuffer *styleBuf =
1105 highlightData ? highlightData->styleBuffer : NULL;
1106 int hCode = 0;
1107 int oldPos = pos;
1108
1109 if (styleBuf != NULL) {
1110 hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1111 if (!hCode)
1112 return 0;
1113 if (hCode == UNFINISHED_STYLE) {
1114 /* encountered "unfinished" style, trigger parsing */
1115 handleUnparsedRegion(window, highlightData->styleBuffer, pos);
1116 hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1117 }
1118 if (*checkCode == 0)
1119 *checkCode = hCode;
1120 while (hCode == *checkCode || hCode == UNFINISHED_STYLE) {
1121 if (hCode == UNFINISHED_STYLE) {
1122 /* encountered "unfinished" style, trigger parsing, then loop */
1123 handleUnparsedRegion(window, highlightData->styleBuffer, pos);
1124 hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1125 }
1126 else {
1127 /* advance the position and get the new code */
1128 hCode = (unsigned char)BufGetCharacter(styleBuf, ++pos);
1129 }
1130 }
1131 }
1132 return pos - oldPos;
1133 }
1134
1135 /*
1136 ** Returns the length over which a particular style applies, starting at pos.
1137 ** If the initial code value *checkCode is zero, the highlight code of pos
1138 ** is used.
1139 */
StyleLengthOfCodeFromPos(WindowInfo * window,int pos,const char ** checkStyleName)1140 int StyleLengthOfCodeFromPos(WindowInfo *window, int pos,
1141 const char **checkStyleName)
1142 {
1143 windowHighlightData *highlightData =
1144 (windowHighlightData *)window->highlightData;
1145 textBuffer *styleBuf =
1146 highlightData ? highlightData->styleBuffer : NULL;
1147 int hCode = 0;
1148 int oldPos = pos;
1149 styleTableEntry *entry;
1150
1151 if (styleBuf != NULL) {
1152 hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1153 if (!hCode)
1154 return 0;
1155 if (hCode == UNFINISHED_STYLE) {
1156 /* encountered "unfinished" style, trigger parsing */
1157 handleUnparsedRegion(window, highlightData->styleBuffer, pos);
1158 hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1159 }
1160 entry = styleTableEntryOfCode(window, hCode);
1161 if (entry == NULL)
1162 return 0;
1163 if ((*checkStyleName) == NULL)
1164 (*checkStyleName) = entry->styleName;
1165 while (hCode == UNFINISHED_STYLE ||
1166 ((entry = styleTableEntryOfCode(window, hCode)) &&
1167 strcmp(entry->styleName, (*checkStyleName)) == 0)) {
1168 if (hCode == UNFINISHED_STYLE) {
1169 /* encountered "unfinished" style, trigger parsing, then loop */
1170 handleUnparsedRegion(window, highlightData->styleBuffer, pos);
1171 hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1172 }
1173 else {
1174 /* advance the position and get the new code */
1175 hCode = (unsigned char)BufGetCharacter(styleBuf, ++pos);
1176 }
1177 }
1178 }
1179 return pos - oldPos;
1180 }
1181
1182 /*
1183 ** Returns a pointer to the entry in the style table for the entry of code
1184 ** hCode (if any).
1185 */
styleTableEntryOfCode(WindowInfo * window,int hCode)1186 static styleTableEntry *styleTableEntryOfCode(WindowInfo *window, int hCode)
1187 {
1188 windowHighlightData *highlightData =
1189 (windowHighlightData *)window->highlightData;
1190
1191 hCode -= UNFINISHED_STYLE; /* get the correct index value */
1192 if (!highlightData || hCode < 0 || hCode >= highlightData->nStyles)
1193 return NULL;
1194 return &highlightData->styleTable[hCode];
1195 }
1196
1197 /*
1198 ** Functions to return style information from the highlighting style table.
1199 */
1200
HighlightNameOfCode(WindowInfo * window,int hCode)1201 char *HighlightNameOfCode(WindowInfo *window, int hCode)
1202 {
1203 styleTableEntry *entry = styleTableEntryOfCode(window, hCode);
1204 return entry ? entry->highlightName : "";
1205 }
1206
HighlightStyleOfCode(WindowInfo * window,int hCode)1207 char *HighlightStyleOfCode(WindowInfo *window, int hCode)
1208 {
1209 styleTableEntry *entry = styleTableEntryOfCode(window, hCode);
1210 return entry ? entry->styleName : "";
1211 }
1212
HighlightColorValueOfCode(WindowInfo * window,int hCode,int * r,int * g,int * b)1213 Pixel HighlightColorValueOfCode(WindowInfo *window, int hCode,
1214 int *r, int *g, int *b)
1215 {
1216 styleTableEntry *entry = styleTableEntryOfCode(window, hCode);
1217 if (entry) {
1218 *r = entry->red;
1219 *g = entry->green;
1220 *b = entry->blue;
1221 return entry->color;
1222 }
1223 else
1224 {
1225 /* pick up foreground color of the (first) text widget of the window */
1226 XColor colorDef;
1227 Colormap cMap;
1228 Display *display = XtDisplay(window->textArea);
1229 *r = *g = *b = 0;
1230 XtVaGetValues(window->textArea,
1231 XtNcolormap, &cMap,
1232 XtNforeground, &colorDef.pixel,
1233 NULL);
1234 if (XQueryColor(display, cMap, &colorDef)) {
1235 *r = colorDef.red;
1236 *g = colorDef.green;
1237 *b = colorDef.blue;
1238 }
1239 return colorDef.pixel;
1240 }
1241 }
1242
GetHighlightBGColorOfCode(WindowInfo * window,int hCode,int * r,int * g,int * b)1243 Pixel GetHighlightBGColorOfCode(WindowInfo *window, int hCode,
1244 int *r, int *g, int *b)
1245 {
1246 styleTableEntry *entry = styleTableEntryOfCode(window, hCode);
1247 if (entry && entry->bgColorName) {
1248 *r = entry->bgRed;
1249 *g = entry->bgGreen;
1250 *b = entry->bgBlue;
1251 return entry->bgColor;
1252 }
1253 else
1254 {
1255 /* pick up background color of the (first) text widget of the window */
1256 XColor colorDef;
1257 Colormap cMap;
1258 Display *display = XtDisplay(window->textArea);
1259 *r = *g = *b = 0;
1260 XtVaGetValues(window->textArea,
1261 XtNcolormap, &cMap,
1262 XtNbackground, &colorDef.pixel,
1263 NULL);
1264 if (XQueryColor(display, cMap, &colorDef)) {
1265 *r = colorDef.red;
1266 *g = colorDef.green;
1267 *b = colorDef.blue;
1268 }
1269 return colorDef.pixel;
1270 }
1271 }
1272
1273 /*
1274 ** Callback to parse an "unfinished" region of the buffer. "unfinished" means
1275 ** that the buffer has been parsed with pass 1 patterns, but this section has
1276 ** not yet been exposed, and thus never had pass 2 patterns applied. This
1277 ** callback is invoked when the text widget's display routines encounter one
1278 ** of these unfinished regions. "pos" is the first position encountered which
1279 ** needs re-parsing. This routine applies pass 2 patterns to a chunk of
1280 ** the buffer of size PASS_2_REPARSE_CHUNK_SIZE beyond pos.
1281 */
handleUnparsedRegion(const WindowInfo * window,textBuffer * styleBuf,int pos)1282 static void handleUnparsedRegion(const WindowInfo* window, textBuffer* styleBuf,
1283 int pos)
1284 {
1285 textBuffer *buf = window->buffer;
1286 int beginParse, endParse, beginSafety, endSafety, p;
1287 windowHighlightData *highlightData =
1288 (windowHighlightData *)window->highlightData;
1289
1290 reparseContext *context = &highlightData->contextRequirements;
1291 highlightDataRec *pass2Patterns = highlightData->pass2Patterns;
1292 char *string, *styleString, *stylePtr, c, prevChar;
1293 const char *stringPtr;
1294 int firstPass2Style = (unsigned char)pass2Patterns[1].style;
1295
1296 /* If there are no pass 2 patterns to process, do nothing (but this
1297 should never be triggered) */
1298 if (pass2Patterns == NULL)
1299 return;
1300
1301 /* Find the point at which to begin parsing to ensure that the character at
1302 pos is parsed correctly (beginSafety), at most one context distance back
1303 from pos, unless there is a pass 1 section from which to start */
1304 beginParse = pos;
1305 beginSafety = backwardOneContext(buf, context, beginParse);
1306 for (p=beginParse; p>=beginSafety; p--) {
1307 c = BufGetCharacter(styleBuf, p);
1308 if (c != UNFINISHED_STYLE && c != PLAIN_STYLE &&
1309 (unsigned char)c < firstPass2Style) {
1310 beginSafety = p + 1;
1311 break;
1312 }
1313 }
1314
1315 /* Decide where to stop (endParse), and the extra distance (endSafety)
1316 necessary to ensure that the changes at endParse are correct. Stop at
1317 the end of the unfinished region, or a max. of PASS_2_REPARSE_CHUNK_SIZE
1318 characters forward from the requested position */
1319 endParse = min(buf->length, pos + PASS_2_REPARSE_CHUNK_SIZE);
1320 endSafety = forwardOneContext(buf, context, endParse);
1321 for (p=pos; p<endSafety; p++) {
1322 c = BufGetCharacter(styleBuf, p);
1323 if (c != UNFINISHED_STYLE && c != PLAIN_STYLE &&
1324 (unsigned char)c < firstPass2Style) {
1325 endParse = min(endParse, p);
1326 endSafety = p;
1327 break;
1328 } else if (c != UNFINISHED_STYLE && p < endParse) {
1329 endParse = p;
1330 if ((unsigned char)c < firstPass2Style)
1331 endSafety = p;
1332 else
1333 endSafety = forwardOneContext(buf, context, endParse);
1334 break;
1335 }
1336 }
1337
1338 /* Copy the buffer range into a string */
1339 /* printf("callback pass2 parsing from %d thru %d w/ safety from %d thru %d\n",
1340 beginParse, endParse, beginSafety, endSafety); */
1341 stringPtr = string = BufGetRange(buf, beginSafety, endSafety);
1342 styleString = stylePtr = BufGetRange(styleBuf, beginSafety, endSafety);
1343
1344 /* Parse it with pass 2 patterns */
1345 prevChar = getPrevChar(buf, beginSafety);
1346 parseString(pass2Patterns, &stringPtr, &stylePtr, endParse - beginSafety,
1347 &prevChar, False, GetWindowDelimiters(window), string, NULL);
1348
1349 /* Update the style buffer the new style information, but only between
1350 beginParse and endParse. Skip the safety region */
1351 styleString[endParse-beginSafety] = '\0';
1352 BufReplace(styleBuf, beginParse, endParse,
1353 &styleString[beginParse-beginSafety]);
1354 NEditFree(styleString);
1355 NEditFree(string);
1356 }
1357
1358 /*
1359 ** Callback wrapper around the above function.
1360 */
handleUnparsedRegionCB(const textDisp * textD,int pos,const void * cbArg)1361 static void handleUnparsedRegionCB(const textDisp* textD, int pos,
1362 const void* cbArg)
1363 {
1364 handleUnparsedRegion((WindowInfo*) cbArg, textD->styleBuffer, pos);
1365 }
1366
1367 /*
1368 ** Re-parse the smallest region possible around a modification to buffer "buf"
1369 ** to gurantee that the promised context lines and characters have
1370 ** been presented to the patterns. Changes the style buffer in "highlightData"
1371 ** with the parsing result.
1372 */
incrementalReparse(windowHighlightData * highlightData,textBuffer * buf,int pos,int nInserted,const char * delimiters)1373 static void incrementalReparse(windowHighlightData *highlightData,
1374 textBuffer *buf, int pos, int nInserted, const char *delimiters)
1375 {
1376 int beginParse, endParse, endAt, lastMod, parseInStyle, nPasses;
1377 textBuffer *styleBuf = highlightData->styleBuffer;
1378 highlightDataRec *pass1Patterns = highlightData->pass1Patterns;
1379 highlightDataRec *pass2Patterns = highlightData->pass2Patterns;
1380 highlightDataRec *startPattern;
1381 reparseContext *context = &highlightData->contextRequirements;
1382 char *parentStyles = highlightData->parentStyles;
1383
1384 /* Find the position "beginParse" at which to begin reparsing. This is
1385 far enough back in the buffer such that the guranteed number of
1386 lines and characters of context are examined. */
1387 beginParse = pos;
1388 parseInStyle = findSafeParseRestartPos(buf, highlightData, &beginParse);
1389
1390 /* Find the position "endParse" at which point it is safe to stop
1391 parsing, unless styles are getting changed beyond the last
1392 modification */
1393 lastMod = pos + nInserted;
1394 endParse = forwardOneContext(buf, context, lastMod);
1395
1396 /*
1397 ** Parse the buffer from beginParse, until styles compare
1398 ** with originals for one full context distance. Distance increases
1399 ** by powers of two until nothing changes from previous step. If
1400 ** parsing ends before endParse, start again one level up in the
1401 ** pattern hierarchy
1402 */
1403 for (nPasses=0; ; nPasses++) {
1404
1405 /* Parse forward from beginParse to one context beyond the end
1406 of the last modification */
1407 startPattern = patternOfStyle(pass1Patterns, parseInStyle);
1408 /* If there is no pattern matching the style, it must be a pass-2
1409 style. It that case, it is (probably) safe to start parsing with
1410 the root pass-1 pattern again. Anyway, passing a NULL-pointer to
1411 the parse routine would result in a crash; restarting with pass-1
1412 patterns is certainly preferable, even if there is a slight chance
1413 of a faulty coloring. */
1414 if (!startPattern) {
1415 startPattern = pass1Patterns;
1416 }
1417 endAt = parseBufferRange(startPattern,
1418 pass2Patterns, buf, styleBuf, context, beginParse, endParse,
1419 delimiters);
1420
1421 /* If parse completed at this level, move one style up in the
1422 hierarchy and start again from where the previous parse left off. */
1423 if (endAt < endParse) {
1424 beginParse = endAt;
1425 endParse = forwardOneContext(buf, context,
1426 max(endAt, max(lastModified(styleBuf), lastMod)));
1427 if (IS_PLAIN(parseInStyle)) {
1428 fprintf(stderr,
1429 "NEdit internal error: incr. reparse fell short\n");
1430 return;
1431 }
1432 parseInStyle = parentStyleOf(parentStyles, parseInStyle);
1433
1434 /* One context distance beyond last style changed means we're done */
1435 } else if (lastModified(styleBuf) <= lastMod) {
1436 return;
1437
1438 /* Styles are changing beyond the modification, continue extending
1439 the end of the parse range by powers of 2 * REPARSE_CHUNK_SIZE and
1440 reparse until nothing changes */
1441 } else {
1442 lastMod = lastModified(styleBuf);
1443 endParse = min(buf->length, forwardOneContext(buf, context, lastMod)
1444 + (REPARSE_CHUNK_SIZE << nPasses));
1445 }
1446 }
1447 }
1448
1449 /*
1450 ** Parse text in buffer "buf" between positions "beginParse" and "endParse"
1451 ** using pass 1 patterns over the entire range and pass 2 patterns where needed
1452 ** to determine whether re-parsed areas have changed and need to be redrawn.
1453 ** Deposits style information in "styleBuf" and expands the selection in
1454 ** styleBuf to show the additional areas which have changed and need
1455 ** redrawing. beginParse must be a position from which pass 1 parsing may
1456 ** safely be started using the pass1Patterns given. Internally, adds a
1457 ** "takeoff" safety region before beginParse, so that pass 2 patterns will be
1458 ** allowed to match properly if they begin before beginParse, and a "landing"
1459 ** safety region beyond endparse so that endParse is guranteed to be parsed
1460 ** correctly in both passes. Returns the buffer position at which parsing
1461 ** finished (this will normally be endParse, unless the pass1Patterns is a
1462 ** pattern which does end and the end is reached).
1463 */
parseBufferRange(highlightDataRec * pass1Patterns,highlightDataRec * pass2Patterns,textBuffer * buf,textBuffer * styleBuf,reparseContext * contextRequirements,int beginParse,int endParse,const char * delimiters)1464 static int parseBufferRange(highlightDataRec *pass1Patterns,
1465 highlightDataRec *pass2Patterns, textBuffer *buf, textBuffer *styleBuf,
1466 reparseContext *contextRequirements, int beginParse, int endParse,
1467 const char *delimiters)
1468 {
1469 char *string, *styleString, *stylePtr, *temp, prevChar;
1470 const char *stringPtr;
1471 int endSafety, endPass2Safety, startPass2Safety, tempLen;
1472 int modStart, modEnd, beginSafety, beginStyle, p, style;
1473 int firstPass2Style = pass2Patterns == NULL ? INT_MAX :
1474 (unsigned char)pass2Patterns[1].style;
1475
1476 /* Begin parsing one context distance back (or to the last style change) */
1477 beginStyle = pass1Patterns->style;
1478 if (CAN_CROSS_LINE_BOUNDARIES(contextRequirements)) {
1479 beginSafety = backwardOneContext(buf, contextRequirements, beginParse);
1480 for (p=beginParse; p>=beginSafety; p--) {
1481 style = BufGetCharacter(styleBuf, p-1);
1482 if (!EQUIVALENT_STYLE(style, beginStyle, firstPass2Style)) {
1483 beginSafety = p;
1484 break;
1485 }
1486 }
1487 } else {
1488 for (beginSafety=max(0,beginParse-1); beginSafety>0; beginSafety--) {
1489 style = BufGetCharacter(styleBuf, beginSafety);
1490 if (!EQUIVALENT_STYLE(style, beginStyle, firstPass2Style) ||
1491 BufGetCharacter(buf, beginSafety) == '\n') {
1492 beginSafety++;
1493 break;
1494 }
1495 }
1496 }
1497
1498 /* Parse one parse context beyond requested end to gurantee that parsing
1499 at endParse is complete, unless patterns can't cross line boundaries,
1500 in which case the end of the line is fine */
1501 if (endParse == 0)
1502 return 0;
1503 if (CAN_CROSS_LINE_BOUNDARIES(contextRequirements))
1504 endSafety = forwardOneContext(buf, contextRequirements, endParse);
1505 else if (endParse>=buf->length || (BufGetCharacter(buf,endParse-1)=='\n'))
1506 endSafety = endParse;
1507 else
1508 endSafety = min(buf->length, BufEndOfLine(buf, endParse) + 1);
1509
1510 /* copy the buffer range into a string */
1511 string = BufGetRange(buf, beginSafety, endSafety);
1512 styleString = BufGetRange(styleBuf, beginSafety, endSafety);
1513
1514 /* Parse it with pass 1 patterns */
1515 /* printf("parsing from %d thru %d\n", beginSafety, endSafety); */
1516 prevChar = getPrevChar(buf, beginParse);
1517 stringPtr = &string[beginParse-beginSafety];
1518 stylePtr = &styleString[beginParse-beginSafety];
1519 parseString(pass1Patterns, &stringPtr, &stylePtr, endParse-beginParse,
1520 &prevChar, False, delimiters, string, NULL);
1521
1522 /* On non top-level patterns, parsing can end early */
1523 endParse = min(endParse, stringPtr-string + beginSafety);
1524
1525 /* If there are no pass 2 patterns, we're done */
1526 if (pass2Patterns == NULL)
1527 goto parseDone;
1528
1529 /* Parsing of pass 2 patterns is done only as necessary for determining
1530 where styles have changed. Find the area to avoid, which is already
1531 marked as changed (all inserted text and previously modified areas) */
1532 if (styleBuf->primary.selected) {
1533 modStart = styleBuf->primary.start;
1534 modEnd = styleBuf->primary.end;
1535 } else
1536 modStart = modEnd = 0;
1537
1538 /* Re-parse the areas before the modification with pass 2 patterns, from
1539 beginSafety to far enough beyond modStart to gurantee that parsing at
1540 modStart is correct (pass 2 patterns must match entirely within one
1541 context distance, and only on the top level). If the parse region
1542 ends entirely before the modification or at or beyond modEnd, parse
1543 the whole thing and take advantage of the safety region which will be
1544 thrown away below. Otherwise save the contents of the safety region
1545 temporarily, and restore it after the parse. */
1546 if (beginSafety < modStart) {
1547 if (endSafety > modStart) {
1548 endPass2Safety = forwardOneContext(buf, contextRequirements,
1549 modStart);
1550 if (endPass2Safety + PASS_2_REPARSE_CHUNK_SIZE >= modEnd)
1551 endPass2Safety = endSafety;
1552 } else
1553 endPass2Safety = endSafety;
1554 prevChar = getPrevChar(buf, beginSafety);
1555 if (endPass2Safety == endSafety) {
1556 passTwoParseString(pass2Patterns, string, styleString,
1557 endParse - beginSafety, &prevChar, delimiters, string, NULL);
1558 goto parseDone;
1559 } else {
1560 tempLen = endPass2Safety - modStart;
1561 temp = (char*)NEditMalloc(tempLen);
1562 strncpy(temp, &styleString[modStart-beginSafety], tempLen);
1563 passTwoParseString(pass2Patterns, string, styleString,
1564 modStart - beginSafety, &prevChar, delimiters, string, NULL);
1565 strncpy(&styleString[modStart-beginSafety], temp, tempLen);
1566 NEditFree(temp);
1567 }
1568 }
1569
1570 /* Re-parse the areas after the modification with pass 2 patterns, from
1571 modEnd to endSafety, with an additional safety region before modEnd
1572 to ensure that parsing at modEnd is correct. */
1573 if (endParse > modEnd) {
1574 if (beginSafety > modEnd) {
1575 prevChar = getPrevChar(buf, beginSafety);
1576 passTwoParseString(pass2Patterns, string, styleString,
1577 endParse - beginSafety, &prevChar, delimiters, string, NULL);
1578 } else {
1579 startPass2Safety = max(beginSafety,
1580 backwardOneContext(buf, contextRequirements, modEnd));
1581 tempLen = modEnd - startPass2Safety;
1582 temp = (char*)NEditMalloc(tempLen);
1583 strncpy(temp, &styleString[startPass2Safety-beginSafety], tempLen);
1584 prevChar = getPrevChar(buf, startPass2Safety);
1585 passTwoParseString(pass2Patterns,
1586 &string[startPass2Safety-beginSafety],
1587 &styleString[startPass2Safety-beginSafety],
1588 endParse-startPass2Safety, &prevChar, delimiters, string,
1589 NULL);
1590 strncpy(&styleString[startPass2Safety-beginSafety], temp, tempLen);
1591 NEditFree(temp);
1592 }
1593 }
1594
1595 parseDone:
1596
1597 /* Update the style buffer with the new style information, but only
1598 through endParse. Skip the safety region at the end */
1599 styleString[endParse-beginSafety] = '\0';
1600 modifyStyleBuf(styleBuf, &styleString[beginParse-beginSafety],
1601 beginParse, endParse, firstPass2Style);
1602 NEditFree(styleString);
1603 NEditFree(string);
1604
1605 return endParse;
1606 }
1607
1608 /*
1609 ** Parses "string" according to compiled regular expressions in "pattern"
1610 ** until endRE is or errorRE are matched, or end of string is reached.
1611 ** Advances "string", "styleString" pointers to the next character past
1612 ** the end of the parsed section, and updates "prevChar" to reflect
1613 ** the new character before "string".
1614 ** If "anchored" is true, just scan the sub-pattern starting at the beginning
1615 ** of the string. "length" is how much of the string must be parsed, but
1616 ** "string" must still be null terminated, the termination indicating how
1617 ** far the string should be searched, and "length" the part which is actually
1618 ** required (the string may or may not be parsed beyond "length").
1619 **
1620 ** "lookBehindTo" indicates the boundary till where look-behind patterns may
1621 ** look back. If NULL, the start of the string is assumed to be the boundary.
1622 **
1623 ** "match_till" indicates the boundary till where matches may extend. If NULL,
1624 ** it is assumed that the terminating \0 indicates the boundary. Note that
1625 ** look-ahead patterns can peek beyond the boundary, if supplied.
1626 **
1627 ** Returns True if parsing was done and the parse succeeded. Returns False if
1628 ** the error pattern matched, if the end of the string was reached without
1629 ** matching the end expression, or in the unlikely event of an internal error.
1630 */
parseString(highlightDataRec * pattern,const char ** string,char ** styleString,int length,char * prevChar,int anchored,const char * delimiters,const char * lookBehindTo,const char * match_till)1631 static int parseString(highlightDataRec *pattern, const char **string,
1632 char **styleString, int length, char *prevChar, int anchored,
1633 const char *delimiters, const char* lookBehindTo,
1634 const char* match_till)
1635 {
1636 int i, subExecuted, subIndex;
1637 char *stylePtr;
1638 const char *stringPtr, *savedStartPtr, *startingStringPtr;
1639 signed char *subExpr;
1640 char savedPrevChar;
1641 char succChar = match_till ? (*match_till) : '\0';
1642 highlightDataRec *subPat = NULL, *subSubPat;
1643
1644 if (length <= 0)
1645 return False;
1646
1647 stringPtr = *string;
1648 stylePtr = *styleString;
1649
1650 while (ExecRE(pattern->subPatternRE, stringPtr, anchored ? *string+1 :
1651 *string+length+1, False, *prevChar, succChar, delimiters,
1652 lookBehindTo, match_till)) {
1653 /* Beware of the case where only one real branch exists, but that
1654 branch has sub-branches itself. In that case the top_branch refers
1655 to the matching sub-branch and must be ignored. */
1656 subIndex = (pattern->nSubBranches > 1) ?
1657 pattern->subPatternRE->top_branch : 0;
1658 /* Combination of all sub-patterns and end pattern matched */
1659 /* printf("combined patterns RE matched at %d\n",
1660 pattern->subPatternRE->startp[0] - *string); */
1661 startingStringPtr = stringPtr;
1662
1663 /* Fill in the pattern style for the text that was skipped over before
1664 the match, and advance the pointers to the start of the pattern */
1665 fillStyleString(&stringPtr, &stylePtr, pattern->subPatternRE->startp[0],
1666 pattern->style, prevChar);
1667
1668 /* If the combined pattern matched this pattern's end pattern, we're
1669 done. Fill in the style string, update the pointers, color the
1670 end expression if there were coloring sub-patterns, and return */
1671 savedStartPtr = stringPtr;
1672 savedPrevChar = *prevChar;
1673 if (pattern->endRE != NULL) {
1674 if (subIndex == 0) {
1675 fillStyleString(&stringPtr, &stylePtr,
1676 pattern->subPatternRE->endp[0], pattern->style, prevChar);
1677 subExecuted = False;
1678 for (i=0;i<pattern->nSubPatterns; i++) {
1679 subPat = pattern->subPatterns[i];
1680 if (subPat->colorOnly) {
1681 if (!subExecuted) {
1682 if (!ExecRE(pattern->endRE, savedStartPtr,
1683 savedStartPtr+1, False, savedPrevChar,
1684 succChar, delimiters, lookBehindTo, match_till)) {
1685 fprintf(stderr, "Internal error, failed to "
1686 "recover end match in parseString\n");
1687 return False;
1688 }
1689 subExecuted = True;
1690 }
1691 for (subExpr=subPat->endSubexprs; *subExpr!=-1; subExpr++)
1692 recolorSubexpr(pattern->endRE, *subExpr,
1693 subPat->style, *string, *styleString);
1694 }
1695 }
1696 *string = stringPtr;
1697 *styleString = stylePtr;
1698 return True;
1699 }
1700 --subIndex;
1701 }
1702
1703 /* If the combined pattern matched this pattern's error pattern, we're
1704 done. Fill in the style string, update the pointers, and return */
1705 if (pattern->errorRE != NULL) {
1706 if (subIndex == 0) {
1707 fillStyleString(&stringPtr, &stylePtr,
1708 pattern->subPatternRE->startp[0], pattern->style, prevChar);
1709 *string = stringPtr;
1710 *styleString = stylePtr;
1711 return False;
1712 }
1713 --subIndex;
1714 }
1715
1716 /* Figure out which sub-pattern matched */
1717 for (i=0; i<pattern->nSubPatterns; i++) {
1718 subPat = pattern->subPatterns[i];
1719 if (subPat->colorOnly) ++subIndex;
1720 else if (i == subIndex) break;
1721 }
1722 if (i == pattern->nSubPatterns) {
1723 fprintf(stderr, "Internal error, failed to match in parseString\n");
1724 return False;
1725 }
1726
1727 /* the sub-pattern is a simple match, just color it */
1728 if (subPat->subPatternRE == NULL) {
1729 fillStyleString(&stringPtr, &stylePtr, pattern->subPatternRE->endp[0], /* subPat->startRE->endp[0],*/
1730 subPat->style, prevChar);
1731
1732 /* Parse the remainder of the sub-pattern */
1733 } else if (subPat->endRE != NULL) {
1734 /* The pattern is a starting/ending type of pattern, proceed with
1735 the regular hierarchical parsing. */
1736
1737 /* If parsing should start after the start pattern, advance
1738 to that point (this is currently always the case) */
1739 if (!(subPat->flags & PARSE_SUBPATS_FROM_START))
1740 fillStyleString(&stringPtr, &stylePtr, pattern->subPatternRE->endp[0], /* subPat->startRE->endp[0],*/
1741 subPat->style, prevChar);
1742
1743 /* Parse to the end of the subPattern */
1744 parseString(subPat, &stringPtr, &stylePtr, length -
1745 (stringPtr - *string), prevChar, False, delimiters,
1746 lookBehindTo, match_till);
1747 } else {
1748 /* If the parent pattern is not a start/end pattern, the
1749 sub-pattern can between the boundaries of the parent's
1750 match. Note that we must limit the recursive matches such
1751 that they do not exceed the parent's ending boundary.
1752 Without that restriction, matching becomes unstable. */
1753
1754 /* Parse to the end of the subPattern */
1755 parseString(subPat, &stringPtr, &stylePtr,
1756 pattern->subPatternRE->endp[0]-stringPtr, prevChar, False,
1757 delimiters, lookBehindTo, pattern->subPatternRE->endp[0]);
1758 }
1759
1760 /* If the sub-pattern has color-only sub-sub-patterns, add color
1761 based on the coloring sub-expression references */
1762 subExecuted = False;
1763 for (i=0; i<subPat->nSubPatterns; i++) {
1764 subSubPat = subPat->subPatterns[i];
1765 if (subSubPat->colorOnly) {
1766 if (!subExecuted) {
1767 if (!ExecRE(subPat->startRE, savedStartPtr,
1768 savedStartPtr+1, False, savedPrevChar, succChar,
1769 delimiters, lookBehindTo, match_till)) {
1770 fprintf(stderr, "Internal error, failed to recover "
1771 "start match in parseString\n");
1772 return False;
1773 }
1774 subExecuted = True;
1775 }
1776 for (subExpr=subSubPat->startSubexprs; *subExpr!=-1; subExpr++)
1777 recolorSubexpr(subPat->startRE, *subExpr, subSubPat->style,
1778 *string, *styleString);
1779 }
1780 }
1781
1782 /* Make sure parsing progresses. If patterns match the empty string,
1783 they can get stuck and hang the process */
1784 if (stringPtr == startingStringPtr) {
1785 /* Avoid stepping over the end of the string (possible for
1786 zero-length matches at end of the string) */
1787 if (*stringPtr == '\0')
1788 break;
1789 fillStyleString(&stringPtr, &stylePtr, stringPtr+1,
1790 pattern->style, prevChar);
1791 }
1792 }
1793
1794 /* If this is an anchored match (must match on first character), and
1795 nothing matched, return False */
1796 if (anchored && stringPtr == *string)
1797 return False;
1798
1799 /* Reached end of string, fill in the remaining text with pattern style
1800 (unless this was an anchored match) */
1801 if (!anchored)
1802 fillStyleString(&stringPtr, &stylePtr, *string+length, pattern->style,
1803 prevChar);
1804
1805 /* Advance the string and style pointers to the end of the parsed text */
1806 *string = stringPtr;
1807 *styleString = stylePtr;
1808 return pattern->endRE == NULL;
1809 }
1810
1811 /*
1812 ** Takes a string which has already been parsed through pass1 parsing and
1813 ** re-parses the areas where pass two patterns are applicable. Parameters
1814 ** have the same meaning as in parseString, except that strings aren't doubly
1815 ** indirect and string pointers are not updated.
1816 */
passTwoParseString(highlightDataRec * pattern,char * string,char * styleString,int length,char * prevChar,const char * delimiters,const char * lookBehindTo,const char * match_till)1817 static void passTwoParseString(highlightDataRec *pattern, char *string,
1818 char *styleString, int length, char *prevChar, const char *delimiters,
1819 const char *lookBehindTo, const char *match_till)
1820 {
1821 int inParseRegion = False;
1822 char *stylePtr, temp, *parseStart = NULL, *parseEnd, *s, *c;
1823 const char *stringPtr;
1824 int firstPass2Style = (unsigned char)pattern[1].style;
1825
1826 for (c = string, s = styleString; ; c++, s++) {
1827 if (!inParseRegion && *c != '\0' && (*s == UNFINISHED_STYLE ||
1828 *s == PLAIN_STYLE || (unsigned char)*s >= firstPass2Style)) {
1829 parseStart = c;
1830 inParseRegion = True;
1831 }
1832 if (inParseRegion && (*c == '\0' || !(*s == UNFINISHED_STYLE ||
1833 *s == PLAIN_STYLE || (unsigned char)*s >= firstPass2Style))) {
1834 parseEnd = c;
1835 if (parseStart != string)
1836 *prevChar = *(parseStart-1);
1837 stringPtr = parseStart;
1838 stylePtr = &styleString[parseStart - string];
1839 temp = *parseEnd;
1840 *parseEnd = '\0';
1841 /* printf("pass2 parsing %d chars\n", strlen(stringPtr)); */
1842 parseString(pattern, &stringPtr, &stylePtr,
1843 min(parseEnd - parseStart, length - (parseStart - string)),
1844 prevChar, False, delimiters, lookBehindTo, match_till);
1845 *parseEnd = temp;
1846 inParseRegion = False;
1847 }
1848 if (*c == '\0' || (!inParseRegion && c - string >= length))
1849 break;
1850 }
1851 }
1852
1853 /*
1854 ** Advance "stringPtr" and "stylePtr" until "stringPtr" == "toPtr", filling
1855 ** "stylePtr" with style "style". Can also optionally update the pre-string
1856 ** character, prevChar, which is fed to regular the expression matching
1857 ** routines for determining word and line boundaries at the start of the string.
1858 */
fillStyleString(const char ** stringPtr,char ** stylePtr,const char * toPtr,char style,char * prevChar)1859 static void fillStyleString(const char **stringPtr, char **stylePtr,
1860 const char *toPtr, char style, char *prevChar)
1861 {
1862 int i, len = toPtr-*stringPtr;
1863
1864 if (*stringPtr >= toPtr)
1865 return;
1866
1867 for (i=0; i<len; i++)
1868 *(*stylePtr)++ = style;
1869 if (prevChar != NULL) *prevChar = *(toPtr-1);
1870 *stringPtr = toPtr;
1871 }
1872
1873 /*
1874 ** Incorporate changes from styleString into styleBuf, tracking changes
1875 ** in need of redisplay, and marking them for redisplay by the text
1876 ** modification callback in textDisp.c. "firstPass2Style" is necessary
1877 ** for distinguishing pass 2 styles which compare as equal to the unfinished
1878 ** style in the original buffer, from pass1 styles which signal a change.
1879 */
modifyStyleBuf(textBuffer * styleBuf,char * styleString,int startPos,int endPos,int firstPass2Style)1880 static void modifyStyleBuf(textBuffer *styleBuf, char *styleString,
1881 int startPos, int endPos, int firstPass2Style)
1882 {
1883 char *c, bufChar;
1884 int pos, modStart, modEnd, minPos = INT_MAX, maxPos = 0;
1885 selection *sel = &styleBuf->primary;
1886
1887 /* Skip the range already marked for redraw */
1888 if (sel->selected) {
1889 modStart = sel->start;
1890 modEnd = sel->end;
1891 } else
1892 modStart = modEnd = startPos;
1893
1894 /* Compare the original style buffer (outside of the modified range) with
1895 the new string with which it will be updated, to find the extent of
1896 the modifications. Unfinished styles in the original match any
1897 pass 2 style */
1898 for (c=styleString, pos=startPos; pos<modStart && pos<endPos; c++, pos++) {
1899 bufChar = BufGetCharacter(styleBuf, pos);
1900 if (*c != bufChar && !(bufChar == UNFINISHED_STYLE &&
1901 (*c == PLAIN_STYLE || (unsigned char)*c >= firstPass2Style))) {
1902 if (pos < minPos) minPos = pos;
1903 if (pos > maxPos) maxPos = pos;
1904 }
1905 }
1906 for (c=&styleString[max(0, modEnd-startPos)], pos=max(modEnd, startPos);
1907 pos<endPos; c++, pos++) {
1908 bufChar = BufGetCharacter(styleBuf, pos);
1909 if (*c != bufChar && !(bufChar == UNFINISHED_STYLE &&
1910 (*c == PLAIN_STYLE || (unsigned char)*c >= firstPass2Style))) {
1911 if (pos < minPos) minPos = pos;
1912 if (pos+1 > maxPos) maxPos = pos+1;
1913 }
1914 }
1915
1916 /* Make the modification */
1917 BufReplace(styleBuf, startPos, endPos, styleString);
1918
1919 /* Mark or extend the range that needs to be redrawn. Even if no
1920 change was made, it's important to re-establish the selection,
1921 because it can get damaged by the BufReplace above */
1922 BufSelect(styleBuf, min(modStart, minPos), max(modEnd, maxPos));
1923 }
1924
1925 /*
1926 ** Return the last modified position in styleBuf (as marked by modifyStyleBuf
1927 ** by the convention used for conveying modification information to the
1928 ** text widget, which is selecting the text)
1929 */
lastModified(textBuffer * styleBuf)1930 static int lastModified(textBuffer *styleBuf)
1931 {
1932 if (styleBuf->primary.selected)
1933 return max(0, styleBuf->primary.end);
1934 return 0;
1935 }
1936
1937 /*
1938 ** Compute the distance between two colors.
1939 */
1940
colorDistance(const XColor * c1,const XColor * c2)1941 static double colorDistance(const XColor *c1, const XColor *c2)
1942 {
1943 /* This is done in RGB space, which is close, but not optimal. It's
1944 probably better to do it in HSV or YIQ space, however, that means
1945 a whole lot of extra conversions. This would allow us to weight
1946 the coordinates differently, e.g, prefer to match hue over
1947 brightness. */
1948
1949 static const double scale = 65535;
1950
1951 double tred = c1->red / scale - c2->red / scale;
1952 double tgreen = c1->green / scale - c2->green / scale;
1953 double tblue = c1->blue / scale - c2->blue / scale;
1954
1955 /* use square Euclidian distance */
1956 return tred * tred + tgreen * tgreen + tblue * tblue;
1957 }
1958
1959 /*
1960 ** use this canned function to call AllocColor() when
1961 ** the r, g & b components is not needed, thus saving
1962 ** the little hassle of creating the dummy variable.
1963 */
AllocateColor(Widget w,const char * colorName)1964 Pixel AllocateColor(Widget w, const char *colorName)
1965 {
1966 int dummy;
1967
1968 return AllocColor(w, colorName, &dummy, &dummy, &dummy);
1969 }
1970
1971 /*
1972 ** Allocate a read-only (shareable) colormap cell for a named color, from the
1973 ** the default colormap of the screen on which the widget (w) is displayed. If
1974 ** the colormap is full and there's no suitable substitute, print an error on
1975 ** stderr, and return the widget's foreground color as a backup.
1976 */
1977
AllocColor(Widget w,const char * colorName,int * r,int * g,int * b)1978 Pixel AllocColor(Widget w, const char *colorName, int *r, int *g, int *b)
1979 {
1980 XColor colorDef;
1981 XColor *allColorDefs;
1982 Display *display = XtDisplay(w);
1983 Colormap cMap;
1984 Pixel foreground, bestPixel;
1985 double small = 1.0e9;
1986 int depth;
1987 unsigned int ncolors;
1988 unsigned long i, best = 0; /* pixel value */
1989
1990 /* Get the correct colormap for compatability with the "best" visual
1991 feature in 5.2. Default visual of screen is no good here. */
1992
1993 XtVaGetValues(w,
1994 XtNcolormap, &cMap,
1995 XtNdepth, &depth,
1996 XtNforeground, &foreground,
1997 NULL);
1998
1999 bestPixel = foreground; /* Our last fallback */
2000
2001 /* First, check for valid syntax */
2002 if (! XParseColor(display, cMap, colorName, &colorDef)) {
2003 fprintf(stderr, "NEdit: Color name %s not in database\n", colorName);
2004 colorDef.pixel = foreground;
2005 if (XQueryColor(display, cMap, &colorDef)) {
2006 *r = colorDef.red;
2007 *g = colorDef.green;
2008 *b = colorDef.blue;
2009 }
2010 return foreground;
2011 }
2012
2013 /* Attempt allocation of the exact color. */
2014 if (XAllocColor(display, cMap, &colorDef)) {
2015 *r = colorDef.red;
2016 *g = colorDef.green;
2017 *b = colorDef.blue;
2018 return colorDef.pixel;
2019 }
2020
2021 /* ---------- Allocation failed, the colormap may be full. ---------- */
2022
2023 #if 0
2024 printf("Couldn't allocate %d %d %d\n", colorDef.red, colorDef.green, colorDef.blue);
2025 #endif
2026
2027 /* We can't do the nearest-match on other than 8 bit visuals because
2028 it just takes too long. */
2029
2030 if (depth > 8) { /* Oh no! */
2031 colorDef.pixel = foreground;
2032 if (XQueryColor(display, cMap, &colorDef)) {
2033 *r = colorDef.red;
2034 *g = colorDef.green;
2035 *b = colorDef.blue;
2036 }
2037 return foreground;
2038 }
2039
2040 /* Get the entire colormap so we can find the closest one. */
2041 ncolors = (1 << depth);
2042 allColorDefs = malloc(ncolors * sizeof(XColor));
2043 memset(allColorDefs, 0, ncolors * sizeof(XColor));
2044
2045 for (i = 0; i < ncolors; i++)
2046 allColorDefs[i].pixel = i;
2047
2048 XQueryColors(display, cMap, allColorDefs, ncolors);
2049
2050 /* Scan through each color, looking for the closest one. */
2051 for (i = 0; i < ncolors; i++)
2052 {
2053 double dist = colorDistance(&allColorDefs[i], &colorDef);
2054
2055 if (dist < small)
2056 {
2057 best = i;
2058 small = dist;
2059 }
2060 }
2061
2062 /* Legally try to acquire the shared color- we should loop through
2063 the shortest distances here. We could sort the map in order
2064 of decreasing distances and loop through it until one works. */
2065
2066 if (XAllocColor(display, cMap, &allColorDefs[best]))
2067 bestPixel = allColorDefs[best].pixel;
2068
2069 #if 0
2070 printf("Got %d %d %d, ", allColorDefs[best].red,
2071 allColorDefs[best].green,
2072 allColorDefs[best].blue);
2073 printf("That's %f off\n", small);
2074 #endif
2075
2076 *r = allColorDefs[best].red;
2077 *g = allColorDefs[best].green;
2078 *b = allColorDefs[best].blue;
2079 free(allColorDefs);
2080 return bestPixel;
2081 }
2082
2083 /*
2084 ** Get the character before position "pos" in buffer "buf"
2085 */
getPrevChar(textBuffer * buf,int pos)2086 static char getPrevChar(textBuffer *buf, int pos)
2087 {
2088 return pos == 0 ? '\0' : BufGetCharacter(buf, pos-1);
2089 }
2090
2091 /*
2092 ** compile a regular expression and present a user friendly dialog on failure.
2093 */
compileREAndWarn(Widget parent,const char * re)2094 static regexp *compileREAndWarn(Widget parent, const char *re)
2095 {
2096 regexp *compiledRE;
2097 char *compileMsg;
2098
2099 compiledRE = CompileRE(re, &compileMsg, REDFLT_STANDARD);
2100 if (compiledRE == NULL)
2101 {
2102 char *boundedRe = NEditStrdup(re);
2103 size_t maxLength = DF_MAX_MSG_LENGTH - strlen(compileMsg) - 60;
2104
2105 /* Prevent buffer overflow in DialogF. If the re is too long,
2106 truncate it and append ... */
2107 if (strlen(boundedRe) > maxLength)
2108 {
2109 strcpy(&boundedRe[maxLength-3], "...");
2110 }
2111
2112 DialogF(DF_WARN, parent, 1, "Error in Regex",
2113 "Error in syntax highlighting regular expression:\n%s\n%s",
2114 "OK", boundedRe, compileMsg);
2115 NEditFree(boundedRe);
2116 return NULL;
2117 }
2118 return compiledRE;
2119 }
2120
parentStyleOf(const char * parentStyles,int style)2121 static int parentStyleOf(const char *parentStyles, int style)
2122 {
2123 return parentStyles[(unsigned char)style-UNFINISHED_STYLE];
2124 }
2125
isParentStyle(const char * parentStyles,int style1,int style2)2126 static int isParentStyle(const char *parentStyles, int style1, int style2)
2127 {
2128 int p;
2129
2130 for (p = parentStyleOf(parentStyles, style2); p != '\0';
2131 p = parentStyleOf(parentStyles, p))
2132 if (style1 == p)
2133 return TRUE;
2134 return FALSE;
2135 }
2136
2137 /*
2138 ** Discriminates patterns which can be used with parseString from those which
2139 ** can't. Leaf patterns are not suitable for parsing, because patterns
2140 ** contain the expressions used for parsing within the context of their own
2141 ** operation, i.e. the parent pattern initiates, and leaf patterns merely
2142 ** confirm and color. Returns TRUE if the pattern is suitable for parsing.
2143 */
patternIsParsable(highlightDataRec * pattern)2144 static int patternIsParsable(highlightDataRec *pattern)
2145 {
2146 return pattern != NULL && pattern->subPatternRE != NULL;
2147 }
2148
2149 /*
2150 ** Back up position pointed to by "pos" enough that parsing from that point
2151 ** on will satisfy context gurantees for pattern matching for modifications
2152 ** at pos. Returns the style with which to begin parsing. The caller is
2153 ** guranteed that parsing may safely BEGIN with that style, but not that it
2154 ** will continue at that level.
2155 **
2156 ** This routine can be fooled if a continuous style run of more than one
2157 ** context distance in length is produced by multiple pattern matches which
2158 ** abut, rather than by a single continuous match. In this case the
2159 ** position returned by this routine may be a bad starting point which will
2160 ** result in an incorrect re-parse. However this will happen very rarely,
2161 ** and, if it does, is unlikely to result in incorrect highlighting.
2162 */
findSafeParseRestartPos(textBuffer * buf,windowHighlightData * highlightData,int * pos)2163 static int findSafeParseRestartPos(textBuffer *buf,
2164 windowHighlightData *highlightData, int *pos)
2165 {
2166 int style, startStyle, runningStyle, checkBackTo, safeParseStart, i;
2167 char *parentStyles = highlightData->parentStyles;
2168 highlightDataRec *pass1Patterns = highlightData->pass1Patterns;
2169 reparseContext *context = &highlightData->contextRequirements;
2170
2171 /* We must begin at least one context distance back from the change */
2172 *pos = backwardOneContext(buf, context, *pos);
2173
2174 /* If the new position is outside of any styles or at the beginning of
2175 the buffer, this is a safe place to begin parsing, and we're done */
2176 if (*pos == 0)
2177 return PLAIN_STYLE;
2178 startStyle = BufGetCharacter(highlightData->styleBuffer, *pos);
2179 if (IS_PLAIN(startStyle))
2180 return PLAIN_STYLE;
2181
2182 /*
2183 ** The new position is inside of a styled region, meaning, its pattern
2184 ** could potentially be affected by the modification.
2185 **
2186 ** Follow the style back by enough context to ensure that if we don't find
2187 ** its beginning, at least we've found a safe place to begin parsing
2188 ** within the styled region.
2189 **
2190 ** A safe starting position within a style is either at a style
2191 ** boundary, or far enough from the beginning and end of the style run
2192 ** to ensure that it's not within the start or end expression match
2193 ** (unfortunately, abutting styles can produce false runs so we're not
2194 ** really ensuring it, just making it likely).
2195 */
2196 if (patternIsParsable(patternOfStyle(pass1Patterns, startStyle))) {
2197 safeParseStart = backwardOneContext(buf, context, *pos);
2198 checkBackTo = backwardOneContext(buf, context, safeParseStart);
2199 } else {
2200 safeParseStart = 0;
2201 checkBackTo = 0;
2202 }
2203 runningStyle = startStyle;
2204 for (i = *pos-1; ; i--) {
2205
2206 /* The start of the buffer is certainly a safe place to parse from */
2207 if (i == 0) {
2208 *pos = 0;
2209 return PLAIN_STYLE;
2210 }
2211
2212 /* If the style is preceded by a parent style, it's safe to parse
2213 with the parent style, provided that the parent is parsable. */
2214 style = BufGetCharacter(highlightData->styleBuffer, i);
2215 if (isParentStyle(parentStyles, style, runningStyle)) {
2216 if (patternIsParsable(patternOfStyle(pass1Patterns, style))) {
2217 *pos = i + 1;
2218 return style;
2219 } else {
2220 /* The parent is not parsable, so well have to continue
2221 searching. The parent now becomes the running style. */
2222 runningStyle = style;
2223 }
2224 }
2225
2226 /* If the style is preceded by a child style, it's safe to resume
2227 parsing with the running style, provided that the running
2228 style is parsable. */
2229 else if (isParentStyle(parentStyles, runningStyle, style)) {
2230 if (patternIsParsable
2231 (patternOfStyle(pass1Patterns, runningStyle))) {
2232 *pos = i + 1;
2233 return runningStyle;
2234 }
2235 /* Else: keep searching; it's no use switching to the child style
2236 because even the running one is not parsable. */
2237 }
2238
2239 /* If the style is preceded by a sibling style, it's safe to resume
2240 parsing with the common ancestor style, provided that the ancestor
2241 is parsable. Checking for siblings is very hard; checking whether
2242 the style has the same parent will probably catch 99% of the cases
2243 in practice. */
2244 else if (runningStyle != style &&
2245 isParentStyle(parentStyles,
2246 parentStyleOf(parentStyles, runningStyle), style)) {
2247 int parentStyle = parentStyleOf(parentStyles, runningStyle);
2248 if (patternIsParsable(patternOfStyle(pass1Patterns, parentStyle))) {
2249 *pos = i + 1;
2250 return parentStyle;
2251 } else {
2252 /* Switch to the new style */
2253 runningStyle = style;
2254 }
2255 }
2256
2257 /* If the style is preceded by an unrelated style, it's safe to
2258 resume parsing with PLAIN_STYLE. (Actually, it isn't, because
2259 we didn't really check for all possible sibling relations; but
2260 it will be ok in practice.) */
2261 else if (runningStyle != style) {
2262 *pos = i + 1;
2263 return PLAIN_STYLE;
2264 }
2265
2266 /* If the style is parsable and didn't change for one whole context
2267 distance on either side of safeParseStart, safeParseStart is a
2268 reasonable guess at a place to start parsing.
2269 Note: No 'else' here! We may come from one of the 'fall-through
2270 cases' above. */
2271 if (i == checkBackTo) {
2272 *pos = safeParseStart;
2273
2274 /* We should never return a non-parsable style, because it will
2275 result in an internal error. If the current style is not
2276 parsable, the pattern set most probably contains a context
2277 distance violation. In that case we can only avoid internal
2278 errors (by climbing the pattern hierarchy till we find a
2279 parsable ancestor) and hope that the highlighting errors are
2280 minor. */
2281 while (!patternIsParsable
2282 (patternOfStyle(pass1Patterns, runningStyle)))
2283 runningStyle = parentStyleOf(parentStyles, runningStyle);
2284
2285 return runningStyle;
2286 }
2287 }
2288 }
2289
2290 /*
2291 ** Return a position far enough back in "buf" from "fromPos" to give patterns
2292 ** their guranteed amount of context for matching (from "context"). If
2293 ** backing up by lines yields the greater distance, the returned position will
2294 ** be to the newline character before the start of the line, rather than to
2295 ** the first character of the line. (I did this because earlier prototypes of
2296 ** the syntax highlighting code, which were based on single-line context, used
2297 ** this to ensure that line-spanning expressions would be detected. I think
2298 ** it may reduce some 2 line context requirements to one line, at a cost of
2299 ** only one extra character, but I'm not sure, and my brain hurts from
2300 ** thinking about it).
2301 */
backwardOneContext(textBuffer * buf,reparseContext * context,int fromPos)2302 static int backwardOneContext(textBuffer *buf, reparseContext *context,
2303 int fromPos)
2304 {
2305 if (context->nLines == 0)
2306 return max(0, fromPos - context->nChars);
2307 else if (context->nChars == 0)
2308 return max(0,
2309 BufCountBackwardNLines(buf, fromPos, context->nLines-1) - 1);
2310 else
2311 return max(0, min(max(0, BufCountBackwardNLines(buf, fromPos,
2312 context->nLines-1) -1), fromPos - context->nChars));
2313 }
2314
2315 /*
2316 ** Return a position far enough forward in "buf" from "fromPos" to ensure
2317 ** that patterns are given their required amount of context for matching
2318 ** (from "context"). If moving forward by lines yields the greater
2319 ** distance, the returned position will be the first character of of the
2320 ** next line, rather than the newline character at the end (see notes in
2321 ** backwardOneContext).
2322 */
forwardOneContext(textBuffer * buf,reparseContext * context,int fromPos)2323 static int forwardOneContext(textBuffer *buf, reparseContext *context,
2324 int fromPos)
2325 {
2326 if (context->nLines == 0)
2327 return min(buf->length, fromPos + context->nChars);
2328 else if (context->nChars == 0)
2329 return min(buf->length,
2330 BufCountForwardNLines(buf, fromPos, context->nLines));
2331 else
2332 return min(buf->length, max(BufCountForwardNLines(buf, fromPos,
2333 context->nLines), fromPos + context->nChars));
2334 }
2335
2336 /*
2337 ** Change styles in the portion of "styleString" to "style" where a particular
2338 ** sub-expression, "subExpr", of regular expression "re" applies to the
2339 ** corresponding portion of "string".
2340 */
recolorSubexpr(regexp * re,int subexpr,int style,const char * string,char * styleString)2341 static void recolorSubexpr(regexp *re, int subexpr, int style,
2342 const char *string, char *styleString)
2343 {
2344 const char *stringPtr;
2345 char *stylePtr;
2346
2347 stringPtr = re->startp[subexpr];
2348 stylePtr = &styleString[stringPtr - string];
2349 fillStyleString(&stringPtr, &stylePtr, re->endp[subexpr], style, NULL);
2350 }
2351
2352 /*
2353 ** Search for a pattern in pattern list "patterns" with style "style"
2354 */
patternOfStyle(highlightDataRec * patterns,int style)2355 static highlightDataRec *patternOfStyle(highlightDataRec *patterns, int style)
2356 {
2357 int i;
2358 for (i=0; patterns[i].style!=0; i++)
2359 if (patterns[i].style == style)
2360 return &patterns[i];
2361 if (style == PLAIN_STYLE || style == UNFINISHED_STYLE)
2362 return &patterns[0];
2363 return NULL;
2364 }
2365
max(int i1,int i2)2366 static int max(int i1, int i2)
2367 {
2368 return i1 >= i2 ? i1 : i2;
2369 }
2370
min(int i1,int i2)2371 static int min(int i1, int i2)
2372 {
2373 return i1 <= i2 ? i1 : i2;
2374 }
2375
indexOfNamedPattern(highlightPattern * patList,int nPats,const char * patName)2376 static int indexOfNamedPattern(highlightPattern *patList, int nPats,
2377 const char *patName)
2378 {
2379 int i;
2380
2381 if (patName == NULL)
2382 return -1;
2383 for (i=0; i<nPats; i++)
2384 if (!strcmp(patList[i].name, patName))
2385 return i;
2386 return -1;
2387 }
2388
findTopLevelParentIndex(highlightPattern * patList,int nPats,int index)2389 static int findTopLevelParentIndex(highlightPattern *patList, int nPats,
2390 int index)
2391 {
2392 int topIndex;
2393
2394 topIndex = index;
2395 while (patList[topIndex].subPatternOf != NULL) {
2396 topIndex = indexOfNamedPattern(patList, nPats,
2397 patList[topIndex].subPatternOf);
2398 if (index==topIndex)
2399 return -1; /* amai: circular dependency ?! */
2400 }
2401 return topIndex;
2402 }
2403
2404 /*
2405 ** Re-size (or re-height, anyhow) a window after adding or removing
2406 ** highlight fonts has changed the required vertical spacing (horizontal
2407 ** spacing is determined by the primary font, which doesn't change).
2408 **
2409 ** Note that this messes up the window manager's height increment hint,
2410 ** which must be subsequently reset by UpdateWMSizeHints.
2411 */
updateWindowHeight(WindowInfo * window,int oldFontHeight)2412 static void updateWindowHeight(WindowInfo *window, int oldFontHeight)
2413 {
2414 int i, borderHeight, marginHeight;
2415 Dimension windowHeight, textAreaHeight, textHeight, newWindowHeight;
2416
2417 /* resize if there's only _one_ document in the window, to avoid
2418 the growing-window bug */
2419 if (NDocuments(window) > 1)
2420 return;
2421
2422 /* Decompose the window height into the part devoted to displaying
2423 text (textHeight) and the non-text part (boderHeight) */
2424 XtVaGetValues(window->shell, XmNheight, &windowHeight, NULL);
2425 XtVaGetValues(window->textArea, XmNheight, &textAreaHeight,
2426 textNmarginHeight, &marginHeight, NULL);
2427 textHeight = textAreaHeight - 2*marginHeight;
2428 for (i=0; i<window->nPanes; i++) {
2429 XtVaGetValues(window->textPanes[i], XmNheight, &textAreaHeight, NULL);
2430 textHeight += textAreaHeight - 2*marginHeight;
2431 }
2432 borderHeight = windowHeight - textHeight;
2433
2434 /* Calculate a new window height appropriate for the new font */
2435 newWindowHeight = (textHeight*getFontHeight(window)) / oldFontHeight +
2436 borderHeight;
2437
2438 /* Many window managers enforce window size increments even on client resize
2439 requests. Our height increment is probably wrong because it is still
2440 set for the previous font. Set the new height in advance, before
2441 attempting to resize. */
2442 XtVaSetValues(window->shell, XmNheightInc, getFontHeight(window), NULL);
2443
2444 /* Re-size the window */
2445 XtVaSetValues(window->shell, XmNheight, newWindowHeight, NULL);
2446 }
2447
2448 /*
2449 ** Find the height currently being used to display text, which is
2450 ** a composite of all of the active highlighting fonts as determined by the
2451 ** text display component
2452 */
getFontHeight(WindowInfo * window)2453 static int getFontHeight(WindowInfo *window)
2454 {
2455 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2456
2457 return textD->ascent + textD->descent;
2458 }
2459