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