1 static const char CVSID
[] = "$Id: highlight.c,v 1.54 2006/12/02 10:27:06 yooden Exp $";
2 /*******************************************************************************
4 * highlight.c -- Nirvana Editor syntax highlighting (text coloring and font *
5 * selected by file content *
7 * Copyright (C) 1999 Mark Edel *
9 * This is free software; you can redistribute it and/or modify it under the *
10 * terms of the GNU General Public License as published by the Free Software *
11 * Foundation; either version 2 of the License, or (at your option) any later *
12 * version. In addition, you may distribute version of this program linked to *
13 * Motif or Open Motif. See README for details. *
15 * This software is distributed in the hope that it will be useful, but WITHOUT *
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
20 * You should have received a copy of the GNU General Public License along with *
21 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
22 * Place, Suite 330, Boston, MA 02111-1307 USA *
24 * Nirvana Text Editor *
27 * Written by Mark Edel *
29 *******************************************************************************/
32 #include "../config.h"
35 #include "highlight.h"
41 #include "regularExp.h"
42 #include "highlightData.h"
43 #include "preferences.h"
45 #include "../util/misc.h"
46 #include "../util/DialogF.h"
54 #include "../util/VMSparam.h"
57 #include <sys/param.h>
64 #include <Xm/PrimitiveP.h>
71 /* How much re-parsing to do when an unfinished style is encountered */
72 #define PASS_2_REPARSE_CHUNK_SIZE 1000
74 /* Initial forward expansion of parsing region in incremental reparsing,
75 when style changes propagate forward beyond the original modification.
76 This distance is increased by a factor of two for each subsequent step. */
77 #define REPARSE_CHUNK_SIZE 80
79 /* Meanings of style buffer characters (styles). Don't use plain 'A' or 'B';
80 it causes problems with EBCDIC coding (possibly negative offsets when
82 #define UNFINISHED_STYLE ASCII_A
83 #define PLAIN_STYLE (ASCII_A+1)
84 #define IS_PLAIN(style) (style == PLAIN_STYLE || style == UNFINISHED_STYLE)
85 #define IS_STYLED(style) (style != PLAIN_STYLE && style != UNFINISHED_STYLE)
87 /* Compare two styles where one of the styles may not yet have been processed
88 with pass2 patterns */
89 #define EQUIVALENT_STYLE(style1, style2, firstPass2Style) (style1 == style2 || \
90 (style1 == UNFINISHED_STYLE && \
91 (style2 == PLAIN_STYLE || (unsigned char)style2 >= firstPass2Style)) || \
92 (style2 == UNFINISHED_STYLE && \
93 (style1 == PLAIN_STYLE || (unsigned char)style1 >= firstPass2Style)))
95 /* Scanning context can be reduced (with big efficiency gains) if we
96 know that patterns can't cross line boundaries, which is implied
97 by a context requirement of 1 line and 0 characters */
98 #define CAN_CROSS_LINE_BOUNDARIES(contextRequirements) \
99 (contextRequirements->nLines != 1 || contextRequirements->nChars != 0)
101 /* "Compiled" version of pattern specification */
102 typedef struct _highlightDataRec
{
106 regexp
*subPatternRE
;
109 signed char startSubexprs
[NSUBEXP
+1];
110 signed char endSubexprs
[NSUBEXP
+1];
113 int nSubBranches
; /* Number of top-level branches of subPatternRE */
115 struct _highlightDataRec
**subPatterns
;
118 /* Context requirements for incremental reparsing of a pattern set */
124 /* Data structure attached to window to hold all syntax highlighting
125 information (for both drawing and incremental reparsing) */
127 highlightDataRec
*pass1Patterns
;
128 highlightDataRec
*pass2Patterns
;
130 reparseContext contextRequirements
;
131 styleTableEntry
*styleTable
;
133 textBuffer
*styleBuffer
;
134 patternSet
*patternSetForWindow
;
135 } windowHighlightData
;
137 static windowHighlightData
*createHighlightData(WindowInfo
*window
,
139 static void freeHighlightData(windowHighlightData
*hd
);
140 static patternSet
*findPatternsForWindow(WindowInfo
*window
, int warn
);
141 static highlightDataRec
*compilePatterns(Widget dialogParent
,
142 highlightPattern
*patternSrc
, int nPatterns
);
143 static void freePatterns(highlightDataRec
*patterns
);
144 static void handleUnparsedRegion(const WindowInfo
* win
, textBuffer
* styleBuf
,
146 static void handleUnparsedRegionCB(const textDisp
* textD
, const int pos
,
148 static void incrementalReparse(windowHighlightData
*highlightData
,
149 textBuffer
*buf
, int pos
, int nInserted
, const char *delimiters
);
150 static int parseBufferRange(highlightDataRec
*pass1Patterns
,
151 highlightDataRec
*pass2Patterns
, textBuffer
*buf
, textBuffer
*styleBuf
,
152 reparseContext
*contextRequirements
, int beginParse
, int endParse
,
153 const char *delimiters
);
154 static int parseString(highlightDataRec
*pattern
, const char **string
,
155 char **styleString
, int length
, char *prevChar
, int anchored
,
156 const char *delimiters
, const char* lookBehindTo
, const char* match_till
);
157 static void passTwoParseString(highlightDataRec
*pattern
, char *string
,
158 char *styleString
, int length
, char *prevChar
, int anchored
,
159 const char *delimiters
, const char* lookBehindTo
, const char* match_till
);
160 static void fillStyleString(const char **stringPtr
, char **stylePtr
,
161 const char *toPtr
, char style
, char *prevChar
);
162 static void modifyStyleBuf(textBuffer
*styleBuf
, char *styleString
,
163 int startPos
, int endPos
, int firstPass2Style
);
164 static int lastModified(textBuffer
*styleBuf
);
165 static int max(int i1
, int i2
);
166 static int min(int i1
, int i2
);
167 static char getPrevChar(textBuffer
*buf
, int pos
);
168 static regexp
*compileREAndWarn(Widget parent
, const char *re
);
169 static int parentStyleOf(const char *parentStyles
, int style
);
170 static int isParentStyle(const char *parentStyles
, int style1
, int style2
);
171 static int findSafeParseRestartPos(textBuffer
*buf
,
172 windowHighlightData
*highlightData
, int *pos
);
173 static int backwardOneContext(textBuffer
*buf
, reparseContext
*context
,
175 static int forwardOneContext(textBuffer
*buf
, reparseContext
*context
,
177 static void recolorSubexpr(regexp
*re
, int subexpr
, int style
,
178 const char *string
, char *styleString
);
179 static int indexOfNamedPattern(highlightPattern
*patList
, int nPats
,
180 const char *patName
);
181 static int findTopLevelParentIndex(highlightPattern
*patList
, int nPats
,
183 static highlightDataRec
*patternOfStyle(highlightDataRec
*patterns
, int style
);
184 static void updateWindowHeight(WindowInfo
*window
, int oldFontHeight
);
185 static int getFontHeight(WindowInfo
*window
);
186 static styleTableEntry
*styleTableEntryOfCode(WindowInfo
*window
, int hCode
);
189 ** Buffer modification callback for triggering re-parsing of modified
190 ** text and keeping the style buffer synchronized with the text buffer.
191 ** This must be attached to the the text buffer BEFORE any widget text
192 ** display callbacks, so it can get the style buffer ready to be used
193 ** by the text display routines.
195 ** Update the style buffer for changes to the text, and mark any style
196 ** changes by selecting the region in the style buffer. This strange
197 ** protocol of informing the text display to redraw style changes by
198 ** making selections in the style buffer is used because this routine
199 ** is intended to be called BEFORE the text display callback paints the
200 ** text (to minimize redraws and, most importantly, to synchronize the
201 ** style buffer with the text buffer). If we redraw now, the text
202 ** display hasn't yet processed the modification, redrawing later is
203 ** not only complicated, it will double-draw almost everything typed.
205 ** Note: This routine must be kept efficient. It is called for every
208 void SyntaxHighlightModifyCB(int pos
, int nInserted
, int nDeleted
,
209 int nRestyled
, const char *deletedText
, void *cbArg
)
211 WindowInfo
*window
= (WindowInfo
*)cbArg
;
213 *highlightData
= (windowHighlightData
*)window
->highlightData
;
215 if (highlightData
== NULL
)
218 /* Restyling-only modifications (usually a primary or secondary selection)
219 don't require any processing, but clear out the style buffer selection
220 so the widget doesn't think it has to keep redrawing the old area */
221 if (nInserted
== 0 && nDeleted
== 0) {
222 BufUnselect(highlightData
->styleBuffer
);
226 /* First and foremost, the style buffer must track the text buffer
227 accurately and correctly */
232 insStyle
= XtMalloc(sizeof(char) * (nInserted
+ 1));
233 for (i
=0; i
<nInserted
; i
++)
234 insStyle
[i
] = UNFINISHED_STYLE
;
236 BufReplace(highlightData
->styleBuffer
, pos
, pos
+nDeleted
, insStyle
);
239 BufRemove(highlightData
->styleBuffer
, pos
, pos
+nDeleted
);
242 /* Mark the changed region in the style buffer as requiring redraw. This
243 is not necessary for getting it redrawn, it will be redrawn anyhow by
244 the text display callback, but it clears the previous selection and
245 saves the modifyStyleBuf routine from unnecessary work in tracking
246 changes that are already scheduled for redraw */
247 BufSelect(highlightData
->styleBuffer
, pos
, pos
+nInserted
);
249 /* Re-parse around the changed region */
250 if (highlightData
->pass1Patterns
)
251 incrementalReparse(highlightData
, window
->buffer
, pos
, nInserted
,
252 GetWindowDelimiters(window
));
256 ** Turn on syntax highlighting. If "warn" is true, warn the user when it
257 ** can't be done, otherwise, just return.
259 void StartHighlighting(WindowInfo
*window
, int warn
)
261 patternSet
*patterns
;
262 windowHighlightData
*highlightData
;
263 char *stylePtr
, *styleString
;
264 const char *stringPtr
, *bufString
;
265 char prevChar
= '\0';
266 int i
, oldFontHeight
;
268 /* Find the pattern set matching the window's current
269 language mode, tell the user if it can't be done */
270 patterns
= findPatternsForWindow(window
, warn
);
271 if (patterns
== NULL
)
274 /* Compile the patterns */
275 highlightData
= createHighlightData(window
, patterns
);
276 if (highlightData
== NULL
)
279 /* Prepare for a long delay, refresh display and put up a watch cursor */
280 BeginWait(window
->shell
);
281 XmUpdateDisplay(window
->shell
);
283 /* Parse the buffer with pass 1 patterns. If there are none, initialize
284 the style buffer to all UNFINISHED_STYLE to trigger parsing later */
285 stylePtr
= styleString
= XtMalloc(window
->buffer
->length
+ 1);
286 if (highlightData
->pass1Patterns
== NULL
) {
287 for (i
=0; i
<window
->buffer
->length
; i
++)
288 *stylePtr
++ = UNFINISHED_STYLE
;
290 stringPtr
= bufString
= BufAsString(window
->buffer
);
291 parseString(highlightData
->pass1Patterns
, &stringPtr
, &stylePtr
,
292 window
->buffer
->length
, &prevChar
, False
,
293 GetWindowDelimiters(window
), bufString
, NULL
);
296 BufSetAll(highlightData
->styleBuffer
, styleString
);
299 /* install highlight pattern data in the window data structure */
300 window
->highlightData
= highlightData
;
302 /* Get the height of the current font in the window, to be used after
303 highlighting is turned on to resize the window to make room for
304 additional highlight fonts which may be sized differently */
305 oldFontHeight
= getFontHeight(window
);
307 /* Attach highlight information to text widgets in each pane */
308 AttachHighlightToWidget(window
->textArea
, window
);
309 for (i
=0; i
<window
->nPanes
; i
++)
310 AttachHighlightToWidget(window
->textPanes
[i
], window
);
312 /* Re-size the window to fit the highlight fonts properly & tell the
313 window manager about the potential line-height change as well */
314 updateWindowHeight(window
, oldFontHeight
);
315 UpdateWMSizeHints(window
);
316 UpdateMinPaneHeights(window
);
318 /* Make sure that if the window has grown, the additional area gets
319 repainted. Otherwise, it is possible that the area gets moved before a
320 repaint event is received and the area doesn't get repainted at all
321 (eg. because of a -line command line argument that moves the text). */
322 XmUpdateDisplay(window
->shell
);
323 EndWait(window
->shell
);
327 ** Turn off syntax highlighting and free style buffer, compiled patterns, and
330 void StopHighlighting(WindowInfo
*window
)
332 int i
, oldFontHeight
;
334 if (window
->highlightData
==NULL
)
337 /* Get the line height being used by the highlight fonts in the window,
338 to be used after highlighting is turned off to resize the window
339 back to the line height of the primary font */
340 oldFontHeight
= getFontHeight(window
);
342 /* Free and remove the highlight data from the window */
343 freeHighlightData((windowHighlightData
*)window
->highlightData
);
344 window
->highlightData
= NULL
;
346 /* Remove and detach style buffer and style table from all text
347 display(s) of window, and redisplay without highlighting */
348 RemoveWidgetHighlight(window
->textArea
);
349 for (i
=0; i
<window
->nPanes
; i
++)
350 RemoveWidgetHighlight(window
->textPanes
[i
]);
352 /* Re-size the window to fit the primary font properly & tell the window
353 manager about the potential line-height change as well */
354 updateWindowHeight(window
, oldFontHeight
);
355 UpdateWMSizeHints(window
);
356 UpdateMinPaneHeights(window
);
360 ** Free highlighting data from a window destined for destruction, without
363 void FreeHighlightingData(WindowInfo
*window
)
367 if (window
->highlightData
== NULL
)
370 /* Free and remove the highlight data from the window */
371 freeHighlightData((windowHighlightData
*)window
->highlightData
);
372 window
->highlightData
= NULL
;
374 /* The text display may make a last desperate attempt to access highlight
375 information when it is destroyed, which would be a disaster. */
376 ((TextWidget
)window
->textArea
)->text
.textD
->styleBuffer
= NULL
;
377 for (i
=0; i
<window
->nPanes
; i
++)
378 ((TextWidget
)window
->textPanes
[i
])->text
.textD
->styleBuffer
= NULL
;
382 ** Attach style information from a window's highlight data to a
383 ** text widget and redisplay.
385 void AttachHighlightToWidget(Widget widget
, WindowInfo
*window
)
387 windowHighlightData
*highlightData
=
388 (windowHighlightData
*)window
->highlightData
;
390 TextDAttachHighlightData(((TextWidget
)widget
)->text
.textD
,
391 highlightData
->styleBuffer
, highlightData
->styleTable
,
392 highlightData
->nStyles
, UNFINISHED_STYLE
, handleUnparsedRegionCB
,
397 ** Remove style information from a text widget and redisplay it.
399 void RemoveWidgetHighlight(Widget widget
)
401 TextDAttachHighlightData(((TextWidget
)widget
)->text
.textD
,
402 NULL
, NULL
, 0, UNFINISHED_STYLE
, NULL
, NULL
);
406 ** Change highlight fonts and/or styles in a highlighted window, without
409 void UpdateHighlightStyles(WindowInfo
*window
)
411 patternSet
*patterns
;
412 windowHighlightData
*highlightData
;
413 windowHighlightData
*oldHighlightData
=
414 (windowHighlightData
*)window
->highlightData
;
415 textBuffer
*styleBuffer
;
418 /* Do nothing if window not highlighted */
419 if (window
->highlightData
== NULL
)
422 /* Find the pattern set for the window's current language mode */
423 patterns
= findPatternsForWindow(window
, False
);
424 if (patterns
== NULL
) {
425 StopHighlighting(window
);
429 /* Build new patterns */
430 highlightData
= createHighlightData(window
, patterns
);
431 if (highlightData
== NULL
) {
432 StopHighlighting(window
);
436 /* Update highlight pattern data in the window data structure, but
437 preserve all of the effort that went in to parsing the buffer
438 by swapping it with the empty one in highlightData (which is then
439 freed in freeHighlightData) */
440 styleBuffer
= oldHighlightData
->styleBuffer
;
441 oldHighlightData
->styleBuffer
= highlightData
->styleBuffer
;
442 freeHighlightData(oldHighlightData
);
443 highlightData
->styleBuffer
= styleBuffer
;
444 window
->highlightData
= highlightData
;
446 /* Attach new highlight information to text widgets in each pane
448 AttachHighlightToWidget(window
->textArea
, window
);
449 for (i
=0; i
<window
->nPanes
; i
++)
450 AttachHighlightToWidget(window
->textPanes
[i
], window
);
454 ** Do a test compile of patterns in "patSet" and report problems to the
455 ** user via dialog. Returns True if patterns are ok.
457 ** This is somewhat kludgy in that it uses createHighlightData, which
458 ** requires a window to find the fonts to use, and just uses a random
459 ** window from the window list. Since the window is used to get the
460 ** dialog parent as well, in non-popups-under-pointer mode, these dialogs
461 ** will appear in odd places on the screen.
463 int TestHighlightPatterns(patternSet
*patSet
)
465 windowHighlightData
*highlightData
;
467 /* Compile the patterns (passing a random window as a source for fonts, and
468 parent for dialogs, since we really don't care what fonts are used) */
469 highlightData
= createHighlightData(WindowList
, patSet
);
470 if (highlightData
== NULL
)
472 freeHighlightData(highlightData
);
477 ** Returns the highlight style of the character at a given position of a
478 ** window. To avoid breaking encapsulation, the highlight style is converted
479 ** to a void* pointer (no other module has to know that characters are used
480 ** to represent highlight styles; that would complicate future extensions).
481 ** Returns NULL if the window has highlighting turned off.
482 ** The only guarantee that this function offers, is that when the same
483 ** pointer is returned for two positions, the corresponding characters have
484 ** the same highlight style.
486 void* GetHighlightInfo(WindowInfo
*window
, int pos
)
489 highlightDataRec
*pattern
= NULL
;
490 windowHighlightData
*highlightData
=
491 (windowHighlightData
*)window
->highlightData
;
495 /* Be careful with signed/unsigned conversions. NO conversion here! */
496 style
= (int)BufGetCharacter(highlightData
->styleBuffer
, pos
);
498 /* Beware of unparsed regions. */
499 if (style
== UNFINISHED_STYLE
) {
500 handleUnparsedRegion(window
, highlightData
->styleBuffer
, pos
);
501 style
= (int)BufGetCharacter(highlightData
->styleBuffer
, pos
);
504 if (highlightData
->pass1Patterns
) {
505 pattern
= patternOfStyle(highlightData
->pass1Patterns
, style
);
508 if (!pattern
&& highlightData
->pass2Patterns
) {
509 pattern
= patternOfStyle(highlightData
->pass2Patterns
, style
);
515 return (void*)pattern
->userStyleIndex
;
519 ** Free allocated memory associated with highlight data, including compiled
520 ** regular expressions, style buffer and style table. Note: be sure to
521 ** NULL out the widget references to the objects in this structure before
522 ** calling this. Because of the slow, multi-phase destruction of
523 ** widgets, this data can be referenced even AFTER destroying the widget.
525 static void freeHighlightData(windowHighlightData
*hd
)
529 if (hd
->pass1Patterns
!= NULL
)
530 freePatterns(hd
->pass1Patterns
);
531 if (hd
->pass2Patterns
!= NULL
)
532 freePatterns(hd
->pass2Patterns
);
533 XtFree(hd
->parentStyles
);
534 BufFree(hd
->styleBuffer
);
535 XtFree((char *)hd
->styleTable
);
540 ** Find the pattern set matching the window's current language mode, or
541 ** tell the user if it can't be done (if warn is True) and return NULL.
543 static patternSet
*findPatternsForWindow(WindowInfo
*window
, int warn
)
545 patternSet
*patterns
;
548 /* Find the window's language mode. If none is set, warn user */
549 modeName
= LanguageModeName(window
->languageMode
);
550 if (modeName
== NULL
) {
552 DialogF(DF_WARN
, window
->shell
, 1, "Language Mode",
553 "No language-specific mode has been set for this file.\n\n"
554 "To use syntax highlighting in this window, please select a\n"
555 "language from the Preferences -> Language Modes menu.\n\n"
556 "New language modes and syntax highlighting patterns can be\n"
557 "added via Preferences -> Default Settings -> Language Modes,\n"
558 "and Preferences -> Default Settings -> Syntax Highlighting.",
563 /* Look up the appropriate pattern for the language */
564 patterns
= FindPatternSet(modeName
);
565 if (patterns
== NULL
)
569 DialogF(DF_WARN
, window
->shell
, 1, "Language Mode",
570 "Syntax highlighting is not available in language\n"
572 "You can create new syntax highlight patterns in the\n"
573 "Preferences -> Default Settings -> Syntax Highlighting\n"
574 "dialog, or choose a different language mode from:\n"
575 "Preferences -> Language Mode.", "OK", modeName
);
584 ** Create complete syntax highlighting information from "patternSrc", using
585 ** highlighting fonts from "window", includes pattern compilation. If errors
586 ** are encountered, warns user with a dialog and returns NULL. To free the
587 ** allocated components of the returned data structure, use freeHighlightData.
589 static windowHighlightData
*createHighlightData(WindowInfo
*window
,
592 highlightPattern
*patternSrc
= patSet
->patterns
;
593 int nPatterns
= patSet
->nPatterns
;
594 int contextLines
= patSet
->lineContext
;
595 int contextChars
= patSet
->charContext
;
596 int i
, nPass1Patterns
, nPass2Patterns
;
597 int noPass1
, noPass2
;
598 char *parentStyles
, *parentStylesPtr
, *parentName
;
599 highlightPattern
*pass1PatternSrc
, *pass2PatternSrc
, *p1Ptr
, *p2Ptr
;
600 styleTableEntry
*styleTable
, *styleTablePtr
;
601 textBuffer
*styleBuf
;
602 highlightDataRec
*pass1Pats
, *pass2Pats
;
603 windowHighlightData
*highlightData
;
605 /* The highlighting code can't handle empty pattern sets, quietly say no */
611 /* Check that the styles and parent pattern names actually exist */
612 if (!NamedStyleExists("Plain"))
614 DialogF(DF_WARN
, window
->shell
, 1, "Highlight Style",
615 "Highlight style \"Plain\" is missing", "OK");
619 for (i
=0; i
<nPatterns
; i
++)
621 if (patternSrc
[i
].subPatternOf
!= NULL
622 && indexOfNamedPattern(patternSrc
, nPatterns
,
623 patternSrc
[i
].subPatternOf
) == -1)
625 DialogF(DF_WARN
, window
->shell
, 1, "Parent Pattern",
626 "Parent field \"%s\" in pattern \"%s\"\n"
627 "does not match any highlight patterns in this set",
628 "OK", patternSrc
[i
].subPatternOf
, patternSrc
[i
].name
);
633 for (i
=0; i
<nPatterns
; i
++)
635 if (!NamedStyleExists(patternSrc
[i
].style
))
637 DialogF(DF_WARN
, window
->shell
, 1, "Highlight Style",
638 "Style \"%s\" named in pattern \"%s\"\n"
639 "does not match any existing style", "OK",
640 patternSrc
[i
].style
, patternSrc
[i
].name
);
645 /* Make DEFER_PARSING flags agree with top level patterns (originally,
646 individual flags had to be correct and were checked here, but dialog now
647 shows this setting only on top patterns which is much less confusing) */
648 for (i
= 0; i
< nPatterns
; i
++)
650 if (patternSrc
[i
].subPatternOf
!= NULL
)
654 parentindex
=findTopLevelParentIndex(patternSrc
, nPatterns
, i
);
657 DialogF(DF_WARN
, window
->shell
, 1, "Parent Pattern",
658 "Pattern \"%s\" does not have valid parent", "OK",
663 if (patternSrc
[parentindex
].flags
& DEFER_PARSING
)
665 patternSrc
[i
].flags
|= DEFER_PARSING
;
668 patternSrc
[i
].flags
&= ~DEFER_PARSING
;
673 /* Sort patterns into those to be used in pass 1 parsing, and those to
674 be used in pass 2, and add default pattern (0) to each list */
677 for (i
=0; i
<nPatterns
; i
++)
678 if (patternSrc
[i
].flags
& DEFER_PARSING
)
682 p1Ptr
= pass1PatternSrc
= (highlightPattern
*)XtMalloc(
683 sizeof(highlightPattern
) * nPass1Patterns
);
684 p2Ptr
= pass2PatternSrc
= (highlightPattern
*)XtMalloc(
685 sizeof(highlightPattern
) * nPass2Patterns
);
686 p1Ptr
->name
= p2Ptr
->name
= "";
687 p1Ptr
->startRE
= p2Ptr
->startRE
= NULL
;
688 p1Ptr
->endRE
= p2Ptr
->endRE
= NULL
;
689 p1Ptr
->errorRE
= p2Ptr
->errorRE
= NULL
;
690 p1Ptr
->style
= p2Ptr
->style
= "Plain";
691 p1Ptr
->subPatternOf
= p2Ptr
->subPatternOf
= NULL
;
692 p1Ptr
->flags
= p2Ptr
->flags
= 0;
694 for (i
=0; i
<nPatterns
; i
++) {
695 if (patternSrc
[i
].flags
& DEFER_PARSING
)
696 *p2Ptr
++ = patternSrc
[i
];
698 *p1Ptr
++ = patternSrc
[i
];
701 /* If a particular pass is empty except for the default pattern, don't
702 bother compiling it or setting up styles */
703 if (nPass1Patterns
== 1)
705 if (nPass2Patterns
== 1)
708 /* Compile patterns */
709 if (nPass1Patterns
== 0)
712 pass1Pats
= compilePatterns(window
->shell
, pass1PatternSrc
,
714 if (pass1Pats
== NULL
)
717 if (nPass2Patterns
== 0)
720 pass2Pats
= compilePatterns(window
->shell
, pass2PatternSrc
,
722 if (pass2Pats
== NULL
)
726 /* Set pattern styles. If there are pass 2 patterns, pass 1 pattern
727 0 should have a default style of UNFINISHED_STYLE. With no pass 2
728 patterns, unstyled areas of pass 1 patterns should be PLAIN_STYLE
729 to avoid triggering re-parsing every time they are encountered */
730 noPass1
= nPass1Patterns
== 0;
731 noPass2
= nPass2Patterns
== 0;
733 pass1Pats
[0].style
= PLAIN_STYLE
;
735 pass2Pats
[0].style
= PLAIN_STYLE
;
737 pass1Pats
[0].style
= UNFINISHED_STYLE
;
738 pass2Pats
[0].style
= PLAIN_STYLE
;
740 for (i
=1; i
<nPass1Patterns
; i
++)
741 pass1Pats
[i
].style
= PLAIN_STYLE
+ i
;
742 for (i
=1; i
<nPass2Patterns
; i
++)
743 pass2Pats
[i
].style
= PLAIN_STYLE
+ (noPass1
? 0 : nPass1Patterns
-1) + i
;
745 /* Create table for finding parent styles */
746 parentStylesPtr
= parentStyles
= XtMalloc(nPass1Patterns
+nPass2Patterns
+2);
747 *parentStylesPtr
++ = '\0';
748 *parentStylesPtr
++ = '\0';
749 for (i
=1; i
<nPass1Patterns
; i
++) {
750 parentName
= pass1PatternSrc
[i
].subPatternOf
;
751 *parentStylesPtr
++ = parentName
== NULL
? PLAIN_STYLE
:
752 pass1Pats
[indexOfNamedPattern(pass1PatternSrc
,
753 nPass1Patterns
, parentName
)].style
;
755 for (i
=1; i
<nPass2Patterns
; i
++) {
756 parentName
= pass2PatternSrc
[i
].subPatternOf
;
757 *parentStylesPtr
++ = parentName
== NULL
? PLAIN_STYLE
:
758 pass2Pats
[indexOfNamedPattern(pass2PatternSrc
,
759 nPass2Patterns
, parentName
)].style
;
762 /* Set up table for mapping colors and fonts to syntax */
763 styleTablePtr
= styleTable
= (styleTableEntry
*)XtMalloc(
764 sizeof(styleTableEntry
) * (nPass1Patterns
+ nPass2Patterns
+ 1));
765 #define setStyleTablePtr(styleTablePtr, patternSrc) \
767 styleTableEntry *p = styleTablePtr; \
768 highlightPattern *pat = patternSrc; \
771 p->highlightName = pat->name; \
772 p->styleName = pat->style; \
773 p->colorName = ColorOfNamedStyle(pat->style); \
774 p->bgColorName = BgColorOfNamedStyle(pat->style); \
775 p->isBold = FontOfNamedStyleIsBold(pat->style); \
776 p->isItalic = FontOfNamedStyleIsItalic(pat->style); \
777 /* And now for the more physical stuff */ \
778 p->color = AllocColor(window->textArea, p->colorName, &r, &g, &b); \
782 if (p->bgColorName) { \
783 p->bgColor = AllocColor(window->textArea, p->bgColorName, &r, &g, &b); \
789 p->bgColor = p->color; \
794 p->font = FontOfNamedStyle(window, pat->style); \
797 /* PLAIN_STYLE (pass 1) */
798 styleTablePtr
->underline
= FALSE
;
799 setStyleTablePtr(styleTablePtr
++,
800 noPass1
? &pass2PatternSrc
[0] : &pass1PatternSrc
[0]);
801 /* PLAIN_STYLE (pass 2) */
802 styleTablePtr
->underline
= FALSE
;
803 setStyleTablePtr(styleTablePtr
++,
804 noPass2
? &pass1PatternSrc
[0] : &pass2PatternSrc
[0]);
805 /* explicit styles (pass 1) */
806 for (i
=1; i
<nPass1Patterns
; i
++) {
807 styleTablePtr
->underline
= FALSE
;
808 setStyleTablePtr(styleTablePtr
++, &pass1PatternSrc
[i
]);
810 /* explicit styles (pass 2) */
811 for (i
=1; i
<nPass2Patterns
; i
++) {
812 styleTablePtr
->underline
= FALSE
;
813 setStyleTablePtr(styleTablePtr
++, &pass2PatternSrc
[i
]);
816 /* Free the temporary sorted pattern source list */
817 XtFree((char *)pass1PatternSrc
);
818 XtFree((char *)pass2PatternSrc
);
820 /* Create the style buffer */
821 styleBuf
= BufCreate();
823 /* Collect all of the highlighting information in a single structure */
824 highlightData
=(windowHighlightData
*)XtMalloc(sizeof(windowHighlightData
));
825 highlightData
->pass1Patterns
= pass1Pats
;
826 highlightData
->pass2Patterns
= pass2Pats
;
827 highlightData
->parentStyles
= parentStyles
;
828 highlightData
->styleTable
= styleTable
;
829 highlightData
->nStyles
= styleTablePtr
- styleTable
;
830 highlightData
->styleBuffer
= styleBuf
;
831 highlightData
->contextRequirements
.nLines
= contextLines
;
832 highlightData
->contextRequirements
.nChars
= contextChars
;
833 highlightData
->patternSetForWindow
= patSet
;
835 return highlightData
;
839 ** Transform pattern sources into the compiled highlight information
840 ** actually used by the code. Output is a tree of highlightDataRec structures
841 ** containing compiled regular expressions and style information.
843 static highlightDataRec
*compilePatterns(Widget dialogParent
,
844 highlightPattern
*patternSrc
, int nPatterns
)
846 int i
, nSubExprs
, patternNum
, length
, subPatIndex
, subExprNum
, charsRead
;
848 char *ptr
, *bigPattern
, *compileMsg
;
849 highlightDataRec
*compiledPats
;
851 /* Allocate memory for the compiled patterns. The list is terminated
852 by a record with style == 0. */
853 compiledPats
= (highlightDataRec
*)XtMalloc(sizeof(highlightDataRec
) *
855 compiledPats
[nPatterns
].style
= 0;
857 /* Build the tree of parse expressions */
858 for (i
=0; i
<nPatterns
; i
++) {
859 compiledPats
[i
].nSubPatterns
= 0;
860 compiledPats
[i
].nSubBranches
= 0;
862 for (i
=1; i
<nPatterns
; i
++)
863 if (patternSrc
[i
].subPatternOf
== NULL
)
864 compiledPats
[0].nSubPatterns
++;
866 compiledPats
[indexOfNamedPattern(patternSrc
, nPatterns
,
867 patternSrc
[i
].subPatternOf
)].nSubPatterns
++;
868 for (i
=0; i
<nPatterns
; i
++)
869 compiledPats
[i
].subPatterns
= compiledPats
[i
].nSubPatterns
== 0 ?
870 NULL
: (highlightDataRec
**)XtMalloc(
871 sizeof(highlightDataRec
*) * compiledPats
[i
].nSubPatterns
);
872 for (i
=0; i
<nPatterns
; i
++)
873 compiledPats
[i
].nSubPatterns
= 0;
874 for (i
=1; i
<nPatterns
; i
++) {
875 if (patternSrc
[i
].subPatternOf
== NULL
) {
876 compiledPats
[0].subPatterns
[compiledPats
[0].nSubPatterns
++] =
879 parentIndex
= indexOfNamedPattern(patternSrc
,
880 nPatterns
, patternSrc
[i
].subPatternOf
);
881 compiledPats
[parentIndex
].subPatterns
[compiledPats
[parentIndex
].
882 nSubPatterns
++] = &compiledPats
[i
];
886 /* Process color-only sub patterns (no regular expressions to match,
887 just colors and fonts for sub-expressions of the parent pattern */
888 for (i
=0; i
<nPatterns
; i
++) {
889 compiledPats
[i
].colorOnly
= patternSrc
[i
].flags
& COLOR_ONLY
;
890 compiledPats
[i
].userStyleIndex
= IndexOfNamedStyle(patternSrc
[i
].style
);
891 if (compiledPats
[i
].colorOnly
&& compiledPats
[i
].nSubPatterns
!= 0)
893 DialogF(DF_WARN
, dialogParent
, 1, "Color-only Pattern",
894 "Color-only pattern \"%s\" may not have subpatterns",
895 "OK", patternSrc
[i
].name
);
899 if (patternSrc
[i
].startRE
!= NULL
) {
900 ptr
= patternSrc
[i
].startRE
;
903 compiledPats
[i
].startSubexprs
[nSubExprs
++] = 0;
905 } else if (sscanf(ptr
, "\\%d%n", &subExprNum
, &charsRead
)==1) {
906 compiledPats
[i
].startSubexprs
[nSubExprs
++] = subExprNum
;
912 compiledPats
[i
].startSubexprs
[nSubExprs
] = -1;
914 if (patternSrc
[i
].endRE
!= NULL
) {
915 ptr
= patternSrc
[i
].endRE
;
918 compiledPats
[i
].endSubexprs
[nSubExprs
++] = 0;
920 } else if (sscanf(ptr
, "\\%d%n", &subExprNum
, &charsRead
)==1) {
921 compiledPats
[i
].endSubexprs
[nSubExprs
++] = subExprNum
;
927 compiledPats
[i
].endSubexprs
[nSubExprs
] = -1;
930 /* Compile regular expressions for all highlight patterns */
931 for (i
=0; i
<nPatterns
; i
++) {
932 if (patternSrc
[i
].startRE
== NULL
|| compiledPats
[i
].colorOnly
)
933 compiledPats
[i
].startRE
= NULL
;
935 if ((compiledPats
[i
].startRE
= compileREAndWarn(dialogParent
,
936 patternSrc
[i
].startRE
)) == NULL
)
939 if (patternSrc
[i
].endRE
== NULL
|| compiledPats
[i
].colorOnly
)
940 compiledPats
[i
].endRE
= NULL
;
942 if ((compiledPats
[i
].endRE
= compileREAndWarn(dialogParent
,
943 patternSrc
[i
].endRE
)) == NULL
)
946 if (patternSrc
[i
].errorRE
== NULL
)
947 compiledPats
[i
].errorRE
= NULL
;
949 if ((compiledPats
[i
].errorRE
= compileREAndWarn(dialogParent
,
950 patternSrc
[i
].errorRE
)) == NULL
)
955 /* Construct and compile the great hairy pattern to match the OR of the
956 end pattern, the error pattern, and all of the start patterns of the
958 for (patternNum
=0; patternNum
<nPatterns
; patternNum
++) {
959 if (patternSrc
[patternNum
].endRE
== NULL
&&
960 patternSrc
[patternNum
].errorRE
== NULL
&&
961 compiledPats
[patternNum
].nSubPatterns
== 0) {
962 compiledPats
[patternNum
].subPatternRE
= NULL
;
965 length
= (compiledPats
[patternNum
].colorOnly
||
966 patternSrc
[patternNum
].endRE
== NULL
) ? 0 :
967 strlen(patternSrc
[patternNum
].endRE
) + 5;
968 length
+= (compiledPats
[patternNum
].colorOnly
||
969 patternSrc
[patternNum
].errorRE
== NULL
) ? 0 :
970 strlen(patternSrc
[patternNum
].errorRE
) + 5;
971 for (i
=0; i
<compiledPats
[patternNum
].nSubPatterns
; i
++) {
972 subPatIndex
= compiledPats
[patternNum
].subPatterns
[i
]-compiledPats
;
973 length
+= compiledPats
[subPatIndex
].colorOnly
? 0 :
974 strlen(patternSrc
[subPatIndex
].startRE
) + 5;
977 compiledPats
[patternNum
].subPatternRE
= NULL
;
980 bigPattern
= XtMalloc(sizeof(char) * (length
+1));
982 if (patternSrc
[patternNum
].endRE
!= NULL
) {
983 *ptr
++ = '('; *ptr
++ = '?'; *ptr
++ = ':';
984 strcpy(ptr
, patternSrc
[patternNum
].endRE
);
985 ptr
+= strlen(patternSrc
[patternNum
].endRE
);
988 compiledPats
[patternNum
].nSubBranches
++;
990 if (patternSrc
[patternNum
].errorRE
!= NULL
) {
991 *ptr
++ = '('; *ptr
++ = '?'; *ptr
++ = ':';
992 strcpy(ptr
, patternSrc
[patternNum
].errorRE
);
993 ptr
+= strlen(patternSrc
[patternNum
].errorRE
);
996 compiledPats
[patternNum
].nSubBranches
++;
998 for (i
=0; i
<compiledPats
[patternNum
].nSubPatterns
; i
++) {
999 subPatIndex
= compiledPats
[patternNum
].subPatterns
[i
]-compiledPats
;
1000 if (compiledPats
[subPatIndex
].colorOnly
)
1002 *ptr
++ = '('; *ptr
++ = '?'; *ptr
++ = ':';
1003 strcpy(ptr
, patternSrc
[subPatIndex
].startRE
);
1004 ptr
+= strlen(patternSrc
[subPatIndex
].startRE
);
1007 compiledPats
[patternNum
].nSubBranches
++;
1010 compiledPats
[patternNum
].subPatternRE
= CompileRE(bigPattern
,
1011 &compileMsg
, REDFLT_STANDARD
);
1012 if (compiledPats
[patternNum
].subPatternRE
== NULL
) {
1013 fprintf(stderr
, "Error compiling syntax highlight patterns:\n%s",
1020 /* Copy remaining parameters from pattern template to compiled tree */
1021 for (i
=0; i
<nPatterns
; i
++)
1022 compiledPats
[i
].flags
= patternSrc
[i
].flags
;
1024 return compiledPats
;
1028 ** Free a pattern list and all of its allocated components
1030 static void freePatterns(highlightDataRec
*patterns
)
1034 for (i
=0; patterns
[i
].style
!=0; i
++) {
1035 if (patterns
[i
].startRE
!= NULL
)
1036 free((char *)patterns
[i
].startRE
);
1037 if (patterns
[i
].endRE
!= NULL
)
1038 free((char *)patterns
[i
].endRE
);
1039 if (patterns
[i
].errorRE
!= NULL
)
1040 free((char *)patterns
[i
].errorRE
);
1041 if (patterns
[i
].subPatternRE
!= NULL
)
1042 free((char *)patterns
[i
].subPatternRE
);
1045 for (i
=0; patterns
[i
].style
!=0; i
++) {
1046 XtFree((char*) patterns
[i
].subPatterns
);
1049 XtFree((char *)patterns
);
1053 ** Find the highlightPattern structure with a given name in the window.
1055 highlightPattern
*FindPatternOfWindow(WindowInfo
*window
, char *name
)
1057 windowHighlightData
*hData
= (windowHighlightData
*)window
->highlightData
;
1061 if (hData
&& (set
= hData
->patternSetForWindow
)) {
1062 for (i
= 0; i
< set
->nPatterns
; i
++)
1063 if (strcmp(set
->patterns
[i
].name
, name
) == 0)
1064 return &set
->patterns
[i
];
1070 ** Picks up the entry in the style buffer for the position (if any). Rather
1071 ** like styleOfPos() in textDisp.c. Returns the style code or zero.
1073 int HighlightCodeOfPos(WindowInfo
*window
, int pos
)
1075 windowHighlightData
*highlightData
=
1076 (windowHighlightData
*)window
->highlightData
;
1077 textBuffer
*styleBuf
=
1078 highlightData
? highlightData
->styleBuffer
: NULL
;
1081 if (styleBuf
!= NULL
) {
1082 hCode
= (unsigned char)BufGetCharacter(styleBuf
, pos
);
1083 if (hCode
== UNFINISHED_STYLE
) {
1084 /* encountered "unfinished" style, trigger parsing */
1085 handleUnparsedRegion(window
, highlightData
->styleBuffer
, pos
);
1086 hCode
= (unsigned char)BufGetCharacter(styleBuf
, pos
);
1093 ** Returns the length over which a particular highlight code applies, starting
1094 ** at pos. If the initial code value *checkCode is zero, the highlight code of
1097 /* YOO: This is called form only one other function, which uses a constant
1098 for checkCode and never evaluates it after the call. */
1099 int HighlightLengthOfCodeFromPos(WindowInfo
*window
, int pos
, int *checkCode
)
1101 windowHighlightData
*highlightData
=
1102 (windowHighlightData
*)window
->highlightData
;
1103 textBuffer
*styleBuf
=
1104 highlightData
? highlightData
->styleBuffer
: NULL
;
1108 if (styleBuf
!= NULL
) {
1109 hCode
= (unsigned char)BufGetCharacter(styleBuf
, pos
);
1112 if (hCode
== UNFINISHED_STYLE
) {
1113 /* encountered "unfinished" style, trigger parsing */
1114 handleUnparsedRegion(window
, highlightData
->styleBuffer
, pos
);
1115 hCode
= (unsigned char)BufGetCharacter(styleBuf
, pos
);
1117 if (*checkCode
== 0)
1119 while (hCode
== *checkCode
|| hCode
== UNFINISHED_STYLE
) {
1120 if (hCode
== UNFINISHED_STYLE
) {
1121 /* encountered "unfinished" style, trigger parsing, then loop */
1122 handleUnparsedRegion(window
, highlightData
->styleBuffer
, pos
);
1123 hCode
= (unsigned char)BufGetCharacter(styleBuf
, pos
);
1126 /* advance the position and get the new code */
1127 hCode
= (unsigned char)BufGetCharacter(styleBuf
, ++pos
);
1131 return pos
- oldPos
;
1135 ** Returns the length over which a particular style applies, starting at pos.
1136 ** If the initial code value *checkCode is zero, the highlight code of pos
1139 int StyleLengthOfCodeFromPos(WindowInfo
*window
, int pos
,
1140 const char **checkStyleName
)
1142 windowHighlightData
*highlightData
=
1143 (windowHighlightData
*)window
->highlightData
;
1144 textBuffer
*styleBuf
=
1145 highlightData
? highlightData
->styleBuffer
: NULL
;
1148 styleTableEntry
*entry
;
1150 if (styleBuf
!= NULL
) {
1151 hCode
= (unsigned char)BufGetCharacter(styleBuf
, pos
);
1154 if (hCode
== UNFINISHED_STYLE
) {
1155 /* encountered "unfinished" style, trigger parsing */
1156 handleUnparsedRegion(window
, highlightData
->styleBuffer
, pos
);
1157 hCode
= (unsigned char)BufGetCharacter(styleBuf
, pos
);
1159 entry
= styleTableEntryOfCode(window
, hCode
);
1162 if ((*checkStyleName
) == NULL
)
1163 (*checkStyleName
) = entry
->styleName
;
1164 while (hCode
== UNFINISHED_STYLE
||
1165 ((entry
= styleTableEntryOfCode(window
, hCode
)) &&
1166 strcmp(entry
->styleName
, (*checkStyleName
)) == 0)) {
1167 if (hCode
== UNFINISHED_STYLE
) {
1168 /* encountered "unfinished" style, trigger parsing, then loop */
1169 handleUnparsedRegion(window
, highlightData
->styleBuffer
, pos
);
1170 hCode
= (unsigned char)BufGetCharacter(styleBuf
, pos
);
1173 /* advance the position and get the new code */
1174 hCode
= (unsigned char)BufGetCharacter(styleBuf
, ++pos
);
1178 return pos
- oldPos
;
1182 ** Returns a pointer to the entry in the style table for the entry of code
1185 static styleTableEntry
*styleTableEntryOfCode(WindowInfo
*window
, int hCode
)
1187 windowHighlightData
*highlightData
=
1188 (windowHighlightData
*)window
->highlightData
;
1190 hCode
-= UNFINISHED_STYLE
; /* get the correct index value */
1191 if (!highlightData
|| hCode
< 0 || hCode
>= highlightData
->nStyles
)
1193 return &highlightData
->styleTable
[hCode
];
1197 ** Functions to return style information from the highlighting style table.
1200 char *HighlightNameOfCode(WindowInfo
*window
, int hCode
)
1202 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1203 return entry
? entry
->highlightName
: "";
1206 char *HighlightStyleOfCode(WindowInfo
*window
, int hCode
)
1208 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1209 return entry
? entry
->styleName
: "";
1212 char *HighlightColorOfCode(WindowInfo
*window
, int hCode
)
1214 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1215 return entry
? entry
->colorName
: "";
1218 char *HighlightBackgroundColorOfCode(WindowInfo
*window
, int hCode
)
1220 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1221 return entry
&& entry
->bgColorName
? entry
->bgColorName
: "";
1224 Pixel
HighlightColorValueOfCode(WindowInfo
*window
, int hCode
,
1225 int *r
, int *g
, int *b
)
1227 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1232 return entry
->color
;
1236 /* pick up foreground color of the (first) text widget of the window */
1239 Display
*display
= XtDisplay(window
->textArea
);
1241 XtVaGetValues(window
->textArea
,
1243 XtNforeground
, &colorDef
.pixel
,
1245 if (XQueryColor(display
, cMap
, &colorDef
)) {
1247 *g
= colorDef
.green
;
1250 return colorDef
.pixel
;
1254 Pixel
GetHighlightBGColorOfCode(WindowInfo
*window
, int hCode
,
1255 int *r
, int *g
, int *b
)
1257 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1258 if (entry
&& entry
->bgColorName
) {
1260 *g
= entry
->bgGreen
;
1262 return entry
->bgColor
;
1266 /* pick up background color of the (first) text widget of the window */
1269 Display
*display
= XtDisplay(window
->textArea
);
1271 XtVaGetValues(window
->textArea
,
1273 XtNbackground
, &colorDef
.pixel
,
1275 if (XQueryColor(display
, cMap
, &colorDef
)) {
1277 *g
= colorDef
.green
;
1280 return colorDef
.pixel
;
1284 int HighlightCodeIsBold(WindowInfo
*window
, int hCode
)
1286 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1287 return entry
? entry
->isBold
: 0;
1290 int HighlightCodeIsItalic(WindowInfo
*window
, int hCode
)
1292 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1293 return entry
? entry
->isItalic
: 0;
1297 ** Callback to parse an "unfinished" region of the buffer. "unfinished" means
1298 ** that the buffer has been parsed with pass 1 patterns, but this section has
1299 ** not yet been exposed, and thus never had pass 2 patterns applied. This
1300 ** callback is invoked when the text widget's display routines encounter one
1301 ** of these unfinished regions. "pos" is the first position encountered which
1302 ** needs re-parsing. This routine applies pass 2 patterns to a chunk of
1303 ** the buffer of size PASS_2_REPARSE_CHUNK_SIZE beyond pos.
1305 static void handleUnparsedRegion(const WindowInfo
* window
, textBuffer
* styleBuf
,
1308 textBuffer
*buf
= window
->buffer
;
1309 int beginParse
, endParse
, beginSafety
, endSafety
, p
;
1310 windowHighlightData
*highlightData
=
1311 (windowHighlightData
*)window
->highlightData
;
1313 reparseContext
*context
= &highlightData
->contextRequirements
;
1314 highlightDataRec
*pass2Patterns
= highlightData
->pass2Patterns
;
1315 char *string
, *styleString
, *stylePtr
, c
, prevChar
;
1316 const char *stringPtr
;
1317 int firstPass2Style
= (unsigned char)pass2Patterns
[1].style
;
1319 /* If there are no pass 2 patterns to process, do nothing (but this
1320 should never be triggered) */
1321 if (pass2Patterns
== NULL
)
1324 /* Find the point at which to begin parsing to ensure that the character at
1325 pos is parsed correctly (beginSafety), at most one context distance back
1326 from pos, unless there is a pass 1 section from which to start */
1328 beginSafety
= backwardOneContext(buf
, context
, beginParse
);
1329 for (p
=beginParse
; p
>=beginSafety
; p
--) {
1330 c
= BufGetCharacter(styleBuf
, p
);
1331 if (c
!= UNFINISHED_STYLE
&& c
!= PLAIN_STYLE
&&
1332 (unsigned char)c
< firstPass2Style
) {
1333 beginSafety
= p
+ 1;
1338 /* Decide where to stop (endParse), and the extra distance (endSafety)
1339 necessary to ensure that the changes at endParse are correct. Stop at
1340 the end of the unfinished region, or a max. of PASS_2_REPARSE_CHUNK_SIZE
1341 characters forward from the requested position */
1342 endParse
= min(buf
->length
, pos
+ PASS_2_REPARSE_CHUNK_SIZE
);
1343 endSafety
= forwardOneContext(buf
, context
, endParse
);
1344 for (p
=pos
; p
<endSafety
; p
++) {
1345 c
= BufGetCharacter(styleBuf
, p
);
1346 if (c
!= UNFINISHED_STYLE
&& c
!= PLAIN_STYLE
&&
1347 (unsigned char)c
< firstPass2Style
) {
1348 endParse
= min(endParse
, p
);
1351 } else if (c
!= UNFINISHED_STYLE
&& p
< endParse
) {
1353 if ((unsigned char)c
< firstPass2Style
)
1356 endSafety
= forwardOneContext(buf
, context
, endParse
);
1361 /* Copy the buffer range into a string */
1362 /* printf("callback pass2 parsing from %d thru %d w/ safety from %d thru %d\n",
1363 beginParse, endParse, beginSafety, endSafety); */
1364 stringPtr
= string
= BufGetRange(buf
, beginSafety
, endSafety
);
1365 styleString
= stylePtr
= BufGetRange(styleBuf
, beginSafety
, endSafety
);
1367 /* Parse it with pass 2 patterns */
1368 prevChar
= getPrevChar(buf
, beginSafety
);
1369 parseString(pass2Patterns
, &stringPtr
, &stylePtr
, endParse
- beginSafety
,
1370 &prevChar
, False
, GetWindowDelimiters(window
), string
, NULL
);
1372 /* Update the style buffer the new style information, but only between
1373 beginParse and endParse. Skip the safety region */
1374 styleString
[endParse
-beginSafety
] = '\0';
1375 BufReplace(styleBuf
, beginParse
, endParse
,
1376 &styleString
[beginParse
-beginSafety
]);
1377 XtFree(styleString
);
1382 ** Callback wrapper around the above function.
1384 static void handleUnparsedRegionCB(const textDisp
* textD
, const int pos
,
1387 handleUnparsedRegion((WindowInfo
*) cbArg
, textD
->styleBuffer
, pos
);
1391 ** Re-parse the smallest region possible around a modification to buffer "buf"
1392 ** to gurantee that the promised context lines and characters have
1393 ** been presented to the patterns. Changes the style buffer in "highlightData"
1394 ** with the parsing result.
1396 static void incrementalReparse(windowHighlightData
*highlightData
,
1397 textBuffer
*buf
, int pos
, int nInserted
, const char *delimiters
)
1399 int beginParse
, endParse
, endAt
, lastMod
, parseInStyle
, nPasses
;
1400 textBuffer
*styleBuf
= highlightData
->styleBuffer
;
1401 highlightDataRec
*pass1Patterns
= highlightData
->pass1Patterns
;
1402 highlightDataRec
*pass2Patterns
= highlightData
->pass2Patterns
;
1403 highlightDataRec
*startPattern
;
1404 reparseContext
*context
= &highlightData
->contextRequirements
;
1405 char *parentStyles
= highlightData
->parentStyles
;
1407 /* Find the position "beginParse" at which to begin reparsing. This is
1408 far enough back in the buffer such that the guranteed number of
1409 lines and characters of context are examined. */
1411 parseInStyle
= findSafeParseRestartPos(buf
, highlightData
, &beginParse
);
1413 /* Find the position "endParse" at which point it is safe to stop
1414 parsing, unless styles are getting changed beyond the last
1416 lastMod
= pos
+ nInserted
;
1417 endParse
= forwardOneContext(buf
, context
, lastMod
);
1420 ** Parse the buffer from beginParse, until styles compare
1421 ** with originals for one full context distance. Distance increases
1422 ** by powers of two until nothing changes from previous step. If
1423 ** parsing ends before endParse, start again one level up in the
1424 ** pattern hierarchy
1426 for (nPasses
=0; ; nPasses
++) {
1428 /* Parse forward from beginParse to one context beyond the end
1429 of the last modification */
1430 startPattern
= patternOfStyle(pass1Patterns
, parseInStyle
);
1431 /* If there is no pattern matching the style, it must be a pass-2
1432 style. It that case, it is (probably) safe to start parsing with
1433 the root pass-1 pattern again. Anyway, passing a NULL-pointer to
1434 the parse routine would result in a crash; restarting with pass-1
1435 patterns is certainly preferable, even if there is a slight chance
1436 of a faulty coloring. */
1437 if (!startPattern
) {
1438 startPattern
= pass1Patterns
;
1440 endAt
= parseBufferRange(startPattern
,
1441 pass2Patterns
, buf
, styleBuf
, context
, beginParse
, endParse
,
1444 /* If parse completed at this level, move one style up in the
1445 hierarchy and start again from where the previous parse left off. */
1446 if (endAt
< endParse
) {
1448 endParse
= forwardOneContext(buf
, context
,
1449 max(endAt
, max(lastModified(styleBuf
), lastMod
)));
1450 if (IS_PLAIN(parseInStyle
)) {
1452 "NEdit internal error: incr. reparse fell short\n");
1455 parseInStyle
= parentStyleOf(parentStyles
, parseInStyle
);
1457 /* One context distance beyond last style changed means we're done */
1458 } else if (lastModified(styleBuf
) <= lastMod
) {
1461 /* Styles are changing beyond the modification, continue extending
1462 the end of the parse range by powers of 2 * REPARSE_CHUNK_SIZE and
1463 reparse until nothing changes */
1465 lastMod
= lastModified(styleBuf
);
1466 endParse
= min(buf
->length
, forwardOneContext(buf
, context
, lastMod
)
1467 + (REPARSE_CHUNK_SIZE
<< nPasses
));
1473 ** Parse text in buffer "buf" between positions "beginParse" and "endParse"
1474 ** using pass 1 patterns over the entire range and pass 2 patterns where needed
1475 ** to determine whether re-parsed areas have changed and need to be redrawn.
1476 ** Deposits style information in "styleBuf" and expands the selection in
1477 ** styleBuf to show the additional areas which have changed and need
1478 ** redrawing. beginParse must be a position from which pass 1 parsing may
1479 ** safely be started using the pass1Patterns given. Internally, adds a
1480 ** "takeoff" safety region before beginParse, so that pass 2 patterns will be
1481 ** allowed to match properly if they begin before beginParse, and a "landing"
1482 ** safety region beyond endparse so that endParse is guranteed to be parsed
1483 ** correctly in both passes. Returns the buffer position at which parsing
1484 ** finished (this will normally be endParse, unless the pass1Patterns is a
1485 ** pattern which does end and the end is reached).
1487 static int parseBufferRange(highlightDataRec
*pass1Patterns
,
1488 highlightDataRec
*pass2Patterns
, textBuffer
*buf
, textBuffer
*styleBuf
,
1489 reparseContext
*contextRequirements
, int beginParse
, int endParse
,
1490 const char *delimiters
)
1492 char *string
, *styleString
, *stylePtr
, *temp
, prevChar
;
1493 const char *stringPtr
;
1494 int endSafety
, endPass2Safety
, startPass2Safety
, tempLen
;
1495 int modStart
, modEnd
, beginSafety
, beginStyle
, p
, style
;
1496 int firstPass2Style
= pass2Patterns
== NULL
? INT_MAX
:
1497 (unsigned char)pass2Patterns
[1].style
;
1499 /* Begin parsing one context distance back (or to the last style change) */
1500 beginStyle
= pass1Patterns
->style
;
1501 if (CAN_CROSS_LINE_BOUNDARIES(contextRequirements
)) {
1502 beginSafety
= backwardOneContext(buf
, contextRequirements
, beginParse
);
1503 for (p
=beginParse
; p
>=beginSafety
; p
--) {
1504 style
= BufGetCharacter(styleBuf
, p
-1);
1505 if (!EQUIVALENT_STYLE(style
, beginStyle
, firstPass2Style
)) {
1511 for (beginSafety
=max(0,beginParse
-1); beginSafety
>0; beginSafety
--) {
1512 style
= BufGetCharacter(styleBuf
, beginSafety
);
1513 if (!EQUIVALENT_STYLE(style
, beginStyle
, firstPass2Style
) ||
1514 BufGetCharacter(buf
, beginSafety
) == '\n') {
1521 /* Parse one parse context beyond requested end to gurantee that parsing
1522 at endParse is complete, unless patterns can't cross line boundaries,
1523 in which case the end of the line is fine */
1526 if (CAN_CROSS_LINE_BOUNDARIES(contextRequirements
))
1527 endSafety
= forwardOneContext(buf
, contextRequirements
, endParse
);
1528 else if (endParse
>=buf
->length
|| (BufGetCharacter(buf
,endParse
-1)=='\n'))
1529 endSafety
= endParse
;
1531 endSafety
= min(buf
->length
, BufEndOfLine(buf
, endParse
) + 1);
1533 /* copy the buffer range into a string */
1534 string
= BufGetRange(buf
, beginSafety
, endSafety
);
1535 styleString
= BufGetRange(styleBuf
, beginSafety
, endSafety
);
1537 /* Parse it with pass 1 patterns */
1538 /* printf("parsing from %d thru %d\n", beginSafety, endSafety); */
1539 prevChar
= getPrevChar(buf
, beginParse
);
1540 stringPtr
= &string
[beginParse
-beginSafety
];
1541 stylePtr
= &styleString
[beginParse
-beginSafety
];
1542 parseString(pass1Patterns
, &stringPtr
, &stylePtr
, endParse
-beginParse
,
1543 &prevChar
, False
, delimiters
, string
, NULL
);
1545 /* On non top-level patterns, parsing can end early */
1546 endParse
= min(endParse
, stringPtr
-string
+ beginSafety
);
1548 /* If there are no pass 2 patterns, we're done */
1549 if (pass2Patterns
== NULL
)
1552 /* Parsing of pass 2 patterns is done only as necessary for determining
1553 where styles have changed. Find the area to avoid, which is already
1554 marked as changed (all inserted text and previously modified areas) */
1555 if (styleBuf
->primary
.selected
) {
1556 modStart
= styleBuf
->primary
.start
;
1557 modEnd
= styleBuf
->primary
.end
;
1559 modStart
= modEnd
= 0;
1561 /* Re-parse the areas before the modification with pass 2 patterns, from
1562 beginSafety to far enough beyond modStart to gurantee that parsing at
1563 modStart is correct (pass 2 patterns must match entirely within one
1564 context distance, and only on the top level). If the parse region
1565 ends entirely before the modification or at or beyond modEnd, parse
1566 the whole thing and take advantage of the safety region which will be
1567 thrown away below. Otherwise save the contents of the safety region
1568 temporarily, and restore it after the parse. */
1569 if (beginSafety
< modStart
) {
1570 if (endSafety
> modStart
) {
1571 endPass2Safety
= forwardOneContext(buf
, contextRequirements
,
1573 if (endPass2Safety
+ PASS_2_REPARSE_CHUNK_SIZE
>= modEnd
)
1574 endPass2Safety
= endSafety
;
1576 endPass2Safety
= endSafety
;
1577 prevChar
= getPrevChar(buf
, beginSafety
);
1578 if (endPass2Safety
== endSafety
) {
1579 passTwoParseString(pass2Patterns
, string
, styleString
,
1580 endParse
- beginSafety
, &prevChar
, False
, delimiters
, string
,
1584 tempLen
= endPass2Safety
- modStart
;
1585 temp
= XtMalloc(tempLen
);
1586 strncpy(temp
, &styleString
[modStart
-beginSafety
], tempLen
);
1587 passTwoParseString(pass2Patterns
, string
, styleString
,
1588 modStart
- beginSafety
, &prevChar
, False
, delimiters
,
1590 strncpy(&styleString
[modStart
-beginSafety
], temp
, tempLen
);
1595 /* Re-parse the areas after the modification with pass 2 patterns, from
1596 modEnd to endSafety, with an additional safety region before modEnd
1597 to ensure that parsing at modEnd is correct. */
1598 if (endParse
> modEnd
) {
1599 if (beginSafety
> modEnd
) {
1600 prevChar
= getPrevChar(buf
, beginSafety
);
1601 passTwoParseString(pass2Patterns
, string
, styleString
,
1602 endParse
- beginSafety
, &prevChar
, False
, delimiters
,
1605 startPass2Safety
= max(beginSafety
,
1606 backwardOneContext(buf
, contextRequirements
, modEnd
));
1607 tempLen
= modEnd
- startPass2Safety
;
1608 temp
= XtMalloc(tempLen
);
1609 strncpy(temp
, &styleString
[startPass2Safety
-beginSafety
], tempLen
);
1610 prevChar
= getPrevChar(buf
, startPass2Safety
);
1611 passTwoParseString(pass2Patterns
,
1612 &string
[startPass2Safety
-beginSafety
],
1613 &styleString
[startPass2Safety
-beginSafety
],
1614 endParse
-startPass2Safety
, &prevChar
, False
, delimiters
,
1616 strncpy(&styleString
[startPass2Safety
-beginSafety
], temp
, tempLen
);
1623 /* Update the style buffer with the new style information, but only
1624 through endParse. Skip the safety region at the end */
1625 styleString
[endParse
-beginSafety
] = '\0';
1626 modifyStyleBuf(styleBuf
, &styleString
[beginParse
-beginSafety
],
1627 beginParse
, endParse
, firstPass2Style
);
1628 XtFree(styleString
);
1635 ** Parses "string" according to compiled regular expressions in "pattern"
1636 ** until endRE is or errorRE are matched, or end of string is reached.
1637 ** Advances "string", "styleString" pointers to the next character past
1638 ** the end of the parsed section, and updates "prevChar" to reflect
1639 ** the new character before "string".
1640 ** If "anchored" is true, just scan the sub-pattern starting at the beginning
1641 ** of the string. "length" is how much of the string must be parsed, but
1642 ** "string" must still be null terminated, the termination indicating how
1643 ** far the string should be searched, and "length" the part which is actually
1644 ** required (the string may or may not be parsed beyond "length").
1646 ** "lookBehindTo" indicates the boundary till where look-behind patterns may
1647 ** look back. If NULL, the start of the string is assumed to be the boundary.
1649 ** "match_till" indicates the boundary till where matches may extend. If NULL,
1650 ** it is assumed that the terminating \0 indicates the boundary. Note that
1651 ** look-ahead patterns can peek beyond the boundary, if supplied.
1653 ** Returns True if parsing was done and the parse succeeded. Returns False if
1654 ** the error pattern matched, if the end of the string was reached without
1655 ** matching the end expression, or in the unlikely event of an internal error.
1657 static int parseString(highlightDataRec
*pattern
, const char **string
,
1658 char **styleString
, int length
, char *prevChar
, int anchored
,
1659 const char *delimiters
, const char* lookBehindTo
,
1660 const char* match_till
)
1662 int i
, subExecuted
, subIndex
;
1664 const char *stringPtr
, *savedStartPtr
, *startingStringPtr
;
1665 signed char *subExpr
;
1667 char succChar
= match_till
? (*match_till
) : '\0';
1668 highlightDataRec
*subPat
= NULL
, *subSubPat
;
1673 stringPtr
= *string
;
1674 stylePtr
= *styleString
;
1676 while(ExecRE(pattern
->subPatternRE
, NULL
, stringPtr
, anchored
? *string
+1 :
1677 *string
+length
+1, False
, *prevChar
, succChar
, delimiters
,
1678 lookBehindTo
, match_till
)) {
1679 /* Beware of the case where only one real branch exists, but that
1680 branch has sub-branches itself. In that case the top_branch refers
1681 to the matching sub-branch and must be ignored. */
1682 subIndex
= (pattern
->nSubBranches
> 1) ?
1683 pattern
->subPatternRE
->top_branch
: 0;
1684 /* Combination of all sub-patterns and end pattern matched */
1685 /* printf("combined patterns RE matched at %d\n",
1686 pattern->subPatternRE->startp[0] - *string); */
1687 startingStringPtr
= stringPtr
;
1689 /* Fill in the pattern style for the text that was skipped over before
1690 the match, and advance the pointers to the start of the pattern */
1691 fillStyleString(&stringPtr
, &stylePtr
, pattern
->subPatternRE
->startp
[0],
1692 pattern
->style
, prevChar
);
1694 /* If the combined pattern matched this pattern's end pattern, we're
1695 done. Fill in the style string, update the pointers, color the
1696 end expression if there were coloring sub-patterns, and return */
1697 savedStartPtr
= stringPtr
;
1698 savedPrevChar
= *prevChar
;
1699 if (pattern
->endRE
!= NULL
) {
1700 if (subIndex
== 0) {
1701 fillStyleString(&stringPtr
, &stylePtr
,
1702 pattern
->subPatternRE
->endp
[0], pattern
->style
, prevChar
);
1703 subExecuted
= False
;
1704 for (i
=0;i
<pattern
->nSubPatterns
; i
++) {
1705 subPat
= pattern
->subPatterns
[i
];
1706 if (subPat
->colorOnly
) {
1708 if (!ExecRE(pattern
->endRE
, NULL
, savedStartPtr
,
1709 savedStartPtr
+1, False
, savedPrevChar
,
1710 succChar
, delimiters
, lookBehindTo
, match_till
)) {
1711 fprintf(stderr
, "Internal error, failed to "
1712 "recover end match in parseString\n");
1717 for (subExpr
=subPat
->endSubexprs
; *subExpr
!=-1; subExpr
++)
1718 recolorSubexpr(pattern
->endRE
, *subExpr
,
1719 subPat
->style
, *string
, *styleString
);
1722 *string
= stringPtr
;
1723 *styleString
= stylePtr
;
1729 /* If the combined pattern matched this pattern's error pattern, we're
1730 done. Fill in the style string, update the pointers, and return */
1731 if (pattern
->errorRE
!= NULL
) {
1732 if (subIndex
== 0) {
1733 fillStyleString(&stringPtr
, &stylePtr
,
1734 pattern
->subPatternRE
->startp
[0], pattern
->style
, prevChar
);
1735 *string
= stringPtr
;
1736 *styleString
= stylePtr
;
1742 /* Figure out which sub-pattern matched */
1743 for (i
=0; i
<pattern
->nSubPatterns
; i
++) {
1744 subPat
= pattern
->subPatterns
[i
];
1745 if (subPat
->colorOnly
) ++subIndex
;
1746 else if (i
== subIndex
) break;
1748 if (i
== pattern
->nSubPatterns
) {
1749 fprintf(stderr
, "Internal error, failed to match in parseString\n");
1753 /* the sub-pattern is a simple match, just color it */
1754 if (subPat
->subPatternRE
== NULL
) {
1755 fillStyleString(&stringPtr
, &stylePtr
, pattern
->subPatternRE
->endp
[0], /* subPat->startRE->endp[0],*/
1756 subPat
->style
, prevChar
);
1758 /* Parse the remainder of the sub-pattern */
1759 } else if (subPat
->endRE
!= NULL
) {
1760 /* The pattern is a starting/ending type of pattern, proceed with
1761 the regular hierarchical parsing. */
1763 /* If parsing should start after the start pattern, advance
1764 to that point (this is currently always the case) */
1765 if (!(subPat
->flags
& PARSE_SUBPATS_FROM_START
))
1766 fillStyleString(&stringPtr
, &stylePtr
, pattern
->subPatternRE
->endp
[0], /* subPat->startRE->endp[0],*/
1767 subPat
->style
, prevChar
);
1769 /* Parse to the end of the subPattern */
1770 parseString(subPat
, &stringPtr
, &stylePtr
, length
-
1771 (stringPtr
- *string
), prevChar
, False
, delimiters
,
1772 lookBehindTo
, match_till
);
1774 /* If the parent pattern is not a start/end pattern, the
1775 sub-pattern can between the boundaries of the parent's
1776 match. Note that we must limit the recursive matches such
1777 that they do not exceed the parent's ending boundary.
1778 Without that restriction, matching becomes unstable. */
1780 /* Parse to the end of the subPattern */
1781 parseString(subPat
, &stringPtr
, &stylePtr
,
1782 pattern
->subPatternRE
->endp
[0]-stringPtr
, prevChar
, False
,
1783 delimiters
, lookBehindTo
, pattern
->subPatternRE
->endp
[0]);
1786 /* If the sub-pattern has color-only sub-sub-patterns, add color
1787 based on the coloring sub-expression references */
1788 subExecuted
= False
;
1789 for (i
=0; i
<subPat
->nSubPatterns
; i
++) {
1790 subSubPat
= subPat
->subPatterns
[i
];
1791 if (subSubPat
->colorOnly
) {
1793 if (!ExecRE(subPat
->startRE
, NULL
, savedStartPtr
,
1794 savedStartPtr
+1, False
, savedPrevChar
, succChar
,
1795 delimiters
, lookBehindTo
, match_till
)) {
1796 fprintf(stderr
, "Internal error, failed to recover "
1797 "start match in parseString\n");
1802 for (subExpr
=subSubPat
->startSubexprs
; *subExpr
!=-1; subExpr
++)
1803 recolorSubexpr(subPat
->startRE
, *subExpr
, subSubPat
->style
,
1804 *string
, *styleString
);
1808 /* Make sure parsing progresses. If patterns match the empty string,
1809 they can get stuck and hang the process */
1810 if (stringPtr
== startingStringPtr
) {
1811 /* Avoid stepping over the end of the string (possible for
1812 zero-length matches at end of the string) */
1813 if (*stringPtr
== '\0')
1815 fillStyleString(&stringPtr
, &stylePtr
, stringPtr
+1,
1816 pattern
->style
, prevChar
);
1820 /* If this is an anchored match (must match on first character), and
1821 nothing matched, return False */
1822 if (anchored
&& stringPtr
== *string
)
1825 /* Reached end of string, fill in the remaining text with pattern style
1826 (unless this was an anchored match) */
1828 fillStyleString(&stringPtr
, &stylePtr
, *string
+length
, pattern
->style
,
1831 /* Advance the string and style pointers to the end of the parsed text */
1832 *string
= stringPtr
;
1833 *styleString
= stylePtr
;
1834 return pattern
->endRE
== NULL
;
1838 ** Takes a string which has already been parsed through pass1 parsing and
1839 ** re-parses the areas where pass two patterns are applicable. Parameters
1840 ** have the same meaning as in parseString, except that strings aren't doubly
1841 ** indirect and string pointers are not updated.
1843 static void passTwoParseString(highlightDataRec
*pattern
, char *string
,
1844 char *styleString
, int length
, char *prevChar
, int anchored
,
1845 const char *delimiters
, const char *lookBehindTo
,
1846 const char *match_till
)
1848 int inParseRegion
= False
;
1849 char *stylePtr
, temp
, *parseStart
= NULL
, *parseEnd
, *s
, *c
;
1850 const char *stringPtr
;
1851 int firstPass2Style
= (unsigned char)pattern
[1].style
;
1853 for (c
= string
, s
= styleString
; ; c
++, s
++) {
1854 if (!inParseRegion
&& *c
!= '\0' && (*s
== UNFINISHED_STYLE
||
1855 *s
== PLAIN_STYLE
|| (unsigned char)*s
>= firstPass2Style
)) {
1857 inParseRegion
= True
;
1859 if (inParseRegion
&& (*c
== '\0' || !(*s
== UNFINISHED_STYLE
||
1860 *s
== PLAIN_STYLE
|| (unsigned char)*s
>= firstPass2Style
))) {
1862 if (parseStart
!= string
)
1863 *prevChar
= *(parseStart
-1);
1864 stringPtr
= parseStart
;
1865 stylePtr
= &styleString
[parseStart
- string
];
1868 /* printf("pass2 parsing %d chars\n", strlen(stringPtr)); */
1869 parseString(pattern
, &stringPtr
, &stylePtr
,
1870 min(parseEnd
- parseStart
, length
- (parseStart
- string
)),
1871 prevChar
, False
, delimiters
, lookBehindTo
, match_till
);
1873 inParseRegion
= False
;
1875 if (*c
== '\0' || (!inParseRegion
&& c
- string
>= length
))
1881 ** Advance "stringPtr" and "stylePtr" until "stringPtr" == "toPtr", filling
1882 ** "stylePtr" with style "style". Can also optionally update the pre-string
1883 ** character, prevChar, which is fed to regular the expression matching
1884 ** routines for determining word and line boundaries at the start of the string.
1886 static void fillStyleString(const char **stringPtr
, char **stylePtr
,
1887 const char *toPtr
, char style
, char *prevChar
)
1889 int i
, len
= toPtr
-*stringPtr
;
1891 if (*stringPtr
>= toPtr
)
1894 for (i
=0; i
<len
; i
++)
1895 *(*stylePtr
)++ = style
;
1896 if (prevChar
!= NULL
) *prevChar
= *(toPtr
-1);
1901 ** Incorporate changes from styleString into styleBuf, tracking changes
1902 ** in need of redisplay, and marking them for redisplay by the text
1903 ** modification callback in textDisp.c. "firstPass2Style" is necessary
1904 ** for distinguishing pass 2 styles which compare as equal to the unfinished
1905 ** style in the original buffer, from pass1 styles which signal a change.
1907 static void modifyStyleBuf(textBuffer
*styleBuf
, char *styleString
,
1908 int startPos
, int endPos
, int firstPass2Style
)
1911 int pos
, modStart
, modEnd
, minPos
= INT_MAX
, maxPos
= 0;
1912 selection
*sel
= &styleBuf
->primary
;
1914 /* Skip the range already marked for redraw */
1915 if (sel
->selected
) {
1916 modStart
= sel
->start
;
1919 modStart
= modEnd
= startPos
;
1921 /* Compare the original style buffer (outside of the modified range) with
1922 the new string with which it will be updated, to find the extent of
1923 the modifications. Unfinished styles in the original match any
1925 for (c
=styleString
, pos
=startPos
; pos
<modStart
&& pos
<endPos
; c
++, pos
++) {
1926 bufChar
= BufGetCharacter(styleBuf
, pos
);
1927 if (*c
!= bufChar
&& !(bufChar
== UNFINISHED_STYLE
&&
1928 (*c
== PLAIN_STYLE
|| (unsigned char)*c
>= firstPass2Style
))) {
1929 if (pos
< minPos
) minPos
= pos
;
1930 if (pos
> maxPos
) maxPos
= pos
;
1933 for (c
=&styleString
[max(0, modEnd
-startPos
)], pos
=max(modEnd
, startPos
);
1934 pos
<endPos
; c
++, pos
++) {
1935 bufChar
= BufGetCharacter(styleBuf
, pos
);
1936 if (*c
!= bufChar
&& !(bufChar
== UNFINISHED_STYLE
&&
1937 (*c
== PLAIN_STYLE
|| (unsigned char)*c
>= firstPass2Style
))) {
1938 if (pos
< minPos
) minPos
= pos
;
1939 if (pos
+1 > maxPos
) maxPos
= pos
+1;
1943 /* Make the modification */
1944 BufReplace(styleBuf
, startPos
, endPos
, styleString
);
1946 /* Mark or extend the range that needs to be redrawn. Even if no
1947 change was made, it's important to re-establish the selection,
1948 because it can get damaged by the BufReplace above */
1949 BufSelect(styleBuf
, min(modStart
, minPos
), max(modEnd
, maxPos
));
1953 ** Return the last modified position in styleBuf (as marked by modifyStyleBuf
1954 ** by the convention used for conveying modification information to the
1955 ** text widget, which is selecting the text)
1957 static int lastModified(textBuffer
*styleBuf
)
1959 if (styleBuf
->primary
.selected
)
1960 return max(0, styleBuf
->primary
.end
);
1965 ** Compute the distance between two colors.
1968 static double colorDistance(const XColor
*c1
, const XColor
*c2
)
1970 /* This is done in RGB space, which is close, but not optimal. It's
1971 probably better to do it in HSV or YIQ space, however, that means
1972 a whole lot of extra conversions. This would allow us to weight
1973 the coordinates differently, e.g, prefer to match hue over
1976 static const double scale
= 65535;
1978 double tred
= c1
->red
/ scale
- c2
->red
/ scale
;
1979 double tgreen
= c1
->green
/ scale
- c2
->green
/ scale
;
1980 double tblue
= c1
->blue
/ scale
- c2
->blue
/ scale
;
1982 /* use square Euclidian distance */
1983 return tred
* tred
+ tgreen
* tgreen
+ tblue
* tblue
;
1987 ** use this canned function to call AllocColor() when
1988 ** the r, g & b components is not needed, thus saving
1989 ** the little hassle of creating the dummy variable.
1991 Pixel
AllocateColor(Widget w
, const char *colorName
)
1995 return AllocColor(w
, colorName
, &dummy
, &dummy
, &dummy
);
1999 ** Allocate a read-only (shareable) colormap cell for a named color, from the
2000 ** the default colormap of the screen on which the widget (w) is displayed. If
2001 ** the colormap is full and there's no suitable substitute, print an error on
2002 ** stderr, and return the widget's foreground color as a backup.
2005 Pixel
AllocColor(Widget w
, const char *colorName
, int *r
, int *g
, int *b
)
2008 XColor
*allColorDefs
;
2009 Display
*display
= XtDisplay(w
);
2011 Pixel foreground
, bestPixel
;
2012 double small
= 1.0e9
;
2014 unsigned int ncolors
;
2015 unsigned long i
, best
= 0; /* pixel value */
2017 /* Get the correct colormap for compatability with the "best" visual
2018 feature in 5.2. Default visual of screen is no good here. */
2023 XtNforeground
, &foreground
,
2026 bestPixel
= foreground
; /* Our last fallback */
2028 /* First, check for valid syntax */
2029 if (! XParseColor(display
, cMap
, colorName
, &colorDef
)) {
2030 fprintf(stderr
, "NEdit: Color name %s not in database\n", colorName
);
2031 colorDef
.pixel
= foreground
;
2032 if (XQueryColor(display
, cMap
, &colorDef
)) {
2034 *g
= colorDef
.green
;
2040 /* Attempt allocation of the exact color. */
2041 if (XAllocColor(display
, cMap
, &colorDef
)) {
2043 *g
= colorDef
.green
;
2045 return colorDef
.pixel
;
2048 /* ---------- Allocation failed, the colormap may be full. ---------- */
2051 printf("Couldn't allocate %d %d %d\n", colorDef
.red
, colorDef
.green
, colorDef
.blue
);
2054 /* We can't do the nearest-match on other than 8 bit visuals because
2055 it just takes too long. */
2057 if (depth
> 8) { /* Oh no! */
2058 colorDef
.pixel
= foreground
;
2059 if (XQueryColor(display
, cMap
, &colorDef
)) {
2061 *g
= colorDef
.green
;
2067 /* Get the entire colormap so we can find the closest one. */
2068 ncolors
= (1 << depth
);
2069 allColorDefs
= malloc(ncolors
* sizeof(XColor
));
2070 memset(allColorDefs
, 0, ncolors
* sizeof(XColor
));
2072 for (i
= 0; i
< ncolors
; i
++)
2073 allColorDefs
[i
].pixel
= i
;
2075 XQueryColors(display
, cMap
, allColorDefs
, ncolors
);
2077 /* Scan through each color, looking for the closest one. */
2078 for (i
= 0; i
< ncolors
; i
++)
2080 double dist
= colorDistance(&allColorDefs
[i
], &colorDef
);
2089 /* Legally try to acquire the shared color- we should loop through
2090 the shortest distances here. We could sort the map in order
2091 of decreasing distances and loop through it until one works. */
2093 if (XAllocColor(display
, cMap
, &allColorDefs
[best
]))
2094 bestPixel
= allColorDefs
[best
].pixel
;
2097 printf("Got %d %d %d, ", allColorDefs
[best
].red
,
2098 allColorDefs
[best
].green
,
2099 allColorDefs
[best
].blue
);
2100 printf("That's %f off\n", small
);
2103 *r
= allColorDefs
[best
].red
;
2104 *g
= allColorDefs
[best
].green
;
2105 *b
= allColorDefs
[best
].blue
;
2111 ** Get the character before position "pos" in buffer "buf"
2113 static char getPrevChar(textBuffer
*buf
, int pos
)
2115 return pos
== 0 ? '\0' : BufGetCharacter(buf
, pos
-1);
2119 ** compile a regular expression and present a user friendly dialog on failure.
2121 static regexp
*compileREAndWarn(Widget parent
, const char *re
)
2126 compiledRE
= CompileRE(re
, &compileMsg
, REDFLT_STANDARD
);
2127 if (compiledRE
== NULL
)
2129 char *boundedRe
= XtNewString(re
);
2130 size_t maxLength
= DF_MAX_MSG_LENGTH
- strlen(compileMsg
) - 60;
2132 /* Prevent buffer overflow in DialogF. If the re is too long,
2133 truncate it and append ... */
2134 if (strlen(boundedRe
) > maxLength
)
2136 strcpy(&boundedRe
[maxLength
-3], "...");
2139 DialogF(DF_WARN
, parent
, 1, "Error in Regex",
2140 "Error in syntax highlighting regular expression:\n%s\n%s",
2141 "OK", boundedRe
, compileMsg
);
2148 static int parentStyleOf(const char *parentStyles
, int style
)
2150 return parentStyles
[(unsigned char)style
-UNFINISHED_STYLE
];
2153 static int isParentStyle(const char *parentStyles
, int style1
, int style2
)
2157 for (p
= parentStyleOf(parentStyles
, style2
); p
!= '\0';
2158 p
= parentStyleOf(parentStyles
, p
))
2165 ** Discriminates patterns which can be used with parseString from those which
2166 ** can't. Leaf patterns are not suitable for parsing, because patterns
2167 ** contain the expressions used for parsing within the context of their own
2168 ** operation, i.e. the parent pattern initiates, and leaf patterns merely
2169 ** confirm and color. Returns TRUE if the pattern is suitable for parsing.
2171 static int patternIsParsable(highlightDataRec
*pattern
)
2173 return pattern
!= NULL
&& pattern
->subPatternRE
!= NULL
;
2177 ** Back up position pointed to by "pos" enough that parsing from that point
2178 ** on will satisfy context gurantees for pattern matching for modifications
2179 ** at pos. Returns the style with which to begin parsing. The caller is
2180 ** guranteed that parsing may safely BEGIN with that style, but not that it
2181 ** will continue at that level.
2183 ** This routine can be fooled if a continuous style run of more than one
2184 ** context distance in length is produced by multiple pattern matches which
2185 ** abut, rather than by a single continuous match. In this case the
2186 ** position returned by this routine may be a bad starting point which will
2187 ** result in an incorrect re-parse. However this will happen very rarely,
2188 ** and, if it does, is unlikely to result in incorrect highlighting.
2190 static int findSafeParseRestartPos(textBuffer
*buf
,
2191 windowHighlightData
*highlightData
, int *pos
)
2193 int style
, startStyle
, runningStyle
, checkBackTo
, safeParseStart
, i
;
2194 char *parentStyles
= highlightData
->parentStyles
;
2195 highlightDataRec
*pass1Patterns
= highlightData
->pass1Patterns
;
2196 reparseContext
*context
= &highlightData
->contextRequirements
;
2198 /* We must begin at least one context distance back from the change */
2199 *pos
= backwardOneContext(buf
, context
, *pos
);
2201 /* If the new position is outside of any styles or at the beginning of
2202 the buffer, this is a safe place to begin parsing, and we're done */
2205 startStyle
= BufGetCharacter(highlightData
->styleBuffer
, *pos
);
2206 if (IS_PLAIN(startStyle
))
2210 ** The new position is inside of a styled region, meaning, its pattern
2211 ** could potentially be affected by the modification.
2213 ** Follow the style back by enough context to ensure that if we don't find
2214 ** its beginning, at least we've found a safe place to begin parsing
2215 ** within the styled region.
2217 ** A safe starting position within a style is either at a style
2218 ** boundary, or far enough from the beginning and end of the style run
2219 ** to ensure that it's not within the start or end expression match
2220 ** (unfortunately, abutting styles can produce false runs so we're not
2221 ** really ensuring it, just making it likely).
2223 if (patternIsParsable(patternOfStyle(pass1Patterns
, startStyle
))) {
2224 safeParseStart
= backwardOneContext(buf
, context
, *pos
);
2225 checkBackTo
= backwardOneContext(buf
, context
, safeParseStart
);
2230 runningStyle
= startStyle
;
2231 for (i
= *pos
-1; ; i
--) {
2233 /* The start of the buffer is certainly a safe place to parse from */
2239 /* If the style is preceded by a parent style, it's safe to parse
2240 with the parent style, provided that the parent is parsable. */
2241 style
= BufGetCharacter(highlightData
->styleBuffer
, i
);
2242 if (isParentStyle(parentStyles
, style
, runningStyle
)) {
2243 if (patternIsParsable(patternOfStyle(pass1Patterns
, style
))) {
2247 /* The parent is not parsable, so well have to continue
2248 searching. The parent now becomes the running style. */
2249 runningStyle
= style
;
2253 /* If the style is preceded by a child style, it's safe to resume
2254 parsing with the running style, provided that the running
2255 style is parsable. */
2256 else if (isParentStyle(parentStyles
, runningStyle
, style
)) {
2257 if (patternIsParsable
2258 (patternOfStyle(pass1Patterns
, runningStyle
))) {
2260 return runningStyle
;
2262 /* Else: keep searching; it's no use switching to the child style
2263 because even the running one is not parsable. */
2266 /* If the style is preceded by a sibling style, it's safe to resume
2267 parsing with the common ancestor style, provided that the ancestor
2268 is parsable. Checking for siblings is very hard; checking whether
2269 the style has the same parent will probably catch 99% of the cases
2271 else if (runningStyle
!= style
&&
2272 isParentStyle(parentStyles
,
2273 parentStyleOf(parentStyles
, runningStyle
), style
)) {
2274 int parentStyle
= parentStyleOf(parentStyles
, runningStyle
);
2275 if (patternIsParsable(patternOfStyle(pass1Patterns
, parentStyle
))) {
2279 /* Switch to the new style */
2280 runningStyle
= style
;
2284 /* If the style is preceded by an unrelated style, it's safe to
2285 resume parsing with PLAIN_STYLE. (Actually, it isn't, because
2286 we didn't really check for all possible sibling relations; but
2287 it will be ok in practice.) */
2288 else if (runningStyle
!= style
) {
2293 /* If the style is parsable and didn't change for one whole context
2294 distance on either side of safeParseStart, safeParseStart is a
2295 reasonable guess at a place to start parsing.
2296 Note: No 'else' here! We may come from one of the 'fall-through
2298 if (i
== checkBackTo
) {
2299 *pos
= safeParseStart
;
2301 /* We should never return a non-parsable style, because it will
2302 result in an internal error. If the current style is not
2303 parsable, the pattern set most probably contains a context
2304 distance violation. In that case we can only avoid internal
2305 errors (by climbing the pattern hierarchy till we find a
2306 parsable ancestor) and hope that the highlighting errors are
2308 while (!patternIsParsable
2309 (patternOfStyle(pass1Patterns
, runningStyle
)))
2310 runningStyle
= parentStyleOf(parentStyles
, runningStyle
);
2312 return runningStyle
;
2318 ** Return a position far enough back in "buf" from "fromPos" to give patterns
2319 ** their guranteed amount of context for matching (from "context"). If
2320 ** backing up by lines yields the greater distance, the returned position will
2321 ** be to the newline character before the start of the line, rather than to
2322 ** the first character of the line. (I did this because earlier prototypes of
2323 ** the syntax highlighting code, which were based on single-line context, used
2324 ** this to ensure that line-spanning expressions would be detected. I think
2325 ** it may reduce some 2 line context requirements to one line, at a cost of
2326 ** only one extra character, but I'm not sure, and my brain hurts from
2327 ** thinking about it).
2329 static int backwardOneContext(textBuffer
*buf
, reparseContext
*context
,
2332 if (context
->nLines
== 0)
2333 return max(0, fromPos
- context
->nChars
);
2334 else if (context
->nChars
== 0)
2336 BufCountBackwardNLines(buf
, fromPos
, context
->nLines
-1) - 1);
2338 return max(0, min(max(0, BufCountBackwardNLines(buf
, fromPos
,
2339 context
->nLines
-1) -1), fromPos
- context
->nChars
));
2343 ** Return a position far enough forward in "buf" from "fromPos" to ensure
2344 ** that patterns are given their required amount of context for matching
2345 ** (from "context"). If moving forward by lines yields the greater
2346 ** distance, the returned position will be the first character of of the
2347 ** next line, rather than the newline character at the end (see notes in
2348 ** backwardOneContext).
2350 static int forwardOneContext(textBuffer
*buf
, reparseContext
*context
,
2353 if (context
->nLines
== 0)
2354 return min(buf
->length
, fromPos
+ context
->nChars
);
2355 else if (context
->nChars
== 0)
2356 return min(buf
->length
,
2357 BufCountForwardNLines(buf
, fromPos
, context
->nLines
));
2359 return min(buf
->length
, max(BufCountForwardNLines(buf
, fromPos
,
2360 context
->nLines
), fromPos
+ context
->nChars
));
2364 ** Change styles in the portion of "styleString" to "style" where a particular
2365 ** sub-expression, "subExpr", of regular expression "re" applies to the
2366 ** corresponding portion of "string".
2368 static void recolorSubexpr(regexp
*re
, int subexpr
, int style
,
2369 const char *string
, char *styleString
)
2371 const char *stringPtr
;
2374 stringPtr
= re
->startp
[subexpr
];
2375 stylePtr
= &styleString
[stringPtr
- string
];
2376 fillStyleString(&stringPtr
, &stylePtr
, re
->endp
[subexpr
], style
, NULL
);
2380 ** Search for a pattern in pattern list "patterns" with style "style"
2382 static highlightDataRec
*patternOfStyle(highlightDataRec
*patterns
, int style
)
2385 for (i
=0; patterns
[i
].style
!=0; i
++)
2386 if (patterns
[i
].style
== style
)
2387 return &patterns
[i
];
2388 if (style
== PLAIN_STYLE
|| style
== UNFINISHED_STYLE
)
2389 return &patterns
[0];
2393 static int max(int i1
, int i2
)
2395 return i1
>= i2
? i1
: i2
;
2398 static int min(int i1
, int i2
)
2400 return i1
<= i2
? i1
: i2
;
2403 static int indexOfNamedPattern(highlightPattern
*patList
, int nPats
,
2404 const char *patName
)
2408 if (patName
== NULL
)
2410 for (i
=0; i
<nPats
; i
++)
2411 if (!strcmp(patList
[i
].name
, patName
))
2416 static int findTopLevelParentIndex(highlightPattern
*patList
, int nPats
,
2422 while (patList
[topIndex
].subPatternOf
!= NULL
) {
2423 topIndex
= indexOfNamedPattern(patList
, nPats
,
2424 patList
[topIndex
].subPatternOf
);
2425 if (index
==topIndex
)
2426 return -1; /* amai: circular dependency ?! */
2432 ** Re-size (or re-height, anyhow) a window after adding or removing
2433 ** highlight fonts has changed the required vertical spacing (horizontal
2434 ** spacing is determined by the primary font, which doesn't change).
2436 ** Note that this messes up the window manager's height increment hint,
2437 ** which must be subsequently reset by UpdateWMSizeHints.
2439 static void updateWindowHeight(WindowInfo
*window
, int oldFontHeight
)
2441 int i
, borderHeight
, marginHeight
;
2442 Dimension windowHeight
, textAreaHeight
, textHeight
, newWindowHeight
;
2444 /* resize if there's only _one_ document in the window, to avoid
2445 the growing-window bug */
2446 if (NDocuments(window
) > 1)
2449 /* Decompose the window height into the part devoted to displaying
2450 text (textHeight) and the non-text part (boderHeight) */
2451 XtVaGetValues(window
->shell
, XmNheight
, &windowHeight
, NULL
);
2452 XtVaGetValues(window
->textArea
, XmNheight
, &textAreaHeight
,
2453 textNmarginHeight
, &marginHeight
, NULL
);
2454 textHeight
= textAreaHeight
- 2*marginHeight
;
2455 for (i
=0; i
<window
->nPanes
; i
++) {
2456 XtVaGetValues(window
->textPanes
[i
], XmNheight
, &textAreaHeight
, NULL
);
2457 textHeight
+= textAreaHeight
- 2*marginHeight
;
2459 borderHeight
= windowHeight
- textHeight
;
2461 /* Calculate a new window height appropriate for the new font */
2462 newWindowHeight
= (textHeight
*getFontHeight(window
)) / oldFontHeight
+
2465 /* Many window managers enforce window size increments even on client resize
2466 requests. Our height increment is probably wrong because it is still
2467 set for the previous font. Set the new height in advance, before
2468 attempting to resize. */
2469 XtVaSetValues(window
->shell
, XmNheightInc
, getFontHeight(window
), NULL
);
2471 /* Re-size the window */
2472 XtVaSetValues(window
->shell
, XmNheight
, newWindowHeight
, NULL
);
2476 ** Find the height currently being used to display text, which is
2477 ** a composite of all of the active highlighting fonts as determined by the
2478 ** text display component
2480 static int getFontHeight(WindowInfo
*window
)
2482 textDisp
*textD
= ((TextWidget
)window
->textArea
)->text
.textD
;
2484 return textD
->ascent
+ textD
->descent
;