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