#449569: Ensure shell/macro accelerators also have accelerator fix
[nedit.git] / source / highlight.c
blob684075c86b58c637a8dbf96ba771d77a1017a10f
1 static const char CVSID[] = "$Id: highlight.c,v 1.18 2001/08/17 21:54:25 edg 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 *******************************************************************************/
29 #include <stdio.h>
30 #include <limits.h>
31 #include <math.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #ifdef VMS
35 #include "../util/VMSparam.h"
36 #else
37 #ifndef __MVS__
38 #include <sys/param.h>
39 #endif
40 #endif /*VMS*/
42 #include <Xm/Xm.h>
43 #include <Xm/XmP.h>
44 #if XmVersion >= 1002
45 #include <Xm/PrimitiveP.h>
46 #endif
48 #include "../util/misc.h"
49 #include "../util/DialogF.h"
50 #include "textBuf.h"
51 #include "textDisp.h"
52 #include "text.h"
53 #include "textP.h"
54 #include "nedit.h"
55 #include "regularExp.h"
56 #include "highlight.h"
57 #include "highlightData.h"
58 #include "preferences.h"
59 #include "window.h"
61 /* How much re-parsing to do when an unfinished style is encountered */
62 #define PASS_2_REPARSE_CHUNK_SIZE 1000
64 /* Initial forward expansion of parsing region in incremental reparsing,
65 when style changes propagate forward beyond the original modification.
66 This distance is increased by a factor of two for each subsequent step. */
67 #define REPARSE_CHUNK_SIZE 80
69 /* Meanings of style buffer characters (styles) */
70 #define UNFINISHED_STYLE 'A'
71 #define PLAIN_STYLE 'B'
72 #define IS_PLAIN(style) (style == PLAIN_STYLE || style == UNFINISHED_STYLE)
73 #define IS_STYLED(style) (style != PLAIN_STYLE && style != UNFINISHED_STYLE)
75 /* Compare two styles where one of the styles may not yet have been processed
76 with pass2 patterns */
77 #define EQUIVALENT_STYLE(style1, style2, firstPass2Style) (style1 == style2 || \
78 (style1 == UNFINISHED_STYLE && \
79 (style2 == PLAIN_STYLE || (unsigned char)style2 >= firstPass2Style)) || \
80 (style2 == UNFINISHED_STYLE && \
81 (style1 == PLAIN_STYLE || (unsigned char)style1 >= firstPass2Style)))
83 /* Scanning context can be reduced (with big efficiency gains) if we
84 know that patterns can't cross line boundaries, which is implied
85 by a context requirement of 1 line and 0 characters */
86 #define CAN_CROSS_LINE_BOUNDARIES(contextRequirements) \
87 (contextRequirements->nLines != 1 || contextRequirements->nChars != 0)
89 /* "Compiled" version of pattern specification */
90 typedef struct _highlightDataRec {
91 regexp *startRE;
92 regexp *endRE;
93 regexp *errorRE;
94 regexp *subPatternRE;
95 char style;
96 int colorOnly;
97 signed char startSubexprs[NSUBEXP+1];
98 signed char endSubexprs[NSUBEXP+1];
99 int flags;
100 int nSubPatterns;
101 struct _highlightDataRec **subPatterns;
102 } highlightDataRec;
104 /* Context requirements for incremental reparsing of a pattern set */
105 typedef struct {
106 int nLines;
107 int nChars;
108 } reparseContext;
110 /* Data structure attached to window to hold all syntax highlighting
111 information (for both drawing and incremental reparsing) */
112 typedef struct {
113 highlightDataRec *pass1Patterns;
114 highlightDataRec *pass2Patterns;
115 char *parentStyles;
116 reparseContext contextRequirements;
117 styleTableEntry *styleTable;
118 int nStyles;
119 textBuffer *styleBuffer;
120 } windowHighlightData;
122 static windowHighlightData *createHighlightData(WindowInfo *window,
123 highlightPattern *patternSrc, int nPatterns, int contextLines,
124 int contextChars);
125 static void freeHighlightData(windowHighlightData *hd);
126 static patternSet *findPatternsForWindow(WindowInfo *window, int warn);
127 static highlightDataRec *compilePatterns(Widget dialogParent,
128 highlightPattern *patternSrc, int nPatterns);
129 static void freePatterns(highlightDataRec *patterns);
130 static void handleUnparsedRegionCB(textDisp *textD, int pos, void *cbArg);
131 static void incrementalReparse(windowHighlightData *highlightData,
132 textBuffer *buf, int pos, int nInserted, char *delimiters);
133 static int parseBufferRange(highlightDataRec *pass1Patterns,
134 highlightDataRec *pass2Patterns, textBuffer *buf, textBuffer *styleBuf,
135 reparseContext *contextRequirements, int beginParse, int endParse,
136 char *delimiters);
137 static int parseString(highlightDataRec *pattern, char **string,
138 char **styleString, int length, char *prevChar, int anchored,
139 char *delimiters);
140 static void passTwoParseString(highlightDataRec *pattern, char *string,
141 char *styleString, int length, char *prevChar, int anchored,
142 char *delimiters);
143 static void fillStyleString(char **stringPtr, char **stylePtr, char *toPtr,
144 char style, char *delimiters, char *prevChar);
145 static void modifyStyleBuf(textBuffer *styleBuf, char *styleString,
146 int startPos, int endPos, int firstPass2Style);
147 static int lastModified(textBuffer *styleBuf);
148 static Pixel allocColor(Widget w, const char *colorName);
149 static int max(int i1, int i2);
150 static int min(int i1, int i2);
151 static char getPrevChar(textBuffer *buf, int pos);
152 static regexp *compileREAndWarn(Widget parent, const char *re);
153 static int parentStyleOf(const char *parentStyles, int style);
154 static int isParentStyle(const char *parentStyles, int style1, int style2);
155 static int findSafeParseRestartPos(textBuffer *buf,
156 windowHighlightData *highlightData, int *pos);
157 static int backwardOneContext(textBuffer *buf, reparseContext *context,
158 int fromPos);
159 static int forwardOneContext(textBuffer *buf, reparseContext *context,
160 int fromPos);
161 static void recolorSubexpr(regexp *re, int subexpr, int style, char *string,
162 char *styleString);
163 static int indexOfNamedPattern(highlightPattern *patList, int nPats,
164 const char *patName);
165 static int findTopLevelParentIndex(highlightPattern *patList, int nPats,
166 int index);
167 static highlightDataRec *patternOfStyle(highlightDataRec *patterns, int style);
168 static void updateWindowHeight(WindowInfo *window, int oldFontHeight);
169 static int getFontHeight(WindowInfo *window);
172 ** Buffer modification callback for triggering re-parsing of modified
173 ** text and keeping the style buffer synchronized with the text buffer.
174 ** This must be attached to the the text buffer BEFORE any widget text
175 ** display callbacks, so it can get the style buffer ready to be used
176 ** by the text display routines.
178 ** Update the style buffer for changes to the text, and mark any style
179 ** changes by selecting the region in the style buffer. This strange
180 ** protocol of informing the text display to redraw style changes by
181 ** making selections in the style buffer is used because this routine
182 ** is intended to be called BEFORE the text display callback paints the
183 ** text (to minimize redraws and, most importantly, to synchronize the
184 ** style buffer with the text buffer). If we redraw now, the text
185 ** display hasn't yet processed the modification, redrawing later is
186 ** not only complicated, it will double-draw almost everything typed.
188 ** Note: This routine must be kept efficient. It is called for every
189 ** character typed.
191 void SyntaxHighlightModifyCB(int pos, int nInserted, int nDeleted,
192 int nRestyled, char *deletedText, void *cbArg)
194 WindowInfo *window = (WindowInfo *)cbArg;
195 windowHighlightData
196 *highlightData = (windowHighlightData *)window->highlightData;
197 char *insStyle;
198 int i;
200 if (highlightData == NULL)
201 return;
203 /* Restyling-only modifications (usually a primary or secondary selection)
204 don't require any processing, but clear out the style buffer selection
205 so the widget doesn't think it has to keep redrawing the old area */
206 if (nInserted == 0 && nDeleted == 0) {
207 BufUnselect(highlightData->styleBuffer);
208 return;
211 /* First and foremost, the style buffer must track the text buffer
212 accurately and correctly */
213 if (nInserted > 0) {
214 insStyle = XtMalloc(sizeof(char) * (nInserted + 1));
215 for (i=0; i<nInserted; i++)
216 insStyle[i] = UNFINISHED_STYLE;
217 insStyle[i] = '\0';
218 BufReplace(highlightData->styleBuffer, pos, pos+nDeleted, insStyle);
219 XtFree(insStyle);
220 } else
221 BufRemove(highlightData->styleBuffer, pos, pos+nDeleted);
223 /* Mark the changed region in the style buffer as requiring redraw. This
224 is not necessary for getting it redrawn, it will be redrawn anyhow by
225 the text display callback, but it clears the previous selection and
226 saves the modifyStyleBuf routine from unnecessary work in tracking
227 changes that are already scheduled for redraw */
228 BufSelect(highlightData->styleBuffer, pos, pos+nInserted);
230 /* Re-parse around the changed region */
231 if (highlightData->pass1Patterns)
232 incrementalReparse(highlightData, window->buffer, pos, nInserted,
233 GetWindowDelimiters(window));
237 ** Turn on syntax highlighting. If "warn" is true, warn the user when it
238 ** can't be done, otherwise, just return.
240 void StartHighlighting(WindowInfo *window, int warn)
242 patternSet *patterns;
243 windowHighlightData *highlightData;
244 char *stylePtr, *styleString, *stringPtr, *bufString;
245 char prevChar = '\0';
246 int i, oldFontHeight;
248 /* Find the pattern set matching the window's current
249 language mode, tell the user if it can't be done */
250 patterns = findPatternsForWindow(window, warn);
251 if (patterns == NULL)
252 return;
254 /* Compile the patterns */
255 highlightData = createHighlightData(window, patterns->patterns,
256 patterns->nPatterns, patterns->lineContext, patterns->charContext);
257 if (highlightData == NULL)
258 return;
260 /* Prepare for a long delay, refresh display and put up a watch cursor */
261 BeginWait(window->shell);
262 XmUpdateDisplay(window->shell);
264 /* Parse the buffer with pass 1 patterns. If there are none, initialize
265 the style buffer to all UNFINISHED_STYLE to trigger parsing later */
266 stylePtr = styleString = XtMalloc(window->buffer->length + 1);
267 if (highlightData->pass1Patterns == NULL) {
268 for (i=0; i<window->buffer->length; i++)
269 *stylePtr++ = UNFINISHED_STYLE;
270 } else {
271 stringPtr = bufString = BufGetAll(window->buffer);
272 parseString(highlightData->pass1Patterns, &stringPtr, &stylePtr,
273 window->buffer->length, &prevChar, False,
274 GetWindowDelimiters(window));
275 XtFree(bufString);
277 *stylePtr = '\0';
278 BufSetAll(highlightData->styleBuffer, styleString);
279 XtFree(styleString);
281 /* install highlight pattern data in the window data structure */
282 window->highlightData = highlightData;
284 /* Get the height of the current font in the window, to be used after
285 highlighting is turned on to resize the window to make room for
286 additional highlight fonts which may be sized differently */
287 oldFontHeight = getFontHeight(window);
289 /* Attach highlight information to text widgets in each pane */
290 AttachHighlightToWidget(window->textArea, window);
291 for (i=0; i<window->nPanes; i++)
292 AttachHighlightToWidget(window->textPanes[i], window);
294 /* Re-size the window to fit the highlight fonts properly & tell the
295 window manager about the potential line-height change as well */
296 updateWindowHeight(window, oldFontHeight);
297 UpdateWMSizeHints(window);
298 UpdateMinPaneHeights(window);
300 /* Make sure that if the window has grown, the additional area gets
301 repainted. Otherwise, it is possible that the area gets moved before a
302 repaint event is received and the area doesn't get repainted at all
303 (eg. because of a -line command line argument that moves the text). */
304 XmUpdateDisplay(window->shell);
305 EndWait(window->shell);
309 ** Turn off syntax highlighting and free style buffer, compiled patterns, and
310 ** related data.
312 void StopHighlighting(WindowInfo *window)
314 int i, oldFontHeight;
316 if (window->highlightData==NULL)
317 return;
319 /* Get the line height being used by the highlight fonts in the window,
320 to be used after highlighting is turned off to resize the window
321 back to the line height of the primary font */
322 oldFontHeight = getFontHeight(window);
324 /* Free and remove the highlight data from the window */
325 freeHighlightData((windowHighlightData *)window->highlightData);
326 window->highlightData = NULL;
328 /* Remove and detach style buffer and style table from all text
329 display(s) of window, and redisplay without highlighting */
330 RemoveWidgetHighlight(window->textArea);
331 for (i=0; i<window->nPanes; i++)
332 RemoveWidgetHighlight(window->textPanes[i]);
334 /* Re-size the window to fit the primary font properly & tell the window
335 manager about the potential line-height change as well */
336 updateWindowHeight(window, oldFontHeight);
337 UpdateWMSizeHints(window);
338 UpdateMinPaneHeights(window);
342 ** Free highlighting data from a window destined for destruction, without
343 ** redisplaying.
345 void FreeHighlightingData(WindowInfo *window)
347 int i;
349 if (window->highlightData == NULL)
350 return;
352 /* Free and remove the highlight data from the window */
353 freeHighlightData((windowHighlightData *)window->highlightData);
354 window->highlightData = NULL;
356 /* The text display may make a last desperate attempt to access highlight
357 information when it is destroyed, which would be a disaster. */
358 ((TextWidget)window->textArea)->text.textD->styleBuffer = NULL;
359 for (i=0; i<window->nPanes; i++)
360 ((TextWidget)window->textPanes[i])->text.textD->styleBuffer = NULL;
364 ** Attach style information from a window's highlight data to a
365 ** text widget and redisplay.
367 void AttachHighlightToWidget(Widget widget, WindowInfo *window)
369 windowHighlightData *highlightData =
370 (windowHighlightData *)window->highlightData;
372 TextDAttachHighlightData(((TextWidget)widget)->text.textD,
373 highlightData->styleBuffer, highlightData->styleTable,
374 highlightData->nStyles, UNFINISHED_STYLE, handleUnparsedRegionCB,
375 window);
379 ** Remove style information from a text widget and redisplay it.
381 void RemoveWidgetHighlight(Widget widget)
383 TextDAttachHighlightData(((TextWidget)widget)->text.textD,
384 NULL, NULL, 0, UNFINISHED_STYLE, NULL, NULL);
388 ** Change highlight fonts and/or styles in a highlighted window, without
389 ** re-parsing.
391 void UpdateHighlightStyles(WindowInfo *window)
393 patternSet *patterns;
394 windowHighlightData *highlightData;
395 windowHighlightData *oldHighlightData =
396 (windowHighlightData *)window->highlightData;
397 textBuffer *styleBuffer;
398 int i;
400 /* Do nothing if window not highlighted */
401 if (window->highlightData == NULL)
402 return;
404 /* Find the pattern set for the window's current language mode */
405 patterns = findPatternsForWindow(window, False);
406 if (patterns == NULL) {
407 StopHighlighting(window);
408 return;
411 /* Build new patterns */
412 highlightData = createHighlightData(window, patterns->patterns,
413 patterns->nPatterns, patterns->lineContext, patterns->charContext);
414 if (highlightData == NULL) {
415 StopHighlighting(window);
416 return;
419 /* Update highlight pattern data in the window data structure, but
420 preserve all of the effort that went in to parsing the buffer
421 by swapping it with the empty one in highlightData (which is then
422 freed in freeHighlightData) */
423 styleBuffer = oldHighlightData->styleBuffer;
424 oldHighlightData->styleBuffer = highlightData->styleBuffer;
425 freeHighlightData(oldHighlightData);
426 highlightData->styleBuffer = styleBuffer;
427 window->highlightData = highlightData;
429 /* Attach new highlight information to text widgets in each pane
430 (and redraw) */
431 AttachHighlightToWidget(window->textArea, window);
432 for (i=0; i<window->nPanes; i++)
433 AttachHighlightToWidget(window->textPanes[i], window);
437 ** Do a test compile of patterns in "patSet" and report problems to the
438 ** user via dialog. Returns True if patterns are ok.
440 ** This is somewhat kludgy in that it uses createHighlightData, which
441 ** requires a window to find the fonts to use, and just uses a random
442 ** window from the window list. Since the window is used to get the
443 ** dialog parent as well, in non-popups-under-pointer mode, these dialogs
444 ** will appear in odd places on the screen.
446 int TestHighlightPatterns(patternSet *patSet)
448 windowHighlightData *highlightData;
450 /* Compile the patterns (passing a random window as a source for fonts, and
451 parent for dialogs, since we really don't care what fonts are used) */
452 highlightData = createHighlightData(WindowList,
453 patSet->patterns, patSet->nPatterns, patSet->lineContext,
454 patSet->charContext);
455 if (highlightData == NULL)
456 return False;
457 freeHighlightData(highlightData);
458 return True;
462 ** Free allocated memory associated with highlight data, including compiled
463 ** regular expressions, style buffer and style table. Note: be sure to
464 ** NULL out the widget references to the objects in this structure before
465 ** calling this. Because of the slow, multi-phase destruction of
466 ** widgets, this data can be referenced even AFTER destroying the widget.
468 static void freeHighlightData(windowHighlightData *hd)
470 if (hd == NULL)
471 return;
472 if (hd->pass1Patterns != NULL)
473 freePatterns(hd->pass1Patterns);
474 if (hd->pass2Patterns != NULL)
475 freePatterns(hd->pass2Patterns);
476 XtFree(hd->parentStyles);
477 BufFree(hd->styleBuffer);
478 XtFree((char *)hd->styleTable);
479 XtFree((char *)hd);
483 ** Find the pattern set matching the window's current language mode, or
484 ** tell the user if it can't be done (if warn is True) and return NULL.
486 static patternSet *findPatternsForWindow(WindowInfo *window, int warn)
488 patternSet *patterns;
489 char *modeName;
491 /* Find the window's language mode. If none is set, warn user */
492 modeName = LanguageModeName(window->languageMode);
493 if (modeName == NULL) {
494 if (warn)
495 DialogF(DF_WARN, window->shell, 1,
496 "No language-specific mode has been set for this file.\n\
498 To use syntax highlighting in this window, please select a\n\
499 language from the Preferences -> Language Modes menu.\n\
501 New language modes and syntax highlighting patterns can be\n\
502 added via Preferences -> Default Settings -> Language Modes,\n\
503 and Preferences -> Default Settings -> Syntax Highlighting.", "Dismiss");
504 return NULL;
507 /* Look up the appropriate pattern for the language */
508 patterns = FindPatternSet(modeName);
509 if (patterns == NULL) {
510 if (warn)
511 DialogF(DF_WARN, window->shell, 1,
512 "Syntax highlighting is not available in language\n\
513 mode %s.\n\
515 You can create new syntax highlight patterns in the\n\
516 Preferences -> Default Settings -> Syntax Highlighting\n\
517 dialog, or choose a different language mode from:\n\
518 Preferences -> Language Mode.", "Dismiss", modeName);
519 return NULL;
522 return patterns;
526 ** Create complete syntax highlighting information from "patternSrc", using
527 ** highlighting fonts from "window", includes pattern compilation. If errors
528 ** are encountered, warns user with a dialog and returns NULL. To free the
529 ** allocated components of the returned data structure, use freeHighlightData.
531 static windowHighlightData *createHighlightData(WindowInfo *window,
532 highlightPattern *patternSrc, int nPatterns, int contextLines,
533 int contextChars)
535 int i, nPass1Patterns, nPass2Patterns;
536 int noPass1, noPass2;
537 char *parentStyles, *parentStylesPtr, *parentName;
538 highlightPattern *pass1PatternSrc, *pass2PatternSrc, *p1Ptr, *p2Ptr;
539 styleTableEntry *styleTable, *styleTablePtr;
540 textBuffer *styleBuf;
541 highlightDataRec *pass1Pats, *pass2Pats;
542 windowHighlightData *highlightData;
544 /* The highlighting code can't handle empty pattern sets, quietly say no */
545 if (nPatterns == 0)
546 return NULL;
548 /* Check that the styles and parent pattern names actually exist */
549 if (!NamedStyleExists("Plain")) {
550 DialogF(DF_WARN, window->shell, 1,
551 "Highlight style \"plain\" is missing", "Dismiss");
552 return NULL;
554 for (i=0; i<nPatterns; i++) {
555 if (patternSrc[i].subPatternOf != NULL && indexOfNamedPattern(
556 patternSrc, nPatterns, patternSrc[i].subPatternOf) == -1) {
557 DialogF(DF_WARN, window->shell, 1,
558 "Parent field \"%s\" in pattern \"%s\"\n\
559 does not match any highlight patterns in this set", "Dismiss",
560 patternSrc[i].subPatternOf, patternSrc[i].name);
561 return NULL;
564 for (i=0; i<nPatterns; i++) {
565 if (!NamedStyleExists(patternSrc[i].style)) {
566 DialogF(DF_WARN, window->shell, 1,
567 "Style \"%s\" named in pattern \"%s\"\ndoes not match \
568 any existing style", "Dismiss", patternSrc[i].style, patternSrc[i].name);
569 return NULL;
573 /* Make DEFER_PARSING flags agree with top level patterns (originally,
574 individual flags had to be correct and were checked here, but dialog now
575 shows this setting only on top patterns which is much less confusing) */
576 for (i=0; i<nPatterns; i++) {
577 if (patternSrc[i].subPatternOf != NULL) {
578 int parentindex;
580 parentindex=findTopLevelParentIndex(patternSrc, nPatterns, i);
581 if (parentindex==-1) {
582 DialogF(DF_WARN, window->shell, 1,
583 "Pattern \"%s\" does not have valid parent",
584 "Dismiss", patternSrc[i].name);
585 return NULL;
587 if (patternSrc[parentindex].flags & DEFER_PARSING)
588 patternSrc[i].flags |= DEFER_PARSING;
589 else
590 patternSrc[i].flags &= ~DEFER_PARSING;
594 /* Sort patterns into those to be used in pass 1 parsing, and those to
595 be used in pass 2, and add default pattern (0) to each list */
596 nPass1Patterns = 1;
597 nPass2Patterns = 1;
598 for (i=0; i<nPatterns; i++)
599 if (patternSrc[i].flags & DEFER_PARSING)
600 nPass2Patterns++;
601 else
602 nPass1Patterns++;
603 p1Ptr = pass1PatternSrc = (highlightPattern *)XtMalloc(
604 sizeof(highlightPattern) * nPass1Patterns);
605 p2Ptr = pass2PatternSrc = (highlightPattern *)XtMalloc(
606 sizeof(highlightPattern) * nPass2Patterns);
607 p1Ptr->name = p2Ptr->name = "top";
608 p1Ptr->startRE = p2Ptr->startRE = NULL;
609 p1Ptr->endRE = p2Ptr->endRE = NULL;
610 p1Ptr->errorRE = p2Ptr->errorRE = NULL;
611 p1Ptr->style = p2Ptr->style = "Plain";
612 p1Ptr->subPatternOf = p2Ptr->subPatternOf = NULL;
613 p1Ptr->flags = p2Ptr->flags = 0;
614 p1Ptr++; p2Ptr++;
615 for (i=0; i<nPatterns; i++) {
616 if (patternSrc[i].flags & DEFER_PARSING)
617 *p2Ptr++ = patternSrc[i];
618 else
619 *p1Ptr++ = patternSrc[i];
622 /* If a particular pass is empty except for the default pattern, don't
623 bother compiling it or setting up styles */
624 if (nPass1Patterns == 1)
625 nPass1Patterns = 0;
626 if (nPass2Patterns == 1)
627 nPass2Patterns = 0;
629 /* Compile patterns */
630 if (nPass1Patterns == 0)
631 pass1Pats = NULL;
632 else {
633 pass1Pats = compilePatterns(window->shell, pass1PatternSrc,
634 nPass1Patterns);
635 if (pass1Pats == NULL)
636 return NULL;
638 if (nPass2Patterns == 0)
639 pass2Pats = NULL;
640 else {
641 pass2Pats = compilePatterns(window->shell, pass2PatternSrc,
642 nPass2Patterns);
643 if (pass2Pats == NULL)
644 return NULL;
647 /* Set pattern styles. If there are pass 2 patterns, pass 1 pattern
648 0 should have a default style of UNFINISHED_STYLE. With no pass 2
649 patterns, unstyled areas of pass 1 patterns should be PLAIN_STYLE
650 to avoid triggering re-parsing every time they are encountered */
651 noPass1 = nPass1Patterns == 0;
652 noPass2 = nPass2Patterns == 0;
653 if (noPass2)
654 pass1Pats[0].style = PLAIN_STYLE;
655 else if (noPass1)
656 pass2Pats[0].style = PLAIN_STYLE;
657 else {
658 pass1Pats[0].style = UNFINISHED_STYLE;
659 pass2Pats[0].style = PLAIN_STYLE;
661 for (i=1; i<nPass1Patterns; i++)
662 pass1Pats[i].style = 'B' + i;
663 for (i=1; i<nPass2Patterns; i++)
664 pass2Pats[i].style = 'B' + (noPass1 ? 0 : nPass1Patterns-1) + i;
666 /* Create table for finding parent styles */
667 parentStylesPtr = parentStyles = XtMalloc(nPass1Patterns+nPass2Patterns+2);
668 *parentStylesPtr++ = '\0';
669 *parentStylesPtr++ = '\0';
670 for (i=1; i<nPass1Patterns; i++) {
671 parentName = pass1PatternSrc[i].subPatternOf;
672 *parentStylesPtr++ = parentName == NULL ? PLAIN_STYLE :
673 pass1Pats[indexOfNamedPattern(pass1PatternSrc,
674 nPass1Patterns, parentName)].style;
676 for (i=1; i<nPass2Patterns; i++) {
677 parentName = pass2PatternSrc[i].subPatternOf;
678 *parentStylesPtr++ = parentName == NULL ? PLAIN_STYLE :
679 pass2Pats[indexOfNamedPattern(pass2PatternSrc,
680 nPass2Patterns, parentName)].style;
683 /* Set up table for mapping colors and fonts to syntax */
684 styleTablePtr = styleTable = (styleTableEntry *)XtMalloc(
685 sizeof(styleTableEntry) * (nPass1Patterns + nPass2Patterns + 1));
686 styleTablePtr->color = allocColor(window->textArea, ColorOfNamedStyle(
687 noPass1 ? pass2PatternSrc[0].style : pass1PatternSrc[0].style));
688 styleTablePtr->underline = FALSE;
689 styleTablePtr++->font = FontOfNamedStyle(window,
690 noPass1 ? pass2PatternSrc[0].style : pass1PatternSrc[0].style);
691 styleTablePtr->color = allocColor(window->textArea, ColorOfNamedStyle(
692 noPass2 ? pass1PatternSrc[0].style : pass2PatternSrc[0].style));
693 styleTablePtr->underline = FALSE;
694 styleTablePtr++->font = FontOfNamedStyle(window,
695 noPass2 ? pass1PatternSrc[0].style : pass2PatternSrc[0].style);
696 for (i=1; i<nPass1Patterns; i++) {
697 styleTablePtr->color = allocColor(window->textArea,
698 ColorOfNamedStyle(pass1PatternSrc[i].style));
699 styleTablePtr->underline = FALSE;
700 styleTablePtr++->font = FontOfNamedStyle(window,
701 pass1PatternSrc[i].style);
703 for (i=1; i<nPass2Patterns; i++) {
704 styleTablePtr->color = allocColor(window->textArea,
705 ColorOfNamedStyle(pass2PatternSrc[i].style));
706 styleTablePtr->underline = FALSE;
707 styleTablePtr++->font = FontOfNamedStyle(window,
708 pass2PatternSrc[i].style);
711 /* Free the temporary sorted pattern source list */
712 XtFree((char *)pass1PatternSrc);
713 XtFree((char *)pass2PatternSrc);
715 /* Create the style buffer */
716 styleBuf = BufCreate();
718 /* Collect all of the highlighting information in a single structure */
719 highlightData =(windowHighlightData *)XtMalloc(sizeof(windowHighlightData));
720 highlightData->pass1Patterns = pass1Pats;
721 highlightData->pass2Patterns = pass2Pats;
722 highlightData->parentStyles = parentStyles;
723 highlightData->styleTable = styleTable;
724 highlightData->nStyles = styleTablePtr - styleTable;
725 highlightData->styleBuffer = styleBuf;
726 highlightData->contextRequirements.nLines = contextLines;
727 highlightData->contextRequirements.nChars = contextChars;
729 return highlightData;
733 ** Transform pattern sources into the compiled highlight information
734 ** actually used by the code. Output is a tree of highlightDataRec structures
735 ** containing compiled regular expressions and style information.
737 static highlightDataRec *compilePatterns(Widget dialogParent,
738 highlightPattern *patternSrc, int nPatterns)
740 int i, nSubExprs, patternNum, length, subPatIndex, subExprNum, charsRead;
741 int parentIndex;
742 char *ptr, *bigPattern, *compileMsg;
743 highlightDataRec *compiledPats;
745 /* Allocate memory for the compiled patterns. The list is terminated
746 by a record with style == 0. */
747 compiledPats = (highlightDataRec *)XtMalloc(sizeof(highlightDataRec) *
748 (nPatterns + 1));
749 compiledPats[nPatterns].style = 0;
751 /* Build the tree of parse expressions */
752 for (i=0; i<nPatterns; i++)
753 compiledPats[i].nSubPatterns = 0;
754 for (i=1; i<nPatterns; i++)
755 if (patternSrc[i].subPatternOf == NULL)
756 compiledPats[0].nSubPatterns++;
757 else
758 compiledPats[indexOfNamedPattern(patternSrc, nPatterns,
759 patternSrc[i].subPatternOf)].nSubPatterns++;
760 for (i=0; i<nPatterns; i++)
761 compiledPats[i].subPatterns = compiledPats[i].nSubPatterns == 0 ?
762 NULL : (highlightDataRec **)XtMalloc(
763 sizeof(highlightDataRec *) * compiledPats[i].nSubPatterns);
764 for (i=0; i<nPatterns; i++)
765 compiledPats[i].nSubPatterns = 0;
766 for (i=1; i<nPatterns; i++) {
767 if (patternSrc[i].subPatternOf == NULL) {
768 compiledPats[0].subPatterns[compiledPats[0].nSubPatterns++] =
769 &compiledPats[i];
770 } else {
771 parentIndex = indexOfNamedPattern(patternSrc,
772 nPatterns, patternSrc[i].subPatternOf);
773 compiledPats[parentIndex].subPatterns[compiledPats[parentIndex].
774 nSubPatterns++] = &compiledPats[i];
778 /* Process color-only sub patterns (no regular expressions to match,
779 just colors and fonts for sub-expressions of the parent pattern */
780 for (i=0; i<nPatterns; i++) {
781 compiledPats[i].colorOnly = patternSrc[i].flags & COLOR_ONLY;
782 if (compiledPats[i].colorOnly && compiledPats[i].nSubPatterns != 0) {
783 DialogF(DF_WARN, dialogParent, 1,
784 "Color-only pattern \"%s\" may not have subpatterns",
785 "Dismiss", patternSrc[i].name);
786 return NULL;
788 nSubExprs = 0;
789 if (patternSrc[i].startRE != NULL) {
790 ptr = patternSrc[i].startRE;
791 while(TRUE) {
792 if (*ptr == '&') {
793 compiledPats[i].startSubexprs[nSubExprs++] = 0;
794 ptr++;
795 } else if (sscanf(ptr, "\\%d%n", &subExprNum, &charsRead)==1) {
796 compiledPats[i].startSubexprs[nSubExprs++] = subExprNum;
797 ptr += charsRead;
798 } else
799 break;
802 compiledPats[i].startSubexprs[nSubExprs] = -1;
803 nSubExprs = 0;
804 if (patternSrc[i].endRE != NULL) {
805 ptr = patternSrc[i].endRE;
806 while(TRUE) {
807 if (*ptr == '&') {
808 compiledPats[i].endSubexprs[nSubExprs++] = 0;
809 ptr++;
810 } else if (sscanf(ptr, "\\%d%n", &subExprNum, &charsRead)==1) {
811 compiledPats[i].endSubexprs[nSubExprs++] = subExprNum;
812 ptr += charsRead;
813 } else
814 break;
817 compiledPats[i].endSubexprs[nSubExprs] = -1;
820 /* Compile regular expressions for all highlight patterns */
821 for (i=0; i<nPatterns; i++) {
822 if (patternSrc[i].startRE == NULL || compiledPats[i].colorOnly)
823 compiledPats[i].startRE = NULL;
824 else {
825 if ((compiledPats[i].startRE = compileREAndWarn(dialogParent,
826 patternSrc[i].startRE)) == NULL)
827 return NULL;
829 if (patternSrc[i].endRE == NULL || compiledPats[i].colorOnly)
830 compiledPats[i].endRE = NULL;
831 else {
832 if ((compiledPats[i].endRE = compileREAndWarn(dialogParent,
833 patternSrc[i].endRE)) == NULL)
834 return NULL;
836 if (patternSrc[i].errorRE == NULL)
837 compiledPats[i].errorRE = NULL;
838 else {
839 if ((compiledPats[i].errorRE = compileREAndWarn(dialogParent,
840 patternSrc[i].errorRE)) == NULL)
841 return NULL;
845 /* Construct and compile the great hairy pattern to match the OR of the
846 end pattern, the error pattern, and all of the start patterns of the
847 sub-patterns */
848 for (patternNum=0; patternNum<nPatterns; patternNum++) {
849 if (patternSrc[patternNum].endRE == NULL &&
850 patternSrc[patternNum].errorRE == NULL &&
851 compiledPats[patternNum].nSubPatterns == 0) {
852 compiledPats[patternNum].subPatternRE = NULL;
853 continue;
855 length = (compiledPats[patternNum].colorOnly ||
856 patternSrc[patternNum].endRE == NULL) ? 0 :
857 strlen(patternSrc[patternNum].endRE) + 5;
858 length += patternSrc[patternNum].errorRE == NULL ? 0 :
859 strlen(patternSrc[patternNum].errorRE) + 5;
860 for (i=0; i<compiledPats[patternNum].nSubPatterns; i++) {
861 subPatIndex = compiledPats[patternNum].subPatterns[i]-compiledPats;
862 length += compiledPats[subPatIndex].colorOnly ? 0 :
863 strlen(patternSrc[subPatIndex].startRE) + 5;
865 if (length == 0) {
866 compiledPats[patternNum].subPatternRE = NULL;
867 continue;
869 bigPattern = XtMalloc(sizeof(char) * (length+1));
870 ptr=bigPattern;
871 if (patternSrc[patternNum].endRE != NULL) {
872 *ptr++ = '('; *ptr++ = '?'; *ptr++ = ':';
873 strcpy(ptr, patternSrc[patternNum].endRE);
874 ptr += strlen(patternSrc[patternNum].endRE);
875 *ptr++ = ')';
876 *ptr++ = '|';
878 if (patternSrc[patternNum].errorRE != NULL) {
879 *ptr++ = '('; *ptr++ = '?'; *ptr++ = ':';
880 strcpy(ptr, patternSrc[patternNum].errorRE);
881 ptr += strlen(patternSrc[patternNum].errorRE);
882 *ptr++ = ')';
883 *ptr++ = '|';
885 for (i=0; i<compiledPats[patternNum].nSubPatterns; i++) {
886 subPatIndex = compiledPats[patternNum].subPatterns[i]-compiledPats;
887 if (compiledPats[subPatIndex].colorOnly)
888 continue;
889 *ptr++ = '('; *ptr++ = '?'; *ptr++ = ':';
890 strcpy(ptr, patternSrc[subPatIndex].startRE);
891 ptr += strlen(patternSrc[subPatIndex].startRE);
892 *ptr++ = ')';
893 *ptr++ = '|';
895 *(ptr-1) = '\0';
896 compiledPats[patternNum].subPatternRE = CompileRE(bigPattern,
897 &compileMsg, REDFLT_STANDARD);
898 if (compiledPats[patternNum].subPatternRE == NULL) {
899 fprintf(stderr, "Error compiling syntax highlight patterns:\n%s",
900 compileMsg);
901 return NULL;
903 XtFree(bigPattern);
906 /* Copy remaining parameters from pattern template to compiled tree */
907 for (i=0; i<nPatterns; i++)
908 compiledPats[i].flags = patternSrc[i].flags;
910 return compiledPats;
914 ** Free a pattern list and all of its allocated components
916 static void freePatterns(highlightDataRec *patterns)
918 int i;
920 for (i=0; patterns[i].style!=0; i++) {
921 if (patterns[i].startRE != NULL)
922 free((char *)patterns[i].startRE);
923 if (patterns[i].endRE != NULL)
924 free((char *)patterns[i].endRE);
925 if (patterns[i].errorRE != NULL)
926 XtFree((char *)patterns[i].errorRE);
927 if (patterns[i].subPatternRE != NULL)
928 free((char *)patterns[i].subPatternRE);
930 for (i=0; patterns[i].style!=0; i++)
931 if (patterns[i].subPatterns != NULL)
932 XtFree((char *)patterns[i].subPatterns);
933 XtFree((char *)patterns);
937 ** Callback to parse an "unfinished" region of the buffer. "unfinished" means
938 ** that the buffer has been parsed with pass 1 patterns, but this section has
939 ** not yet been exposed, and thus never had pass 2 patterns applied. This
940 ** callback is invoked when the text widget's display routines encounter one
941 ** of these unfinished regions. "pos" is the first position encountered which
942 ** needs re-parsing. This routine applies pass 2 patterns to a chunk of
943 ** the buffer of size PASS_2_REPARSE_CHUNK_SIZE beyond pos.
945 static void handleUnparsedRegionCB(textDisp *textD, int pos, void *cbArg)
947 WindowInfo *window = (WindowInfo *)cbArg;
948 textBuffer *buf = window->buffer;
949 textBuffer *styleBuf = textD->styleBuffer;
950 int beginParse, endParse, beginSafety, endSafety, p;
951 windowHighlightData *highlightData =
952 (windowHighlightData *)window->highlightData;
953 reparseContext *context = &highlightData->contextRequirements;
954 highlightDataRec *pass2Patterns = highlightData->pass2Patterns;
955 char *string, *styleString, *stringPtr, *stylePtr, c, prevChar;
956 int firstPass2Style = (unsigned char)pass2Patterns[1].style;
958 /* If there are no pass 2 patterns to process, do nothing (but this
959 should never be triggered) */
960 if (pass2Patterns == NULL)
961 return;
963 /* Find the point at which to begin parsing to ensure that the character at
964 pos is parsed correctly (beginSafety), at most one context distance back
965 from pos, unless there is a pass 1 section from which to start */
966 beginParse = pos;
967 beginSafety = backwardOneContext(buf, context, beginParse);
968 for (p=beginParse; p>=beginSafety; p--) {
969 c = BufGetCharacter(styleBuf, p);
970 if (c != UNFINISHED_STYLE && c != PLAIN_STYLE &&
971 (unsigned char)c < firstPass2Style) {
972 beginSafety = p + 1;
973 break;
977 /* Decide where to stop (endParse), and the extra distance (endSafety)
978 necessary to ensure that the changes at endParse are correct. Stop at
979 the end of the unfinished region, or a max. of PASS_2_REPARSE_CHUNK_SIZE
980 characters forward from the requested position */
981 endParse = min(buf->length, pos + PASS_2_REPARSE_CHUNK_SIZE);
982 endSafety = forwardOneContext(buf, context, endParse);
983 for (p=pos; p<endSafety; p++) {
984 c = BufGetCharacter(styleBuf, p);
985 if (c != UNFINISHED_STYLE && c != PLAIN_STYLE &&
986 (unsigned char)c < firstPass2Style) {
987 endParse = min(endParse, p);
988 endSafety = p;
989 break;
990 } else if ((unsigned char)c != UNFINISHED_STYLE && p < endParse) {
991 endParse = p;
992 if (c < firstPass2Style)
993 endSafety = p;
994 else
995 endSafety = forwardOneContext(buf, context, endParse);
996 break;
1000 /* Copy the buffer range into a string */
1001 /* printf("callback pass2 parsing from %d thru %d w/ safety from %d thru %d\n",
1002 beginParse, endParse, beginSafety, endSafety); */
1003 string = stringPtr = BufGetRange(buf, beginSafety, endSafety);
1004 styleString = stylePtr = BufGetRange(styleBuf, beginSafety, endSafety);
1006 /* Parse it with pass 2 patterns */
1007 prevChar = getPrevChar(buf, beginSafety);
1008 parseString(pass2Patterns, &stringPtr, &stylePtr, endParse - beginSafety,
1009 &prevChar, False, GetWindowDelimiters(window));
1011 /* Update the style buffer the new style information, but only between
1012 beginParse and endParse. Skip the safety region */
1013 styleString[endParse-beginSafety] = '\0';
1014 BufReplace(styleBuf, beginParse, endParse,
1015 &styleString[beginParse-beginSafety]);
1016 XtFree(styleString);
1017 XtFree(string);
1021 ** Re-parse the smallest region possible around a modification to buffer "buf"
1022 ** to gurantee that the promised context lines and characters have
1023 ** been presented to the patterns. Changes the style buffer in "highlightData"
1024 ** with the parsing result.
1026 static void incrementalReparse(windowHighlightData *highlightData,
1027 textBuffer *buf, int pos, int nInserted, char *delimiters)
1029 int beginParse, endParse, endAt, lastMod, parseInStyle, nPasses;
1030 textBuffer *styleBuf = highlightData->styleBuffer;
1031 highlightDataRec *pass1Patterns = highlightData->pass1Patterns;
1032 highlightDataRec *pass2Patterns = highlightData->pass2Patterns;
1033 highlightDataRec *startPattern;
1034 reparseContext *context = &highlightData->contextRequirements;
1035 char *parentStyles = highlightData->parentStyles;
1037 /* Find the position "beginParse" at which to begin reparsing. This is
1038 far enough back in the buffer such that the guranteed number of
1039 lines and characters of context are examined. */
1040 beginParse = pos;
1041 parseInStyle = findSafeParseRestartPos(buf, highlightData, &beginParse);
1043 /* Find the position "endParse" at which point it is safe to stop
1044 parsing, unless styles are getting changed beyond the last
1045 modification */
1046 lastMod = pos + nInserted;
1047 endParse = forwardOneContext(buf, context, lastMod);
1050 ** Parse the buffer from beginParse, until styles compare
1051 ** with originals for one full context distance. Distance increases
1052 ** by powers of two until nothing changes from previous step. If
1053 ** parsing ends before endParse, start again one level up in the
1054 ** pattern hierarchy
1056 for (nPasses=0; ; nPasses++) {
1058 /* Parse forward from beginParse to one context beyond the end
1059 of the last modification */
1060 startPattern = patternOfStyle(pass1Patterns, parseInStyle);
1061 /* If there is no pattern matching the style, it must be a pass-2
1062 style. It that case, it is (probably) safe to start parsing with
1063 the root pass-1 pattern again. Anyway, passing a NULL-pointer to
1064 the parse routine would result in a crash; restarting with pass-1
1065 patterns is certainly preferable, even if there is a slight chance
1066 of a faulty coloring. */
1067 if (!startPattern) {
1068 startPattern = pass1Patterns;
1070 endAt = parseBufferRange(startPattern,
1071 pass2Patterns, buf, styleBuf, context, beginParse, endParse,
1072 delimiters);
1074 /* If parse completed at this level, move one style up in the
1075 hierarchy and start again from where the previous parse left off. */
1076 if (endAt < endParse) {
1077 beginParse = endAt;
1078 endParse = forwardOneContext(buf, context,
1079 max(endAt, max(lastModified(styleBuf), lastMod)));
1080 if (IS_PLAIN(parseInStyle)) {
1081 fprintf(stderr,
1082 "NEdit internal error: incr. reparse fell short\n");
1083 return;
1085 parseInStyle = parentStyleOf(parentStyles, parseInStyle);
1087 /* One context distance beyond last style changed means we're done */
1088 } else if (lastModified(styleBuf) <= lastMod) {
1089 return;
1091 /* Styles are changing beyond the modification, continue extending
1092 the end of the parse range by powers of 2 * REPARSE_CHUNK_SIZE and
1093 reparse until nothing changes */
1094 } else {
1095 lastMod = lastModified(styleBuf);
1096 endParse = min(buf->length, forwardOneContext(buf, context, lastMod)
1097 + (REPARSE_CHUNK_SIZE << nPasses));
1103 ** Parse text in buffer "buf" between positions "beginParse" and "endParse"
1104 ** using pass 1 patterns over the entire range and pass 2 patterns where needed
1105 ** to determine whether re-parsed areas have changed and need to be redrawn.
1106 ** Deposits style information in "styleBuf" and expands the selection in
1107 ** styleBuf to show the additional areas which have changed and need
1108 ** redrawing. beginParse must be a position from which pass 1 parsing may
1109 ** safely be started using the pass1Patterns given. Internally, adds a
1110 ** "takeoff" safety region before beginParse, so that pass 2 patterns will be
1111 ** allowed to match properly if they begin before beginParse, and a "landing"
1112 ** safety region beyond endparse so that endParse is guranteed to be parsed
1113 ** correctly in both passes. Returns the buffer position at which parsing
1114 ** finished (this will normally be endParse, unless the pass1Patterns is a
1115 ** pattern which does end and the end is reached).
1117 static int parseBufferRange(highlightDataRec *pass1Patterns,
1118 highlightDataRec *pass2Patterns, textBuffer *buf, textBuffer *styleBuf,
1119 reparseContext *contextRequirements, int beginParse, int endParse,
1120 char *delimiters)
1122 char *string, *styleString, *stringPtr, *stylePtr, *temp, prevChar;
1123 int endSafety, endPass2Safety, startPass2Safety, tempLen;
1124 int modStart, modEnd, beginSafety, beginStyle, p, style;
1125 int firstPass2Style = pass2Patterns == NULL ? INT_MAX :
1126 (unsigned char)pass2Patterns[1].style;
1128 /* Begin parsing one context distance back (or to the last style change) */
1129 beginStyle = pass1Patterns->style;
1130 if (CAN_CROSS_LINE_BOUNDARIES(contextRequirements)) {
1131 beginSafety = backwardOneContext(buf, contextRequirements, beginParse);
1132 for (p=beginParse; p>=beginSafety; p--) {
1133 style = BufGetCharacter(styleBuf, p-1);
1134 if (!EQUIVALENT_STYLE(style, beginStyle, firstPass2Style)) {
1135 beginSafety = p;
1136 break;
1139 } else {
1140 for (beginSafety=max(0,beginParse-1); beginSafety>0; beginSafety--) {
1141 style = BufGetCharacter(styleBuf, beginSafety);
1142 if (!EQUIVALENT_STYLE(style, beginStyle, firstPass2Style) ||
1143 BufGetCharacter(buf, beginSafety) == '\n') {
1144 beginSafety++;
1145 break;
1150 /* Parse one parse context beyond requested end to gurantee that parsing
1151 at endParse is complete, unless patterns can't cross line boundaries,
1152 in which case the end of the line is fine */
1153 if (endParse == 0)
1154 return 0;
1155 if (CAN_CROSS_LINE_BOUNDARIES(contextRequirements))
1156 endSafety = forwardOneContext(buf, contextRequirements, endParse);
1157 else if (endParse>=buf->length || (BufGetCharacter(buf,endParse-1)=='\n'))
1158 endSafety = endParse;
1159 else
1160 endSafety = min(buf->length, BufEndOfLine(buf, endParse) + 1);
1162 /* copy the buffer range into a string */
1163 string = BufGetRange(buf, beginSafety, endSafety);
1164 styleString = BufGetRange(styleBuf, beginSafety, endSafety);
1166 /* Parse it with pass 1 patterns */
1167 /* printf("parsing from %d thru %d\n", beginSafety, endSafety); */
1168 prevChar = getPrevChar(buf, beginParse);
1169 stringPtr = &string[beginParse-beginSafety];
1170 stylePtr = &styleString[beginParse-beginSafety];
1171 parseString(pass1Patterns, &stringPtr, &stylePtr, endParse-beginParse,
1172 &prevChar, False, delimiters);
1174 /* On non top-level patterns, parsing can end early */
1175 endParse = min(endParse, stringPtr-string + beginSafety);
1177 /* If there are no pass 2 patterns, we're done */
1178 if (pass2Patterns == NULL)
1179 goto parseDone;
1181 /* Parsing of pass 2 patterns is done only as necessary for determining
1182 where styles have changed. Find the area to avoid, which is already
1183 marked as changed (all inserted text and previously modified areas) */
1184 if (styleBuf->primary.selected) {
1185 modStart = styleBuf->primary.start;
1186 modEnd = styleBuf->primary.end;
1187 } else
1188 modStart = modEnd = 0;
1190 /* Re-parse the areas before the modification with pass 2 patterns, from
1191 beginSafety to far enough beyond modStart to gurantee that parsing at
1192 modStart is correct (pass 2 patterns must match entirely within one
1193 context distance, and only on the top level). If the parse region
1194 ends entirely before the modification or at or beyond modEnd, parse
1195 the whole thing and take advantage of the safety region which will be
1196 thrown away below. Otherwise save the contents of the safety region
1197 temporarily, and restore it after the parse. */
1198 if (beginSafety < modStart) {
1199 if (endSafety > modStart) {
1200 endPass2Safety = forwardOneContext(buf, contextRequirements,
1201 modStart);
1202 if (endPass2Safety + PASS_2_REPARSE_CHUNK_SIZE >= modEnd)
1203 endPass2Safety = endSafety;
1204 } else
1205 endPass2Safety = endSafety;
1206 prevChar = getPrevChar(buf, beginSafety);
1207 if (endPass2Safety == endSafety) {
1208 passTwoParseString(pass2Patterns, string, styleString,
1209 endParse - beginSafety, &prevChar, False, delimiters);
1210 goto parseDone;
1211 } else {
1212 tempLen = endPass2Safety - modStart;
1213 temp = XtMalloc(tempLen);
1214 strncpy(temp, &styleString[modStart-beginSafety], tempLen);
1215 passTwoParseString(pass2Patterns, string, styleString,
1216 modStart - beginSafety, &prevChar, False, delimiters);
1217 strncpy(&styleString[modStart-beginSafety], temp, tempLen);
1218 XtFree(temp);
1222 /* Re-parse the areas after the modification with pass 2 patterns, from
1223 modEnd to endSafety, with an additional safety region before modEnd
1224 to ensure that parsing at modEnd is correct. */
1225 if (endParse > modEnd) {
1226 if (beginSafety > modEnd) {
1227 prevChar = getPrevChar(buf, beginSafety);
1228 passTwoParseString(pass2Patterns, string, styleString,
1229 endParse - beginSafety, &prevChar, False, delimiters);
1230 } else {
1231 startPass2Safety = max(beginSafety,
1232 backwardOneContext(buf, contextRequirements, modEnd));
1233 tempLen = modEnd - startPass2Safety;
1234 temp = XtMalloc(tempLen);
1235 strncpy(temp, &styleString[startPass2Safety-beginSafety], tempLen);
1236 prevChar = getPrevChar(buf, startPass2Safety);
1237 passTwoParseString(pass2Patterns,
1238 &string[startPass2Safety-beginSafety],
1239 &styleString[startPass2Safety-beginSafety],
1240 endParse-startPass2Safety, &prevChar, False, delimiters);
1241 strncpy(&styleString[startPass2Safety-beginSafety], temp, tempLen);
1242 XtFree(temp);
1246 parseDone:
1248 /* Update the style buffer with the new style information, but only
1249 through endParse. Skip the safety region at the end */
1250 styleString[endParse-beginSafety] = '\0';
1251 modifyStyleBuf(styleBuf, &styleString[beginParse-beginSafety],
1252 beginParse, endParse, firstPass2Style);
1253 XtFree(styleString);
1254 XtFree(string);
1256 return endParse;
1260 ** Parses "string" according to compiled regular expressions in "pattern"
1261 ** until endRE is or errorRE are matched, or end of string is reached.
1262 ** Advances "string", "styleString" pointers to the next character past
1263 ** the end of the parsed section, and updates "prevChar" to reflect
1264 ** the new character before "string".
1265 ** If "anchored" is true, just scan the sub-pattern starting at the beginning
1266 ** of the string. "length" is how much of the string must be parsed, but
1267 ** "string" must still be null terminated, the termination indicating how
1268 ** far the string should be searched, and "length" the part which is actually
1269 ** required (the string may or may not be parsed beyond "length").
1271 ** Returns True if parsing was done and the parse succeeded. Returns False if
1272 ** the error pattern matched, if the end of the string was reached without
1273 ** matching the end expression, or in the unlikely event of an internal error.
1275 static int parseString(highlightDataRec *pattern, char **string,
1276 char **styleString, int length, char *prevChar, int anchored,
1277 char *delimiters)
1279 int i;
1280 char *stringPtr, *stylePtr, *startingStringPtr;
1281 signed char *subExpr;
1282 highlightDataRec *subPat = NULL, *subSubPat;
1284 if (length <= 0)
1285 return False;
1287 stringPtr = *string;
1288 stylePtr = *styleString;
1290 while(ExecRE(pattern->subPatternRE, NULL, stringPtr, anchored ? *string+1 :
1291 *string+length+1, False, *prevChar, '\0', delimiters)) {
1293 /* Combination of all sub-patterns and end pattern matched */
1294 /* printf("combined patterns RE matched at %d\n",
1295 pattern->subPatternRE->startp[0] - *string); */
1296 startingStringPtr = stringPtr;
1298 /* Fill in the pattern style for the text that was skipped over before
1299 the match, and advance the pointers to the start of the pattern */
1300 fillStyleString(&stringPtr, &stylePtr, pattern->subPatternRE->startp[0],
1301 pattern->style, delimiters, prevChar);
1303 /* If the combined pattern matched this pattern's end pattern, we're
1304 done. Fill in the style string, update the pointers, color the
1305 end expression if there were coloring sub-patterns, and return */
1306 if (pattern->endRE != NULL && ExecRE(pattern->endRE, NULL, stringPtr,
1307 stringPtr+1, False, *prevChar, '\0', delimiters)) {
1308 fillStyleString(&stringPtr, &stylePtr, pattern->endRE->endp[0],
1309 pattern->style, delimiters, prevChar);
1310 for (i=0;i<pattern->nSubPatterns; i++) {
1311 subPat = pattern->subPatterns[i];
1312 if (subPat->colorOnly) {
1313 for (subExpr=subPat->endSubexprs; *subExpr!=-1; subExpr++)
1314 recolorSubexpr(pattern->endRE, *subExpr, subPat->style,
1315 *string, *styleString);
1318 *string = stringPtr;
1319 *styleString = stylePtr;
1320 return True;
1323 /* If the combined pattern matched this pattern's error pattern, we're
1324 done. Fill in the style string, update the pointers, and return */
1325 if (pattern->errorRE != NULL && ExecRE(pattern->errorRE, NULL,
1326 stringPtr, stringPtr+1, False, *prevChar, '\0', delimiters)) {
1327 fillStyleString(&stringPtr, &stylePtr, pattern->errorRE->startp[0],
1328 pattern->style, delimiters, prevChar);
1329 *string = stringPtr;
1330 *styleString = stylePtr;
1331 return False;
1334 /* Figure out which sub-pattern matched */
1335 for (i=0; i<pattern->nSubPatterns; i++) {
1336 subPat = pattern->subPatterns[i];
1337 if (!subPat->colorOnly && ExecRE(subPat->startRE, NULL, stringPtr,
1338 stringPtr+1, False, *prevChar, '\0', delimiters))
1339 break;
1341 if (i == pattern->nSubPatterns) {
1342 fprintf(stderr, "Internal error, failed to match in parseString\n");
1343 return False;
1346 /* the sub-pattern is a simple match, just color it */
1347 if (subPat->subPatternRE == NULL) {
1348 fillStyleString(&stringPtr, &stylePtr, subPat->startRE->endp[0],
1349 subPat->style, delimiters, prevChar);
1351 /* Parse the remainder of the sub-pattern */
1352 } else {
1354 /* If parsing should start after the start pattern, advance
1355 to that point */
1356 if (!(subPat->flags & PARSE_SUBPATS_FROM_START))
1357 fillStyleString(&stringPtr, &stylePtr, subPat->startRE->endp[0],
1358 subPat->style, delimiters, prevChar);
1360 /* Parse to the end of the subPattern */
1361 parseString(subPat, &stringPtr, &stylePtr, length -
1362 (stringPtr - *string), prevChar, False, delimiters);
1365 /* If the sub-pattern has color-only sub-sub-patterns, add color
1366 based on the coloring sub-expression references */
1367 for (i=0; i<subPat->nSubPatterns; i++) {
1368 subSubPat = subPat->subPatterns[i];
1369 if (subSubPat->colorOnly) {
1370 for (subExpr=subSubPat->startSubexprs; *subExpr!=-1; subExpr++)
1371 recolorSubexpr(subPat->startRE, *subExpr, subSubPat->style,
1372 *string, *styleString);
1376 /* Make sure parsing progresses. If patterns match the empty string,
1377 they can get stuck and hang the process */
1378 if (stringPtr == startingStringPtr)
1379 fillStyleString(&stringPtr, &stylePtr, stringPtr+1,
1380 pattern->style, delimiters, prevChar);
1383 /* If this is an anchored match (must match on first character), and
1384 nothing matched, return False */
1385 if (anchored && stringPtr == *string)
1386 return False;
1388 /* Reached end of string, fill in the remaining text with pattern style
1389 (unless this was an anchored match) */
1390 if (!anchored)
1391 fillStyleString(&stringPtr, &stylePtr, *string+length, pattern->style,
1392 delimiters, prevChar);
1394 /* Advance the string and style pointers to the end of the parsed text */
1395 *string = stringPtr;
1396 *styleString = stylePtr;
1397 return pattern->endRE == NULL;
1401 ** Takes a string which has already been parsed through pass1 parsing and
1402 ** re-parses the areas where pass two patterns are applicable. Parameters
1403 ** have the same meaning as in parseString, except that strings aren't doubly
1404 ** indirect and string pointers are not updated.
1406 static void passTwoParseString(highlightDataRec *pattern, char *string,
1407 char *styleString, int length, char *prevChar, int anchored,
1408 char *delimiters)
1410 int inParseRegion = False;
1411 char *stylePtr, *stringPtr, temp, *parseStart = NULL, *parseEnd, *s, *c;
1412 int firstPass2Style = (unsigned char)pattern[1].style;
1414 for (c = string, s = styleString; ; c++, s++) {
1415 if (!inParseRegion && *c != '\0' && (*s == UNFINISHED_STYLE ||
1416 *s == PLAIN_STYLE || (unsigned char)*s >= firstPass2Style)) {
1417 parseStart = c;
1418 inParseRegion = True;
1420 if (inParseRegion && (*c == '\0' || !(*s == UNFINISHED_STYLE ||
1421 *s == PLAIN_STYLE || (unsigned char)*s >= firstPass2Style))) {
1422 parseEnd = c;
1423 if (parseStart != string)
1424 *prevChar = *(parseStart-1);
1425 stringPtr = parseStart;
1426 stylePtr = &styleString[parseStart - string];
1427 temp = *parseEnd;
1428 *parseEnd = '\0';
1429 /* printf("pass2 parsing %d chars\n", strlen(stringPtr)); */
1430 parseString(pattern, &stringPtr, &stylePtr,
1431 min(parseEnd - parseStart, length - (parseStart - string)),
1432 prevChar, False, delimiters);
1433 *parseEnd = temp;
1434 inParseRegion = False;
1436 if (*c == '\0' || (!inParseRegion && c - string >= length))
1437 break;
1442 ** Advance "stringPtr" and "stylePtr" until "stringPtr" == "toPtr", filling
1443 ** "stylePtr" with style "style". Can also optionally update the pre-string
1444 ** character, prevChar, which is fed to regular the expression matching
1445 ** routines for determining word and line boundaries at the start of the string.
1447 static void fillStyleString(char **stringPtr, char **stylePtr, char *toPtr,
1448 char style, char *delimiters, char *prevChar)
1450 int i;
1452 if (*stringPtr >= toPtr)
1453 return;
1455 for (i=0; i<toPtr-*stringPtr; i++)
1456 *(*stylePtr)++ = style;
1457 if (prevChar != NULL) *prevChar = *(toPtr-1);
1458 *stringPtr = toPtr;
1462 ** Incorporate changes from styleString into styleBuf, tracking changes
1463 ** in need of redisplay, and marking them for redisplay by the text
1464 ** modification callback in textDisp.c. "firstPass2Style" is necessary
1465 ** for distinguishing pass 2 styles which compare as equal to the unfinished
1466 ** style in the original buffer, from pass1 styles which signal a change.
1468 static void modifyStyleBuf(textBuffer *styleBuf, char *styleString,
1469 int startPos, int endPos, int firstPass2Style)
1471 char *c, bufChar;
1472 int pos, modStart, modEnd, minPos = INT_MAX, maxPos = 0;
1473 selection *sel = &styleBuf->primary;
1475 /* Skip the range already marked for redraw */
1476 if (sel->selected) {
1477 modStart = sel->start;
1478 modEnd = sel->end;
1479 } else
1480 modStart = modEnd = startPos;
1482 /* Compare the original style buffer (outside of the modified range) with
1483 the new string with which it will be updated, to find the extent of
1484 the modifications. Unfinished styles in the original match any
1485 pass 2 style */
1486 for (c=styleString, pos=startPos; pos<modStart && pos<endPos; c++, pos++) {
1487 bufChar = BufGetCharacter(styleBuf, pos);
1488 if (*c != bufChar && !(bufChar == UNFINISHED_STYLE &&
1489 (*c == PLAIN_STYLE || (unsigned char)*c >= firstPass2Style))) {
1490 if (pos < minPos) minPos = pos;
1491 if (pos > maxPos) maxPos = pos;
1494 for (c=&styleString[max(0, modEnd-startPos)], pos=max(modEnd, startPos);
1495 pos<endPos; c++, pos++) {
1496 bufChar = BufGetCharacter(styleBuf, pos);
1497 if (*c != bufChar && !(bufChar == UNFINISHED_STYLE &&
1498 (*c == PLAIN_STYLE || (unsigned char)*c >= firstPass2Style))) {
1499 if (pos < minPos) minPos = pos;
1500 if (pos+1 > maxPos) maxPos = pos+1;
1504 /* Make the modification */
1505 BufReplace(styleBuf, startPos, endPos, styleString);
1507 /* Mark or extend the range that needs to be redrawn. Even if no
1508 change was made, it's important to re-establish the selection,
1509 because it can get damaged by the BufReplace above */
1510 BufSelect(styleBuf, min(modStart, minPos), max(modEnd, maxPos));
1514 ** Return the last modified position in styleBuf (as marked by modifyStyleBuf
1515 ** by the convention used for conveying modification information to the
1516 ** text widget, which is selecting the text)
1518 static int lastModified(textBuffer *styleBuf)
1520 if (styleBuf->primary.selected)
1521 return max(0, styleBuf->primary.end);
1522 return 0;
1526 ** Compute the distance between two colors.
1529 static double colorDistance(const XColor *c1, const XColor *c2)
1531 /* This is done in RGB space, which is close, but not optimal. It's
1532 probably better to do it in HSV or YIQ space, however, that means
1533 a whole lot of extra conversions. This would allow us to weight
1534 the coordinates differently, e.g, prefer to match hue over
1535 brightness. */
1537 static const double scale = 65535;
1539 double tred = c1->red / scale - c2->red / scale;
1540 double tgreen = c1->green / scale - c2->green / scale;
1541 double tblue = c1->blue / scale - c2->blue / scale;
1543 /* use square Euclidian distance */
1544 return tred * tred + tgreen * tgreen + tblue * tblue;
1548 ** Allocate a read-only (shareable) colormap cell for a named color, from the
1549 ** the default colormap of the screen on which the widget (w) is displayed. If
1550 ** the colormap is full and there's no suitable substitute, print an error on
1551 ** stderr, and return the widget's foreground color as a backup.
1554 static Pixel allocColor(Widget w, const char *colorName)
1556 XColor colorDef;
1557 XColor *allColorDefs;
1558 Display *display = XtDisplay(w);
1559 Colormap cMap;
1560 Pixel foreground, bestPixel;
1561 double small = 1.0e9;
1562 int depth;
1563 unsigned int ncolors;
1564 unsigned long i, best = 0; /* pixel value */
1566 /* Get the correct colormap for compatability with the "best" visual
1567 feature in 5.2. Default visual of screen is no good here. */
1569 XtVaGetValues(w,
1570 XtNcolormap, &cMap,
1571 XtNdepth, &depth,
1572 XtNforeground, &foreground,
1573 NULL);
1575 bestPixel = foreground; /* Our last fallback */
1577 /* First, check for valid syntax */
1578 if (! XParseColor(display, cMap, colorName, &colorDef)) {
1579 fprintf(stderr, "NEdit: Color name %s not in database\n", colorName);
1580 return foreground;
1583 /* Attempt allocation of the exact color. */
1584 if (XAllocColor(display, cMap, &colorDef))
1585 return colorDef.pixel;
1587 /* ---------- Allocation failed, the colormap may be full. ---------- */
1589 #if 0
1590 printf("Couldn't allocate %d %d %d\n", colorDef.red, colorDef.green, colorDef.blue);
1591 #endif
1593 /* We can't do the nearest-match on other than 8 bit visuals because
1594 it just takes too long. */
1596 if (depth > 8) /* Oh no! */
1597 return foreground;
1599 /* Get the entire colormap so we can find the closet one. */
1600 ncolors = pow(2, depth);
1601 allColorDefs = malloc(ncolors * sizeof(XColor));
1602 memset(allColorDefs, 0, ncolors * sizeof(XColor));
1604 for (i = 0; i < ncolors; i++)
1605 allColorDefs[i].pixel = i;
1607 XQueryColors(display, cMap, allColorDefs, ncolors);
1609 /* Scan through each color, looking for the closest one. */
1610 for (i = 0; i < ncolors; i++)
1612 double dist = colorDistance(&allColorDefs[i], &colorDef);
1614 if (dist < small)
1616 best = i;
1617 small = dist;
1621 /* Legally try to acquire the shared color- we should loop through
1622 the shortest distances here. We could sort the map in order
1623 of decreasing distances and loop through it until one works. */
1625 if (XAllocColor(display, cMap, &allColorDefs[best]))
1626 bestPixel = allColorDefs[best].pixel;
1628 #if 0
1629 printf("Got %d %d %d, ", allColorDefs[best].red,
1630 allColorDefs[best].green,
1631 allColorDefs[best].blue);
1632 printf("That's %f off\n", small);
1633 #endif
1635 free(allColorDefs);
1636 return bestPixel;
1640 ** Get the character before position "pos" in buffer "buf"
1642 static char getPrevChar(textBuffer *buf, int pos)
1644 return pos == 0 ? '\0' : BufGetCharacter(buf, pos-1);
1648 ** compile a regular expression and present a user friendly dialog on failure.
1650 static regexp *compileREAndWarn(Widget parent, const char *re)
1652 regexp *compiledRE;
1653 char *compileMsg;
1655 compiledRE = CompileRE(re, &compileMsg, REDFLT_STANDARD);
1656 if (compiledRE == NULL) {
1657 DialogF(DF_WARN, parent, 1,
1658 "Error in syntax highlighting regular expression:\n%s\n%s",
1659 "Dismiss", re, compileMsg);
1660 return NULL;
1662 return compiledRE;
1665 static int parentStyleOf(const char *parentStyles, int style)
1667 return parentStyles[style-'A'];
1670 static int isParentStyle(const char *parentStyles, int style1, int style2)
1672 int p;
1674 for (p = parentStyleOf(parentStyles, style2); p != '\0';
1675 p = parentStyleOf(parentStyles, p))
1676 if (style1 == p)
1677 return TRUE;
1678 return FALSE;
1682 ** Discriminates patterns which can be used with parseString from those which
1683 ** can't. Leaf patterns are not suitable for parsing, because patterns
1684 ** contain the expressions used for parsing within the context of their own
1685 ** operation, i.e. the parent pattern initiates, and leaf patterns merely
1686 ** confirm and color. Returns TRUE if the pattern is suitable for parsing.
1688 static int patternIsParsable(highlightDataRec *pattern)
1690 return pattern != NULL && pattern->subPatternRE != NULL;
1694 ** Back up position pointed to by "pos" enough that parsing from that point
1695 ** on will satisfy context gurantees for pattern matching for modifications
1696 ** at pos. Returns the style with which to begin parsing. The caller is
1697 ** guranteed that parsing may safely BEGIN with that style, but not that it
1698 ** will continue at that level.
1700 ** This routine can be fooled if a continuous style run of more than one
1701 ** context distance in length is produced by multiple pattern matches which
1702 ** abut, rather than by a single continuous match. In this case the
1703 ** position returned by this routine may be a bad starting point which will
1704 ** result in an incorrect re-parse. However this will happen very rarely,
1705 ** and, if it does, is unlikely to result in incorrect highlighting.
1707 static int findSafeParseRestartPos(textBuffer *buf,
1708 windowHighlightData *highlightData, int *pos)
1710 int style, startStyle, checkBackTo, safeParseStart, i;
1711 reparseContext *context = &highlightData->contextRequirements;
1713 /* We must begin at least one context distance back from the change */
1714 *pos = backwardOneContext(buf, context, *pos);
1716 /* If the new position is outside of any styles or at the beginning of
1717 the buffer, this is a safe place to begin parsing, and we're done */
1718 if (*pos == 0)
1719 return PLAIN_STYLE;
1720 startStyle = BufGetCharacter(highlightData->styleBuffer, *pos);
1721 if (IS_PLAIN(startStyle))
1722 return PLAIN_STYLE;
1725 ** The new position is inside of a styled region, meaning, its pattern
1726 ** could potentially be affected by the modification.
1728 ** Follow the style back by enough context to ensure that if we don't find
1729 ** its beginning, at least we've found a safe place to begin parsing
1730 ** within the styled region.
1732 ** A safe starting position within a style is either at a style
1733 ** boundary, or far enough from the beginning and end of the style run
1734 ** to ensure that it's not within the start or end expression match
1735 ** (unfortunately, abutting styles can produce false runs so we're not
1736 ** really ensuring it, just making it likely).
1738 if (patternIsParsable(
1739 patternOfStyle(highlightData->pass1Patterns, startStyle))) {
1740 safeParseStart = backwardOneContext(buf, context, *pos);
1741 checkBackTo = backwardOneContext(buf, context, safeParseStart);
1742 } else {
1743 safeParseStart = 0;
1744 checkBackTo = 0;
1746 for (i = *pos-1; ; i--) {
1748 /* The start of the buffer is certainly a safe place to parse from */
1749 if (i == 0) {
1750 *pos = 0;
1751 return PLAIN_STYLE;
1754 /* If the style is preceded by a parent style, it's safe to parse
1755 with the parent style. */
1756 style = BufGetCharacter(highlightData->styleBuffer, i);
1757 if (isParentStyle(highlightData->parentStyles, style, startStyle)) {
1758 *pos = i + 1;
1759 return style;
1762 /* If the style is preceded by a child style, it's safe to resume
1763 parsing with the original style */
1764 if (isParentStyle(highlightData->parentStyles, startStyle, style)) {
1765 *pos = i + 1;
1766 return startStyle;
1769 /* If the style is preceded by an unrelated style, it's safe to
1770 resume parsing with PLAIN_STYLE */
1771 if (startStyle != style) {
1772 *pos = i + 1;
1773 return PLAIN_STYLE;
1776 /* If the style is parsable and didn't change for one whole context
1777 distance on either side of safeParseStart, safeParseStart is a
1778 reasonable guess at a place to start parsing. */
1779 if (i == checkBackTo) {
1780 *pos = safeParseStart;
1781 return startStyle;
1787 ** Return a position far enough back in "buf" from "fromPos" to give patterns
1788 ** their guranteed amount of context for matching (from "context"). If
1789 ** backing up by lines yields the greater distance, the returned position will
1790 ** be to the newline character before the start of the line, rather than to
1791 ** the first character of the line. (I did this because earlier prototypes of
1792 ** the syntax highlighting code, which were based on single-line context, used
1793 ** this to ensure that line-spanning expressions would be detected. I think
1794 ** it may reduce some 2 line context requirements to one line, at a cost of
1795 ** only one extra character, but I'm not sure, and my brain hurts from
1796 ** thinking about it).
1798 static int backwardOneContext(textBuffer *buf, reparseContext *context,
1799 int fromPos)
1801 if (context->nLines == 0)
1802 return max(0, fromPos - context->nChars);
1803 else if (context->nChars == 0)
1804 return max(0,
1805 BufCountBackwardNLines(buf, fromPos, context->nLines-1) - 1);
1806 else
1807 return max(0, min(max(0, BufCountBackwardNLines(buf, fromPos,
1808 context->nLines-1) -1), fromPos - context->nChars));
1812 ** Return a position far enough forward in "buf" from "fromPos" to ensure
1813 ** that patterns are given their required amount of context for matching
1814 ** (from "context"). If moving forward by lines yields the greater
1815 ** distance, the returned position will be the first character of of the
1816 ** next line, rather than the newline character at the end (see notes in
1817 ** backwardOneContext).
1819 static int forwardOneContext(textBuffer *buf, reparseContext *context,
1820 int fromPos)
1822 if (context->nLines == 0)
1823 return min(buf->length, fromPos + context->nChars);
1824 else if (context->nChars == 0)
1825 return min(buf->length,
1826 BufCountForwardNLines(buf, fromPos, context->nLines));
1827 else
1828 return min(buf->length, max(BufCountForwardNLines(buf, fromPos,
1829 context->nLines), fromPos + context->nChars));
1833 ** Change styles in the portion of "styleString" to "style" where a particular
1834 ** sub-expression, "subExpr", of regular expression "re" applies to the
1835 ** corresponding portion of "string".
1837 static void recolorSubexpr(regexp *re, int subexpr, int style, char *string,
1838 char *styleString)
1840 char *stringPtr, *stylePtr;
1842 stringPtr = re->startp[subexpr];
1843 stylePtr = &styleString[stringPtr - string];
1844 fillStyleString(&stringPtr, &stylePtr, re->endp[subexpr], style, NULL,
1845 NULL);
1849 ** Search for a pattern in pattern list "patterns" with style "style"
1851 static highlightDataRec *patternOfStyle(highlightDataRec *patterns, int style)
1853 int i;
1855 for (i=0; patterns[i].style!=0; i++)
1856 if (patterns[i].style == style)
1857 return &patterns[i];
1858 if (style == PLAIN_STYLE || style == UNFINISHED_STYLE)
1859 return &patterns[0];
1860 return NULL;
1863 static int max(int i1, int i2)
1865 return i1 >= i2 ? i1 : i2;
1868 static int min(int i1, int i2)
1870 return i1 <= i2 ? i1 : i2;
1873 static int indexOfNamedPattern(highlightPattern *patList, int nPats,
1874 const char *patName)
1876 int i;
1878 if (patName == NULL)
1879 return -1;
1880 for (i=0; i<nPats; i++)
1881 if (!strcmp(patList[i].name, patName))
1882 return i;
1883 return -1;
1886 static int findTopLevelParentIndex(highlightPattern *patList, int nPats,
1887 int index)
1889 int topIndex;
1891 topIndex = index;
1892 while (patList[topIndex].subPatternOf != NULL) {
1893 topIndex = indexOfNamedPattern(patList, nPats,
1894 patList[topIndex].subPatternOf);
1895 if (index==topIndex)
1896 return -1; /* amai: circular dependency ?! */
1898 return topIndex;
1902 ** Re-size (or re-height, anyhow) a window after adding or removing
1903 ** highlight fonts has changed the required vertical spacing (horizontal
1904 ** spacing is determined by the primary font, which doesn't change).
1906 ** Note that this messes up the window manager's height increment hint,
1907 ** which must be subsequently reset by UpdateWMSizeHints.
1909 static void updateWindowHeight(WindowInfo *window, int oldFontHeight)
1911 int i, borderHeight, marginHeight;
1912 Dimension windowHeight, textAreaHeight, textHeight, newWindowHeight;
1914 /* Decompose the window height into the part devoted to displaying
1915 text (textHeight) and the non-text part (boderHeight) */
1916 XtVaGetValues(window->shell, XmNheight, &windowHeight, NULL);
1917 XtVaGetValues(window->textArea, XmNheight, &textAreaHeight,
1918 textNmarginHeight, &marginHeight, NULL);
1919 textHeight = textAreaHeight - 2*marginHeight;
1920 for (i=0; i<window->nPanes; i++) {
1921 XtVaGetValues(window->textPanes[i], XmNheight, &textAreaHeight, NULL);
1922 textHeight += textAreaHeight - 2*marginHeight;
1924 borderHeight = windowHeight - textHeight;
1926 /* Calculate a new window height appropriate for the new font */
1927 newWindowHeight = (textHeight*getFontHeight(window)) / oldFontHeight +
1928 borderHeight;
1930 /* Many window managers enforce window size increments even on client resize
1931 requests. Our height increment is probably wrong because it is still
1932 set for the previous font. Set the new height in advance, before
1933 attempting to resize. */
1934 XtVaSetValues(window->shell, XmNheightInc, getFontHeight(window), NULL);
1936 /* Re-size the window */
1937 XtVaSetValues(window->shell, XmNheight, newWindowHeight, NULL);
1941 ** Find the height currently being used to display text, which is
1942 ** a composite of all of the active highlighting fonts as determined by the
1943 ** text display component
1945 static int getFontHeight(WindowInfo *window)
1947 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
1949 return textD->ascent + textD->descent;