1 static const char CVSID
[] = "$Id: highlight.c,v 1.42 2003/05/09 17:43:45 edg 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 *
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 *
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 *
23 * Nirvana Text Editor *
26 * Written by Mark Edel *
28 *******************************************************************************/
31 #include "../config.h"
34 #include "highlight.h"
40 #include "regularExp.h"
41 #include "highlightData.h"
42 #include "preferences.h"
44 #include "../util/misc.h"
45 #include "../util/DialogF.h"
53 #include "../util/VMSparam.h"
56 #include <sys/param.h>
63 #include <Xm/PrimitiveP.h>
70 /* How much re-parsing to do when an unfinished style is encountered */
71 #define PASS_2_REPARSE_CHUNK_SIZE 1000
73 /* Initial forward expansion of parsing region in incremental reparsing,
74 when style changes propagate forward beyond the original modification.
75 This distance is increased by a factor of two for each subsequent step. */
76 #define REPARSE_CHUNK_SIZE 80
78 /* Meanings of style buffer characters (styles). Don't use plain 'A' or 'B';
79 it causes problems with EBCDIC coding (possibly negative offsets when
81 #define UNFINISHED_STYLE ASCII_A
82 #define PLAIN_STYLE (ASCII_A+1)
83 #define IS_PLAIN(style) (style == PLAIN_STYLE || style == UNFINISHED_STYLE)
84 #define IS_STYLED(style) (style != PLAIN_STYLE && style != UNFINISHED_STYLE)
86 /* Compare two styles where one of the styles may not yet have been processed
87 with pass2 patterns */
88 #define EQUIVALENT_STYLE(style1, style2, firstPass2Style) (style1 == style2 || \
89 (style1 == UNFINISHED_STYLE && \
90 (style2 == PLAIN_STYLE || (unsigned char)style2 >= firstPass2Style)) || \
91 (style2 == UNFINISHED_STYLE && \
92 (style1 == PLAIN_STYLE || (unsigned char)style1 >= firstPass2Style)))
94 /* Scanning context can be reduced (with big efficiency gains) if we
95 know that patterns can't cross line boundaries, which is implied
96 by a context requirement of 1 line and 0 characters */
97 #define CAN_CROSS_LINE_BOUNDARIES(contextRequirements) \
98 (contextRequirements->nLines != 1 || contextRequirements->nChars != 0)
100 /* "Compiled" version of pattern specification */
101 typedef struct _highlightDataRec
{
105 regexp
*subPatternRE
;
108 signed char startSubexprs
[NSUBEXP
+1];
109 signed char endSubexprs
[NSUBEXP
+1];
112 int nSubBranches
; /* Number of top-level branches of subPatternRE */
114 struct _highlightDataRec
**subPatterns
;
117 /* Context requirements for incremental reparsing of a pattern set */
123 /* Data structure attached to window to hold all syntax highlighting
124 information (for both drawing and incremental reparsing) */
126 highlightDataRec
*pass1Patterns
;
127 highlightDataRec
*pass2Patterns
;
129 reparseContext contextRequirements
;
130 styleTableEntry
*styleTable
;
132 textBuffer
*styleBuffer
;
133 patternSet
*patternSetForWindow
;
134 } windowHighlightData
;
136 static windowHighlightData
*createHighlightData(WindowInfo
*window
,
138 static void freeHighlightData(windowHighlightData
*hd
);
139 static patternSet
*findPatternsForWindow(WindowInfo
*window
, int warn
);
140 static highlightDataRec
*compilePatterns(Widget dialogParent
,
141 highlightPattern
*patternSrc
, int nPatterns
);
142 static void freePatterns(highlightDataRec
*patterns
);
143 static void handleUnparsedRegion(WindowInfo
* win
, textBuffer
*buf
, int pos
);
144 static void handleUnparsedRegionCB(textDisp
*textD
, int pos
, void *cbArg
);
145 static void incrementalReparse(windowHighlightData
*highlightData
,
146 textBuffer
*buf
, int pos
, int nInserted
, const char *delimiters
);
147 static int parseBufferRange(highlightDataRec
*pass1Patterns
,
148 highlightDataRec
*pass2Patterns
, textBuffer
*buf
, textBuffer
*styleBuf
,
149 reparseContext
*contextRequirements
, int beginParse
, int endParse
,
150 const char *delimiters
);
151 static int parseString(highlightDataRec
*pattern
, char **string
,
152 char **styleString
, int length
, char *prevChar
, int anchored
,
153 const char *delimiters
, const char* lookBehindTo
);
154 static void passTwoParseString(highlightDataRec
*pattern
, char *string
,
155 char *styleString
, int length
, char *prevChar
, int anchored
,
156 const char *delimiters
, const char* lookBehindTo
);
157 static void fillStyleString(char **stringPtr
, char **stylePtr
, char *toPtr
,
158 char style
, char *prevChar
);
159 static void modifyStyleBuf(textBuffer
*styleBuf
, char *styleString
,
160 int startPos
, int endPos
, int firstPass2Style
);
161 static int lastModified(textBuffer
*styleBuf
);
162 static int max(int i1
, int i2
);
163 static int min(int i1
, int i2
);
164 static char getPrevChar(textBuffer
*buf
, int pos
);
165 static regexp
*compileREAndWarn(Widget parent
, const char *re
);
166 static int parentStyleOf(const char *parentStyles
, int style
);
167 static int isParentStyle(const char *parentStyles
, int style1
, int style2
);
168 static int findSafeParseRestartPos(textBuffer
*buf
,
169 windowHighlightData
*highlightData
, int *pos
);
170 static int backwardOneContext(textBuffer
*buf
, reparseContext
*context
,
172 static int forwardOneContext(textBuffer
*buf
, reparseContext
*context
,
174 static void recolorSubexpr(regexp
*re
, int subexpr
, int style
, char *string
,
176 static int indexOfNamedPattern(highlightPattern
*patList
, int nPats
,
177 const char *patName
);
178 static int findTopLevelParentIndex(highlightPattern
*patList
, int nPats
,
180 static highlightDataRec
*patternOfStyle(highlightDataRec
*patterns
, int style
);
181 static void updateWindowHeight(WindowInfo
*window
, int oldFontHeight
);
182 static int getFontHeight(WindowInfo
*window
);
185 ** Buffer modification callback for triggering re-parsing of modified
186 ** text and keeping the style buffer synchronized with the text buffer.
187 ** This must be attached to the the text buffer BEFORE any widget text
188 ** display callbacks, so it can get the style buffer ready to be used
189 ** by the text display routines.
191 ** Update the style buffer for changes to the text, and mark any style
192 ** changes by selecting the region in the style buffer. This strange
193 ** protocol of informing the text display to redraw style changes by
194 ** making selections in the style buffer is used because this routine
195 ** is intended to be called BEFORE the text display callback paints the
196 ** text (to minimize redraws and, most importantly, to synchronize the
197 ** style buffer with the text buffer). If we redraw now, the text
198 ** display hasn't yet processed the modification, redrawing later is
199 ** not only complicated, it will double-draw almost everything typed.
201 ** Note: This routine must be kept efficient. It is called for every
204 void SyntaxHighlightModifyCB(int pos
, int nInserted
, int nDeleted
,
205 int nRestyled
, char *deletedText
, void *cbArg
)
207 WindowInfo
*window
= (WindowInfo
*)cbArg
;
209 *highlightData
= (windowHighlightData
*)window
->highlightData
;
211 if (highlightData
== NULL
)
214 /* Restyling-only modifications (usually a primary or secondary selection)
215 don't require any processing, but clear out the style buffer selection
216 so the widget doesn't think it has to keep redrawing the old area */
217 if (nInserted
== 0 && nDeleted
== 0) {
218 BufUnselect(highlightData
->styleBuffer
);
222 /* First and foremost, the style buffer must track the text buffer
223 accurately and correctly */
228 insStyle
= XtMalloc(sizeof(char) * (nInserted
+ 1));
229 for (i
=0; i
<nInserted
; i
++)
230 insStyle
[i
] = UNFINISHED_STYLE
;
232 BufReplace(highlightData
->styleBuffer
, pos
, pos
+nDeleted
, insStyle
);
235 BufRemove(highlightData
->styleBuffer
, pos
, pos
+nDeleted
);
238 /* Mark the changed region in the style buffer as requiring redraw. This
239 is not necessary for getting it redrawn, it will be redrawn anyhow by
240 the text display callback, but it clears the previous selection and
241 saves the modifyStyleBuf routine from unnecessary work in tracking
242 changes that are already scheduled for redraw */
243 BufSelect(highlightData
->styleBuffer
, pos
, pos
+nInserted
);
245 /* Re-parse around the changed region */
246 if (highlightData
->pass1Patterns
)
247 incrementalReparse(highlightData
, window
->buffer
, pos
, nInserted
,
248 GetWindowDelimiters(window
));
252 ** Turn on syntax highlighting. If "warn" is true, warn the user when it
253 ** can't be done, otherwise, just return.
255 void StartHighlighting(WindowInfo
*window
, int warn
)
257 patternSet
*patterns
;
258 windowHighlightData
*highlightData
;
259 char *stylePtr
, *styleString
, *stringPtr
, *bufString
;
260 char prevChar
= '\0';
261 int i
, oldFontHeight
;
263 /* Find the pattern set matching the window's current
264 language mode, tell the user if it can't be done */
265 patterns
= findPatternsForWindow(window
, warn
);
266 if (patterns
== NULL
)
269 /* Compile the patterns */
270 highlightData
= createHighlightData(window
, patterns
);
271 if (highlightData
== NULL
)
274 /* Prepare for a long delay, refresh display and put up a watch cursor */
275 BeginWait(window
->shell
);
276 XmUpdateDisplay(window
->shell
);
278 /* Parse the buffer with pass 1 patterns. If there are none, initialize
279 the style buffer to all UNFINISHED_STYLE to trigger parsing later */
280 stylePtr
= styleString
= XtMalloc(window
->buffer
->length
+ 1);
281 if (highlightData
->pass1Patterns
== NULL
) {
282 for (i
=0; i
<window
->buffer
->length
; i
++)
283 *stylePtr
++ = UNFINISHED_STYLE
;
285 stringPtr
= bufString
= BufGetAll(window
->buffer
);
286 parseString(highlightData
->pass1Patterns
, &stringPtr
, &stylePtr
,
287 window
->buffer
->length
, &prevChar
, False
,
288 GetWindowDelimiters(window
), bufString
);
292 BufSetAll(highlightData
->styleBuffer
, styleString
);
295 /* install highlight pattern data in the window data structure */
296 window
->highlightData
= highlightData
;
298 /* Get the height of the current font in the window, to be used after
299 highlighting is turned on to resize the window to make room for
300 additional highlight fonts which may be sized differently */
301 oldFontHeight
= getFontHeight(window
);
303 /* Attach highlight information to text widgets in each pane */
304 AttachHighlightToWidget(window
->textArea
, window
);
305 for (i
=0; i
<window
->nPanes
; i
++)
306 AttachHighlightToWidget(window
->textPanes
[i
], window
);
308 /* Re-size the window to fit the highlight fonts properly & tell the
309 window manager about the potential line-height change as well */
310 updateWindowHeight(window
, oldFontHeight
);
311 UpdateWMSizeHints(window
);
312 UpdateMinPaneHeights(window
);
314 /* Make sure that if the window has grown, the additional area gets
315 repainted. Otherwise, it is possible that the area gets moved before a
316 repaint event is received and the area doesn't get repainted at all
317 (eg. because of a -line command line argument that moves the text). */
318 XmUpdateDisplay(window
->shell
);
319 EndWait(window
->shell
);
323 ** Turn off syntax highlighting and free style buffer, compiled patterns, and
326 void StopHighlighting(WindowInfo
*window
)
328 int i
, oldFontHeight
;
330 if (window
->highlightData
==NULL
)
333 /* Get the line height being used by the highlight fonts in the window,
334 to be used after highlighting is turned off to resize the window
335 back to the line height of the primary font */
336 oldFontHeight
= getFontHeight(window
);
338 /* Free and remove the highlight data from the window */
339 freeHighlightData((windowHighlightData
*)window
->highlightData
);
340 window
->highlightData
= NULL
;
342 /* Remove and detach style buffer and style table from all text
343 display(s) of window, and redisplay without highlighting */
344 RemoveWidgetHighlight(window
->textArea
);
345 for (i
=0; i
<window
->nPanes
; i
++)
346 RemoveWidgetHighlight(window
->textPanes
[i
]);
348 /* Re-size the window to fit the primary font properly & tell the window
349 manager about the potential line-height change as well */
350 updateWindowHeight(window
, oldFontHeight
);
351 UpdateWMSizeHints(window
);
352 UpdateMinPaneHeights(window
);
356 ** Free highlighting data from a window destined for destruction, without
359 void FreeHighlightingData(WindowInfo
*window
)
363 if (window
->highlightData
== NULL
)
366 /* Free and remove the highlight data from the window */
367 freeHighlightData((windowHighlightData
*)window
->highlightData
);
368 window
->highlightData
= NULL
;
370 /* The text display may make a last desperate attempt to access highlight
371 information when it is destroyed, which would be a disaster. */
372 ((TextWidget
)window
->textArea
)->text
.textD
->styleBuffer
= NULL
;
373 for (i
=0; i
<window
->nPanes
; i
++)
374 ((TextWidget
)window
->textPanes
[i
])->text
.textD
->styleBuffer
= NULL
;
378 ** Attach style information from a window's highlight data to a
379 ** text widget and redisplay.
381 void AttachHighlightToWidget(Widget widget
, WindowInfo
*window
)
383 windowHighlightData
*highlightData
=
384 (windowHighlightData
*)window
->highlightData
;
386 TextDAttachHighlightData(((TextWidget
)widget
)->text
.textD
,
387 highlightData
->styleBuffer
, highlightData
->styleTable
,
388 highlightData
->nStyles
, UNFINISHED_STYLE
, handleUnparsedRegionCB
,
393 ** Remove style information from a text widget and redisplay it.
395 void RemoveWidgetHighlight(Widget widget
)
397 TextDAttachHighlightData(((TextWidget
)widget
)->text
.textD
,
398 NULL
, NULL
, 0, UNFINISHED_STYLE
, NULL
, NULL
);
402 ** Change highlight fonts and/or styles in a highlighted window, without
405 void UpdateHighlightStyles(WindowInfo
*window
)
407 patternSet
*patterns
;
408 windowHighlightData
*highlightData
;
409 windowHighlightData
*oldHighlightData
=
410 (windowHighlightData
*)window
->highlightData
;
411 textBuffer
*styleBuffer
;
414 /* Do nothing if window not highlighted */
415 if (window
->highlightData
== NULL
)
418 /* Find the pattern set for the window's current language mode */
419 patterns
= findPatternsForWindow(window
, False
);
420 if (patterns
== NULL
) {
421 StopHighlighting(window
);
425 /* Build new patterns */
426 highlightData
= createHighlightData(window
, patterns
);
427 if (highlightData
== NULL
) {
428 StopHighlighting(window
);
432 /* Update highlight pattern data in the window data structure, but
433 preserve all of the effort that went in to parsing the buffer
434 by swapping it with the empty one in highlightData (which is then
435 freed in freeHighlightData) */
436 styleBuffer
= oldHighlightData
->styleBuffer
;
437 oldHighlightData
->styleBuffer
= highlightData
->styleBuffer
;
438 freeHighlightData(oldHighlightData
);
439 highlightData
->styleBuffer
= styleBuffer
;
440 window
->highlightData
= highlightData
;
442 /* Attach new highlight information to text widgets in each pane
444 AttachHighlightToWidget(window
->textArea
, window
);
445 for (i
=0; i
<window
->nPanes
; i
++)
446 AttachHighlightToWidget(window
->textPanes
[i
], window
);
450 ** Do a test compile of patterns in "patSet" and report problems to the
451 ** user via dialog. Returns True if patterns are ok.
453 ** This is somewhat kludgy in that it uses createHighlightData, which
454 ** requires a window to find the fonts to use, and just uses a random
455 ** window from the window list. Since the window is used to get the
456 ** dialog parent as well, in non-popups-under-pointer mode, these dialogs
457 ** will appear in odd places on the screen.
459 int TestHighlightPatterns(patternSet
*patSet
)
461 windowHighlightData
*highlightData
;
463 /* Compile the patterns (passing a random window as a source for fonts, and
464 parent for dialogs, since we really don't care what fonts are used) */
465 highlightData
= createHighlightData(WindowList
, patSet
);
466 if (highlightData
== NULL
)
468 freeHighlightData(highlightData
);
473 ** Returns the highlight style of the character at a given position of a
474 ** window. To avoid breaking encapsulation, the highlight style is converted
475 ** to a void* pointer (no other module has to know that characters are used
476 ** to represent highlight styles; that would complicate future extensions).
477 ** Returns NULL if the window has highlighting turned off.
478 ** The only guarantee that this function offers, is that when the same
479 ** pointer is returned for two positions, the corresponding characters have
480 ** the same highlight style.
482 void* GetHighlightInfo(WindowInfo
*window
, int pos
)
485 highlightDataRec
*pattern
= NULL
;
486 windowHighlightData
*highlightData
=
487 (windowHighlightData
*)window
->highlightData
;
491 /* Be careful with signed/unsigned conversions. NO conversion here! */
492 style
= (int)BufGetCharacter(highlightData
->styleBuffer
, pos
);
494 /* Beware of unparsed regions. */
495 if (style
== UNFINISHED_STYLE
) {
496 handleUnparsedRegion(window
, highlightData
->styleBuffer
, pos
);
497 style
= (int)BufGetCharacter(highlightData
->styleBuffer
, pos
);
500 if (highlightData
->pass1Patterns
) {
501 pattern
= patternOfStyle(highlightData
->pass1Patterns
, style
);
504 if (!pattern
&& highlightData
->pass2Patterns
) {
505 pattern
= patternOfStyle(highlightData
->pass2Patterns
, style
);
511 return (void*)pattern
->userStyleIndex
;
515 ** Free allocated memory associated with highlight data, including compiled
516 ** regular expressions, style buffer and style table. Note: be sure to
517 ** NULL out the widget references to the objects in this structure before
518 ** calling this. Because of the slow, multi-phase destruction of
519 ** widgets, this data can be referenced even AFTER destroying the widget.
521 static void freeHighlightData(windowHighlightData
*hd
)
525 if (hd
->pass1Patterns
!= NULL
)
526 freePatterns(hd
->pass1Patterns
);
527 if (hd
->pass2Patterns
!= NULL
)
528 freePatterns(hd
->pass2Patterns
);
529 XtFree(hd
->parentStyles
);
530 BufFree(hd
->styleBuffer
);
531 XtFree((char *)hd
->styleTable
);
536 ** Find the pattern set matching the window's current language mode, or
537 ** tell the user if it can't be done (if warn is True) and return NULL.
539 static patternSet
*findPatternsForWindow(WindowInfo
*window
, int warn
)
541 patternSet
*patterns
;
544 /* Find the window's language mode. If none is set, warn user */
545 modeName
= LanguageModeName(window
->languageMode
);
546 if (modeName
== NULL
) {
548 DialogF(DF_WARN
, window
->shell
, 1, "Language Mode",
549 "No language-specific mode has been set for this file.\n\n"
550 "To use syntax highlighting in this window, please select a\n"
551 "language from the Preferences -> Language Modes menu.\n\n"
552 "New language modes and syntax highlighting patterns can be\n"
553 "added via Preferences -> Default Settings -> Language Modes,\n"
554 "and Preferences -> Default Settings -> Syntax Highlighting.",
559 /* Look up the appropriate pattern for the language */
560 patterns
= FindPatternSet(modeName
);
561 if (patterns
== NULL
)
565 DialogF(DF_WARN
, window
->shell
, 1, "Language Mode",
566 "Syntax highlighting is not available in language\n"
568 "You can create new syntax highlight patterns in the\n"
569 "Preferences -> Default Settings -> Syntax Highlighting\n"
570 "dialog, or choose a different language mode from:\n"
571 "Preferences -> Language Mode.", "Dismiss", modeName
);
580 ** Create complete syntax highlighting information from "patternSrc", using
581 ** highlighting fonts from "window", includes pattern compilation. If errors
582 ** are encountered, warns user with a dialog and returns NULL. To free the
583 ** allocated components of the returned data structure, use freeHighlightData.
585 static windowHighlightData
*createHighlightData(WindowInfo
*window
,
588 highlightPattern
*patternSrc
= patSet
->patterns
;
589 int nPatterns
= patSet
->nPatterns
;
590 int contextLines
= patSet
->lineContext
;
591 int contextChars
= patSet
->charContext
;
592 int i
, nPass1Patterns
, nPass2Patterns
;
593 int noPass1
, noPass2
;
594 char *parentStyles
, *parentStylesPtr
, *parentName
;
595 highlightPattern
*pass1PatternSrc
, *pass2PatternSrc
, *p1Ptr
, *p2Ptr
;
596 styleTableEntry
*styleTable
, *styleTablePtr
;
597 textBuffer
*styleBuf
;
598 highlightDataRec
*pass1Pats
, *pass2Pats
;
599 windowHighlightData
*highlightData
;
601 /* The highlighting code can't handle empty pattern sets, quietly say no */
607 /* Check that the styles and parent pattern names actually exist */
608 if (!NamedStyleExists("Plain"))
610 DialogF(DF_WARN
, window
->shell
, 1, "Highlight Style",
611 "Highlight style \"plain\" is missing", "Dismiss");
615 for (i
=0; i
<nPatterns
; i
++)
617 if (patternSrc
[i
].subPatternOf
!= NULL
618 && indexOfNamedPattern(patternSrc
, nPatterns
,
619 patternSrc
[i
].subPatternOf
) == -1)
621 DialogF(DF_WARN
, window
->shell
, 1, "Parent Pattern",
622 "Parent field \"%s\" in pattern \"%s\"\n"
623 "does not match any highlight patterns in this set",
624 "Dismiss", patternSrc
[i
].subPatternOf
, patternSrc
[i
].name
);
629 for (i
=0; i
<nPatterns
; i
++)
631 if (!NamedStyleExists(patternSrc
[i
].style
))
633 DialogF(DF_WARN
, window
->shell
, 1, "Highlight Style",
634 "Style \"%s\" named in pattern \"%s\"\n"
635 "does not match any existing style", "Dismiss",
636 patternSrc
[i
].style
, patternSrc
[i
].name
);
641 /* Make DEFER_PARSING flags agree with top level patterns (originally,
642 individual flags had to be correct and were checked here, but dialog now
643 shows this setting only on top patterns which is much less confusing) */
644 for (i
= 0; i
< nPatterns
; i
++)
646 if (patternSrc
[i
].subPatternOf
!= NULL
)
650 parentindex
=findTopLevelParentIndex(patternSrc
, nPatterns
, i
);
653 DialogF(DF_WARN
, window
->shell
, 1, "Parent Pattern",
654 "Pattern \"%s\" does not have valid parent", "Dismiss",
659 if (patternSrc
[parentindex
].flags
& DEFER_PARSING
)
661 patternSrc
[i
].flags
|= DEFER_PARSING
;
664 patternSrc
[i
].flags
&= ~DEFER_PARSING
;
669 /* Sort patterns into those to be used in pass 1 parsing, and those to
670 be used in pass 2, and add default pattern (0) to each list */
673 for (i
=0; i
<nPatterns
; i
++)
674 if (patternSrc
[i
].flags
& DEFER_PARSING
)
678 p1Ptr
= pass1PatternSrc
= (highlightPattern
*)XtMalloc(
679 sizeof(highlightPattern
) * nPass1Patterns
);
680 p2Ptr
= pass2PatternSrc
= (highlightPattern
*)XtMalloc(
681 sizeof(highlightPattern
) * nPass2Patterns
);
682 p1Ptr
->name
= p2Ptr
->name
= "top";
683 p1Ptr
->startRE
= p2Ptr
->startRE
= NULL
;
684 p1Ptr
->endRE
= p2Ptr
->endRE
= NULL
;
685 p1Ptr
->errorRE
= p2Ptr
->errorRE
= NULL
;
686 p1Ptr
->style
= p2Ptr
->style
= "Plain";
687 p1Ptr
->subPatternOf
= p2Ptr
->subPatternOf
= NULL
;
688 p1Ptr
->flags
= p2Ptr
->flags
= 0;
690 for (i
=0; i
<nPatterns
; i
++) {
691 if (patternSrc
[i
].flags
& DEFER_PARSING
)
692 *p2Ptr
++ = patternSrc
[i
];
694 *p1Ptr
++ = patternSrc
[i
];
697 /* If a particular pass is empty except for the default pattern, don't
698 bother compiling it or setting up styles */
699 if (nPass1Patterns
== 1)
701 if (nPass2Patterns
== 1)
704 /* Compile patterns */
705 if (nPass1Patterns
== 0)
708 pass1Pats
= compilePatterns(window
->shell
, pass1PatternSrc
,
710 if (pass1Pats
== NULL
)
713 if (nPass2Patterns
== 0)
716 pass2Pats
= compilePatterns(window
->shell
, pass2PatternSrc
,
718 if (pass2Pats
== NULL
)
722 /* Set pattern styles. If there are pass 2 patterns, pass 1 pattern
723 0 should have a default style of UNFINISHED_STYLE. With no pass 2
724 patterns, unstyled areas of pass 1 patterns should be PLAIN_STYLE
725 to avoid triggering re-parsing every time they are encountered */
726 noPass1
= nPass1Patterns
== 0;
727 noPass2
= nPass2Patterns
== 0;
729 pass1Pats
[0].style
= PLAIN_STYLE
;
731 pass2Pats
[0].style
= PLAIN_STYLE
;
733 pass1Pats
[0].style
= UNFINISHED_STYLE
;
734 pass2Pats
[0].style
= PLAIN_STYLE
;
736 for (i
=1; i
<nPass1Patterns
; i
++)
737 pass1Pats
[i
].style
= PLAIN_STYLE
+ i
;
738 for (i
=1; i
<nPass2Patterns
; i
++)
739 pass2Pats
[i
].style
= PLAIN_STYLE
+ (noPass1
? 0 : nPass1Patterns
-1) + i
;
741 /* Create table for finding parent styles */
742 parentStylesPtr
= parentStyles
= XtMalloc(nPass1Patterns
+nPass2Patterns
+2);
743 *parentStylesPtr
++ = '\0';
744 *parentStylesPtr
++ = '\0';
745 for (i
=1; i
<nPass1Patterns
; i
++) {
746 parentName
= pass1PatternSrc
[i
].subPatternOf
;
747 *parentStylesPtr
++ = parentName
== NULL
? PLAIN_STYLE
:
748 pass1Pats
[indexOfNamedPattern(pass1PatternSrc
,
749 nPass1Patterns
, parentName
)].style
;
751 for (i
=1; i
<nPass2Patterns
; i
++) {
752 parentName
= pass2PatternSrc
[i
].subPatternOf
;
753 *parentStylesPtr
++ = parentName
== NULL
? PLAIN_STYLE
:
754 pass2Pats
[indexOfNamedPattern(pass2PatternSrc
,
755 nPass2Patterns
, parentName
)].style
;
758 /* Set up table for mapping colors and fonts to syntax */
759 styleTablePtr
= styleTable
= (styleTableEntry
*)XtMalloc(
760 sizeof(styleTableEntry
) * (nPass1Patterns
+ nPass2Patterns
+ 1));
761 #define setStyleTablePtr(styleTablePtr, patternSrc) \
763 styleTableEntry *p = styleTablePtr; \
764 highlightPattern *pat = patternSrc; \
767 p->highlightName = pat->name; \
768 p->styleName = pat->style; \
769 p->colorName = ColorOfNamedStyle(pat->style); \
770 p->bgColorName = BgColorOfNamedStyle(pat->style); \
771 p->isBold = FontOfNamedStyleIsBold(pat->style); \
772 p->isItalic = FontOfNamedStyleIsItalic(pat->style); \
773 /* And now for the more physical stuff */ \
774 p->color = AllocColor(window->textArea, p->colorName, &r, &g, &b); \
778 if (p->bgColorName) { \
779 p->bgColor = AllocColor(window->textArea, p->bgColorName, &r, &g, &b); \
785 p->bgColor = p->color; \
790 p->font = FontOfNamedStyle(window, pat->style); \
793 /* PLAIN_STYLE (pass 1) */
794 styleTablePtr
->underline
= FALSE
;
795 setStyleTablePtr(styleTablePtr
++,
796 noPass1
? &pass2PatternSrc
[0] : &pass1PatternSrc
[0]);
797 /* PLAIN_STYLE (pass 2) */
798 styleTablePtr
->underline
= FALSE
;
799 setStyleTablePtr(styleTablePtr
++,
800 noPass2
? &pass1PatternSrc
[0] : &pass2PatternSrc
[0]);
801 /* explicit styles (pass 1) */
802 for (i
=1; i
<nPass1Patterns
; i
++) {
803 styleTablePtr
->underline
= FALSE
;
804 setStyleTablePtr(styleTablePtr
++, &pass1PatternSrc
[i
]);
806 /* explicit styles (pass 2) */
807 for (i
=1; i
<nPass2Patterns
; i
++) {
808 styleTablePtr
->underline
= FALSE
;
809 setStyleTablePtr(styleTablePtr
++, &pass2PatternSrc
[i
]);
812 /* Free the temporary sorted pattern source list */
813 XtFree((char *)pass1PatternSrc
);
814 XtFree((char *)pass2PatternSrc
);
816 /* Create the style buffer */
817 styleBuf
= BufCreate();
819 /* Collect all of the highlighting information in a single structure */
820 highlightData
=(windowHighlightData
*)XtMalloc(sizeof(windowHighlightData
));
821 highlightData
->pass1Patterns
= pass1Pats
;
822 highlightData
->pass2Patterns
= pass2Pats
;
823 highlightData
->parentStyles
= parentStyles
;
824 highlightData
->styleTable
= styleTable
;
825 highlightData
->nStyles
= styleTablePtr
- styleTable
;
826 highlightData
->styleBuffer
= styleBuf
;
827 highlightData
->contextRequirements
.nLines
= contextLines
;
828 highlightData
->contextRequirements
.nChars
= contextChars
;
829 highlightData
->patternSetForWindow
= patSet
;
831 return highlightData
;
835 ** Transform pattern sources into the compiled highlight information
836 ** actually used by the code. Output is a tree of highlightDataRec structures
837 ** containing compiled regular expressions and style information.
839 static highlightDataRec
*compilePatterns(Widget dialogParent
,
840 highlightPattern
*patternSrc
, int nPatterns
)
842 int i
, nSubExprs
, patternNum
, length
, subPatIndex
, subExprNum
, charsRead
;
844 char *ptr
, *bigPattern
, *compileMsg
;
845 highlightDataRec
*compiledPats
;
847 /* Allocate memory for the compiled patterns. The list is terminated
848 by a record with style == 0. */
849 compiledPats
= (highlightDataRec
*)XtMalloc(sizeof(highlightDataRec
) *
851 compiledPats
[nPatterns
].style
= 0;
853 /* Build the tree of parse expressions */
854 for (i
=0; i
<nPatterns
; i
++) {
855 compiledPats
[i
].nSubPatterns
= 0;
856 compiledPats
[i
].nSubBranches
= 0;
858 for (i
=1; i
<nPatterns
; i
++)
859 if (patternSrc
[i
].subPatternOf
== NULL
)
860 compiledPats
[0].nSubPatterns
++;
862 compiledPats
[indexOfNamedPattern(patternSrc
, nPatterns
,
863 patternSrc
[i
].subPatternOf
)].nSubPatterns
++;
864 for (i
=0; i
<nPatterns
; i
++)
865 compiledPats
[i
].subPatterns
= compiledPats
[i
].nSubPatterns
== 0 ?
866 NULL
: (highlightDataRec
**)XtMalloc(
867 sizeof(highlightDataRec
*) * compiledPats
[i
].nSubPatterns
);
868 for (i
=0; i
<nPatterns
; i
++)
869 compiledPats
[i
].nSubPatterns
= 0;
870 for (i
=1; i
<nPatterns
; i
++) {
871 if (patternSrc
[i
].subPatternOf
== NULL
) {
872 compiledPats
[0].subPatterns
[compiledPats
[0].nSubPatterns
++] =
875 parentIndex
= indexOfNamedPattern(patternSrc
,
876 nPatterns
, patternSrc
[i
].subPatternOf
);
877 compiledPats
[parentIndex
].subPatterns
[compiledPats
[parentIndex
].
878 nSubPatterns
++] = &compiledPats
[i
];
882 /* Process color-only sub patterns (no regular expressions to match,
883 just colors and fonts for sub-expressions of the parent pattern */
884 for (i
=0; i
<nPatterns
; i
++) {
885 compiledPats
[i
].colorOnly
= patternSrc
[i
].flags
& COLOR_ONLY
;
886 compiledPats
[i
].userStyleIndex
= IndexOfNamedStyle(patternSrc
[i
].style
);
887 if (compiledPats
[i
].colorOnly
&& compiledPats
[i
].nSubPatterns
!= 0)
889 DialogF(DF_WARN
, dialogParent
, 1, "Color-only Pattern",
890 "Color-only pattern \"%s\" may not have subpatterns",
891 "Dismiss", patternSrc
[i
].name
);
895 if (patternSrc
[i
].startRE
!= NULL
) {
896 ptr
= patternSrc
[i
].startRE
;
899 compiledPats
[i
].startSubexprs
[nSubExprs
++] = 0;
901 } else if (sscanf(ptr
, "\\%d%n", &subExprNum
, &charsRead
)==1) {
902 compiledPats
[i
].startSubexprs
[nSubExprs
++] = subExprNum
;
908 compiledPats
[i
].startSubexprs
[nSubExprs
] = -1;
910 if (patternSrc
[i
].endRE
!= NULL
) {
911 ptr
= patternSrc
[i
].endRE
;
914 compiledPats
[i
].endSubexprs
[nSubExprs
++] = 0;
916 } else if (sscanf(ptr
, "\\%d%n", &subExprNum
, &charsRead
)==1) {
917 compiledPats
[i
].endSubexprs
[nSubExprs
++] = subExprNum
;
923 compiledPats
[i
].endSubexprs
[nSubExprs
] = -1;
926 /* Compile regular expressions for all highlight patterns */
927 for (i
=0; i
<nPatterns
; i
++) {
928 if (patternSrc
[i
].startRE
== NULL
|| compiledPats
[i
].colorOnly
)
929 compiledPats
[i
].startRE
= NULL
;
931 if ((compiledPats
[i
].startRE
= compileREAndWarn(dialogParent
,
932 patternSrc
[i
].startRE
)) == NULL
)
935 if (patternSrc
[i
].endRE
== NULL
|| compiledPats
[i
].colorOnly
)
936 compiledPats
[i
].endRE
= NULL
;
938 if ((compiledPats
[i
].endRE
= compileREAndWarn(dialogParent
,
939 patternSrc
[i
].endRE
)) == NULL
)
942 if (patternSrc
[i
].errorRE
== NULL
)
943 compiledPats
[i
].errorRE
= NULL
;
945 if ((compiledPats
[i
].errorRE
= compileREAndWarn(dialogParent
,
946 patternSrc
[i
].errorRE
)) == NULL
)
951 /* Construct and compile the great hairy pattern to match the OR of the
952 end pattern, the error pattern, and all of the start patterns of the
954 for (patternNum
=0; patternNum
<nPatterns
; patternNum
++) {
955 if (patternSrc
[patternNum
].endRE
== NULL
&&
956 patternSrc
[patternNum
].errorRE
== NULL
&&
957 compiledPats
[patternNum
].nSubPatterns
== 0) {
958 compiledPats
[patternNum
].subPatternRE
= NULL
;
961 length
= (compiledPats
[patternNum
].colorOnly
||
962 patternSrc
[patternNum
].endRE
== NULL
) ? 0 :
963 strlen(patternSrc
[patternNum
].endRE
) + 5;
964 length
+= patternSrc
[patternNum
].errorRE
== NULL
? 0 :
965 strlen(patternSrc
[patternNum
].errorRE
) + 5;
966 for (i
=0; i
<compiledPats
[patternNum
].nSubPatterns
; i
++) {
967 subPatIndex
= compiledPats
[patternNum
].subPatterns
[i
]-compiledPats
;
968 length
+= compiledPats
[subPatIndex
].colorOnly
? 0 :
969 strlen(patternSrc
[subPatIndex
].startRE
) + 5;
972 compiledPats
[patternNum
].subPatternRE
= NULL
;
975 bigPattern
= XtMalloc(sizeof(char) * (length
+1));
977 if (patternSrc
[patternNum
].endRE
!= NULL
) {
978 *ptr
++ = '('; *ptr
++ = '?'; *ptr
++ = ':';
979 strcpy(ptr
, patternSrc
[patternNum
].endRE
);
980 ptr
+= strlen(patternSrc
[patternNum
].endRE
);
983 compiledPats
[patternNum
].nSubBranches
++;
985 if (patternSrc
[patternNum
].errorRE
!= NULL
) {
986 *ptr
++ = '('; *ptr
++ = '?'; *ptr
++ = ':';
987 strcpy(ptr
, patternSrc
[patternNum
].errorRE
);
988 ptr
+= strlen(patternSrc
[patternNum
].errorRE
);
991 compiledPats
[patternNum
].nSubBranches
++;
993 for (i
=0; i
<compiledPats
[patternNum
].nSubPatterns
; i
++) {
994 subPatIndex
= compiledPats
[patternNum
].subPatterns
[i
]-compiledPats
;
995 if (compiledPats
[subPatIndex
].colorOnly
)
997 *ptr
++ = '('; *ptr
++ = '?'; *ptr
++ = ':';
998 strcpy(ptr
, patternSrc
[subPatIndex
].startRE
);
999 ptr
+= strlen(patternSrc
[subPatIndex
].startRE
);
1002 compiledPats
[patternNum
].nSubBranches
++;
1005 compiledPats
[patternNum
].subPatternRE
= CompileRE(bigPattern
,
1006 &compileMsg
, REDFLT_STANDARD
);
1007 if (compiledPats
[patternNum
].subPatternRE
== NULL
) {
1008 fprintf(stderr
, "Error compiling syntax highlight patterns:\n%s",
1015 /* Copy remaining parameters from pattern template to compiled tree */
1016 for (i
=0; i
<nPatterns
; i
++)
1017 compiledPats
[i
].flags
= patternSrc
[i
].flags
;
1019 return compiledPats
;
1023 ** Free a pattern list and all of its allocated components
1025 static void freePatterns(highlightDataRec
*patterns
)
1029 for (i
=0; patterns
[i
].style
!=0; i
++) {
1030 if (patterns
[i
].startRE
!= NULL
)
1031 free((char *)patterns
[i
].startRE
);
1032 if (patterns
[i
].endRE
!= NULL
)
1033 free((char *)patterns
[i
].endRE
);
1034 if (patterns
[i
].errorRE
!= NULL
)
1035 XtFree((char *)patterns
[i
].errorRE
);
1036 if (patterns
[i
].subPatternRE
!= NULL
)
1037 free((char *)patterns
[i
].subPatternRE
);
1039 for (i
=0; patterns
[i
].style
!=0; i
++)
1040 if (patterns
[i
].subPatterns
!= NULL
)
1041 XtFree((char *)patterns
[i
].subPatterns
);
1042 XtFree((char *)patterns
);
1046 ** Find the highlightPattern structure with a given name in the window.
1048 highlightPattern
*FindPatternOfWindow(WindowInfo
*window
, char *name
)
1050 windowHighlightData
*hData
= (windowHighlightData
*)window
->highlightData
;
1054 if (hData
&& (set
= hData
->patternSetForWindow
)) {
1055 for (i
= 0; i
< set
->nPatterns
; i
++)
1056 if (strcmp(set
->patterns
[i
].name
, name
) == 0)
1057 return &set
->patterns
[i
];
1063 ** Picks up the entry in the style buffer for the position (if any). Rather
1064 ** like styleOfPos() in textDisp.c. Returns the style code or zero.
1066 int HighlightCodeOfPos(WindowInfo
*window
, int pos
)
1068 windowHighlightData
*highlightData
=
1069 (windowHighlightData
*)window
->highlightData
;
1070 textBuffer
*styleBuf
=
1071 highlightData
? highlightData
->styleBuffer
: NULL
;
1074 if (styleBuf
!= NULL
) {
1075 hCode
= (unsigned char)BufGetCharacter(styleBuf
, pos
);
1076 if (hCode
== UNFINISHED_STYLE
) {
1077 /* encountered "unfinished" style, trigger parsing */
1078 handleUnparsedRegion(window
, highlightData
->styleBuffer
, pos
);
1079 hCode
= (unsigned char)BufGetCharacter(styleBuf
, pos
);
1086 ** Returns the length over which a particular highlight code applies, starting
1087 ** at pos. If the initial code value *checkCode is zero, the highlight code of
1090 /* YOO: This is called form only one other function, which uses a constant
1091 for checkCode and never evaluates it after the call. */
1092 int HighlightLengthOfCodeFromPos(WindowInfo
*window
, int pos
, int *checkCode
)
1094 windowHighlightData
*highlightData
=
1095 (windowHighlightData
*)window
->highlightData
;
1096 textBuffer
*styleBuf
=
1097 highlightData
? highlightData
->styleBuffer
: NULL
;
1101 if (styleBuf
!= NULL
) {
1102 hCode
= (unsigned char)BufGetCharacter(styleBuf
, pos
);
1105 if (*checkCode
== 0)
1107 while (hCode
== *checkCode
|| hCode
== UNFINISHED_STYLE
) {
1108 if (hCode
== UNFINISHED_STYLE
) {
1109 /* encountered "unfinished" style, trigger parsing, then loop */
1110 handleUnparsedRegion(window
, highlightData
->styleBuffer
, pos
);
1111 hCode
= (unsigned char)BufGetCharacter(styleBuf
, pos
);
1114 /* advance the position and get the new code */
1115 hCode
= (unsigned char)BufGetCharacter(styleBuf
, ++pos
);
1119 return pos
- oldPos
;
1123 ** Returns a pointer to the entry in the style table for the entry of code
1126 static styleTableEntry
*styleTableEntryOfCode(WindowInfo
*window
, int hCode
)
1128 windowHighlightData
*highlightData
=
1129 (windowHighlightData
*)window
->highlightData
;
1131 hCode
-= UNFINISHED_STYLE
; /* get the correct index value */
1132 if (!highlightData
|| hCode
< 0 || hCode
>= highlightData
->nStyles
)
1134 return &highlightData
->styleTable
[hCode
];
1138 ** Functions to return style information from the highlighting style table.
1141 char *HighlightNameOfCode(WindowInfo
*window
, int hCode
)
1143 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1144 return entry
? entry
->highlightName
: "";
1147 char *HighlightStyleOfCode(WindowInfo
*window
, int hCode
)
1149 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1150 return entry
? entry
->styleName
: "";
1153 char *HighlightColorOfCode(WindowInfo
*window
, int hCode
)
1155 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1156 return entry
? entry
->colorName
: "";
1159 char *HighlightBackgroundColorOfCode(WindowInfo
*window
, int hCode
)
1161 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1162 return entry
&& entry
->bgColorName
? entry
->bgColorName
: "";
1165 Pixel
HighlightColorValueOfCode(WindowInfo
*window
, int hCode
,
1166 int *r
, int *g
, int *b
)
1168 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1173 return entry
->color
;
1177 /* pick up foreground color of the (first) text widget of the window */
1180 Display
*display
= XtDisplay(window
->textArea
);
1182 XtVaGetValues(window
->textArea
,
1184 XtNforeground
, &colorDef
.pixel
,
1186 if (XQueryColor(display
, cMap
, &colorDef
)) {
1188 *g
= colorDef
.green
;
1191 return colorDef
.pixel
;
1195 Pixel
GetHighlightBGColorOfCode(WindowInfo
*window
, int hCode
,
1196 int *r
, int *g
, int *b
)
1198 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1199 if (entry
&& entry
->bgColorName
) {
1201 *g
= entry
->bgGreen
;
1203 return entry
->bgColor
;
1207 /* pick up background color of the (first) text widget of the window */
1210 Display
*display
= XtDisplay(window
->textArea
);
1212 XtVaGetValues(window
->textArea
,
1214 XtNbackground
, &colorDef
.pixel
,
1216 if (XQueryColor(display
, cMap
, &colorDef
)) {
1218 *g
= colorDef
.green
;
1221 return colorDef
.pixel
;
1225 int HighlightCodeIsBold(WindowInfo
*window
, int hCode
)
1227 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1228 return entry
? entry
->isBold
: 0;
1231 int HighlightCodeIsItalic(WindowInfo
*window
, int hCode
)
1233 styleTableEntry
*entry
= styleTableEntryOfCode(window
, hCode
);
1234 return entry
? entry
->isItalic
: 0;
1238 ** Callback to parse an "unfinished" region of the buffer. "unfinished" means
1239 ** that the buffer has been parsed with pass 1 patterns, but this section has
1240 ** not yet been exposed, and thus never had pass 2 patterns applied. This
1241 ** callback is invoked when the text widget's display routines encounter one
1242 ** of these unfinished regions. "pos" is the first position encountered which
1243 ** needs re-parsing. This routine applies pass 2 patterns to a chunk of
1244 ** the buffer of size PASS_2_REPARSE_CHUNK_SIZE beyond pos.
1246 static void handleUnparsedRegion(WindowInfo
*window
, textBuffer
*styleBuf
,
1249 textBuffer
*buf
= window
->buffer
;
1250 int beginParse
, endParse
, beginSafety
, endSafety
, p
;
1251 windowHighlightData
*highlightData
=
1252 (windowHighlightData
*)window
->highlightData
;
1254 reparseContext
*context
= &highlightData
->contextRequirements
;
1255 highlightDataRec
*pass2Patterns
= highlightData
->pass2Patterns
;
1256 char *string
, *styleString
, *stringPtr
, *stylePtr
, c
, prevChar
;
1257 int firstPass2Style
= (unsigned char)pass2Patterns
[1].style
;
1259 /* If there are no pass 2 patterns to process, do nothing (but this
1260 should never be triggered) */
1261 if (pass2Patterns
== NULL
)
1264 /* Find the point at which to begin parsing to ensure that the character at
1265 pos is parsed correctly (beginSafety), at most one context distance back
1266 from pos, unless there is a pass 1 section from which to start */
1268 beginSafety
= backwardOneContext(buf
, context
, beginParse
);
1269 for (p
=beginParse
; p
>=beginSafety
; p
--) {
1270 c
= BufGetCharacter(styleBuf
, p
);
1271 if (c
!= UNFINISHED_STYLE
&& c
!= PLAIN_STYLE
&&
1272 (unsigned char)c
< firstPass2Style
) {
1273 beginSafety
= p
+ 1;
1278 /* Decide where to stop (endParse), and the extra distance (endSafety)
1279 necessary to ensure that the changes at endParse are correct. Stop at
1280 the end of the unfinished region, or a max. of PASS_2_REPARSE_CHUNK_SIZE
1281 characters forward from the requested position */
1282 endParse
= min(buf
->length
, pos
+ PASS_2_REPARSE_CHUNK_SIZE
);
1283 endSafety
= forwardOneContext(buf
, context
, endParse
);
1284 for (p
=pos
; p
<endSafety
; p
++) {
1285 c
= BufGetCharacter(styleBuf
, p
);
1286 if (c
!= UNFINISHED_STYLE
&& c
!= PLAIN_STYLE
&&
1287 (unsigned char)c
< firstPass2Style
) {
1288 endParse
= min(endParse
, p
);
1291 } else if (c
!= UNFINISHED_STYLE
&& p
< endParse
) {
1293 if ((unsigned char)c
< firstPass2Style
)
1296 endSafety
= forwardOneContext(buf
, context
, endParse
);
1301 /* Copy the buffer range into a string */
1302 /* printf("callback pass2 parsing from %d thru %d w/ safety from %d thru %d\n",
1303 beginParse, endParse, beginSafety, endSafety); */
1304 string
= stringPtr
= BufGetRange(buf
, beginSafety
, endSafety
);
1305 styleString
= stylePtr
= BufGetRange(styleBuf
, beginSafety
, endSafety
);
1307 /* Parse it with pass 2 patterns */
1308 prevChar
= getPrevChar(buf
, beginSafety
);
1309 parseString(pass2Patterns
, &stringPtr
, &stylePtr
, endParse
- beginSafety
,
1310 &prevChar
, False
, GetWindowDelimiters(window
), string
);
1312 /* Update the style buffer the new style information, but only between
1313 beginParse and endParse. Skip the safety region */
1314 styleString
[endParse
-beginSafety
] = '\0';
1315 BufReplace(styleBuf
, beginParse
, endParse
,
1316 &styleString
[beginParse
-beginSafety
]);
1317 XtFree(styleString
);
1322 ** Callback wrapper around the above function.
1324 static void handleUnparsedRegionCB(textDisp
*textD
, int pos
, void *cbArg
)
1326 handleUnparsedRegion((WindowInfo
*)cbArg
, textD
->styleBuffer
, pos
);
1330 ** Re-parse the smallest region possible around a modification to buffer "buf"
1331 ** to gurantee that the promised context lines and characters have
1332 ** been presented to the patterns. Changes the style buffer in "highlightData"
1333 ** with the parsing result.
1335 static void incrementalReparse(windowHighlightData
*highlightData
,
1336 textBuffer
*buf
, int pos
, int nInserted
, const char *delimiters
)
1338 int beginParse
, endParse
, endAt
, lastMod
, parseInStyle
, nPasses
;
1339 textBuffer
*styleBuf
= highlightData
->styleBuffer
;
1340 highlightDataRec
*pass1Patterns
= highlightData
->pass1Patterns
;
1341 highlightDataRec
*pass2Patterns
= highlightData
->pass2Patterns
;
1342 highlightDataRec
*startPattern
;
1343 reparseContext
*context
= &highlightData
->contextRequirements
;
1344 char *parentStyles
= highlightData
->parentStyles
;
1346 /* Find the position "beginParse" at which to begin reparsing. This is
1347 far enough back in the buffer such that the guranteed number of
1348 lines and characters of context are examined. */
1350 parseInStyle
= findSafeParseRestartPos(buf
, highlightData
, &beginParse
);
1352 /* Find the position "endParse" at which point it is safe to stop
1353 parsing, unless styles are getting changed beyond the last
1355 lastMod
= pos
+ nInserted
;
1356 endParse
= forwardOneContext(buf
, context
, lastMod
);
1359 ** Parse the buffer from beginParse, until styles compare
1360 ** with originals for one full context distance. Distance increases
1361 ** by powers of two until nothing changes from previous step. If
1362 ** parsing ends before endParse, start again one level up in the
1363 ** pattern hierarchy
1365 for (nPasses
=0; ; nPasses
++) {
1367 /* Parse forward from beginParse to one context beyond the end
1368 of the last modification */
1369 startPattern
= patternOfStyle(pass1Patterns
, parseInStyle
);
1370 /* If there is no pattern matching the style, it must be a pass-2
1371 style. It that case, it is (probably) safe to start parsing with
1372 the root pass-1 pattern again. Anyway, passing a NULL-pointer to
1373 the parse routine would result in a crash; restarting with pass-1
1374 patterns is certainly preferable, even if there is a slight chance
1375 of a faulty coloring. */
1376 if (!startPattern
) {
1377 startPattern
= pass1Patterns
;
1379 endAt
= parseBufferRange(startPattern
,
1380 pass2Patterns
, buf
, styleBuf
, context
, beginParse
, endParse
,
1383 /* If parse completed at this level, move one style up in the
1384 hierarchy and start again from where the previous parse left off. */
1385 if (endAt
< endParse
) {
1387 endParse
= forwardOneContext(buf
, context
,
1388 max(endAt
, max(lastModified(styleBuf
), lastMod
)));
1389 if (IS_PLAIN(parseInStyle
)) {
1391 "NEdit internal error: incr. reparse fell short\n");
1394 parseInStyle
= parentStyleOf(parentStyles
, parseInStyle
);
1396 /* One context distance beyond last style changed means we're done */
1397 } else if (lastModified(styleBuf
) <= lastMod
) {
1400 /* Styles are changing beyond the modification, continue extending
1401 the end of the parse range by powers of 2 * REPARSE_CHUNK_SIZE and
1402 reparse until nothing changes */
1404 lastMod
= lastModified(styleBuf
);
1405 endParse
= min(buf
->length
, forwardOneContext(buf
, context
, lastMod
)
1406 + (REPARSE_CHUNK_SIZE
<< nPasses
));
1412 ** Parse text in buffer "buf" between positions "beginParse" and "endParse"
1413 ** using pass 1 patterns over the entire range and pass 2 patterns where needed
1414 ** to determine whether re-parsed areas have changed and need to be redrawn.
1415 ** Deposits style information in "styleBuf" and expands the selection in
1416 ** styleBuf to show the additional areas which have changed and need
1417 ** redrawing. beginParse must be a position from which pass 1 parsing may
1418 ** safely be started using the pass1Patterns given. Internally, adds a
1419 ** "takeoff" safety region before beginParse, so that pass 2 patterns will be
1420 ** allowed to match properly if they begin before beginParse, and a "landing"
1421 ** safety region beyond endparse so that endParse is guranteed to be parsed
1422 ** correctly in both passes. Returns the buffer position at which parsing
1423 ** finished (this will normally be endParse, unless the pass1Patterns is a
1424 ** pattern which does end and the end is reached).
1426 static int parseBufferRange(highlightDataRec
*pass1Patterns
,
1427 highlightDataRec
*pass2Patterns
, textBuffer
*buf
, textBuffer
*styleBuf
,
1428 reparseContext
*contextRequirements
, int beginParse
, int endParse
,
1429 const char *delimiters
)
1431 char *string
, *styleString
, *stringPtr
, *stylePtr
, *temp
, prevChar
;
1432 int endSafety
, endPass2Safety
, startPass2Safety
, tempLen
;
1433 int modStart
, modEnd
, beginSafety
, beginStyle
, p
, style
;
1434 int firstPass2Style
= pass2Patterns
== NULL
? INT_MAX
:
1435 (unsigned char)pass2Patterns
[1].style
;
1437 /* Begin parsing one context distance back (or to the last style change) */
1438 beginStyle
= pass1Patterns
->style
;
1439 if (CAN_CROSS_LINE_BOUNDARIES(contextRequirements
)) {
1440 beginSafety
= backwardOneContext(buf
, contextRequirements
, beginParse
);
1441 for (p
=beginParse
; p
>=beginSafety
; p
--) {
1442 style
= BufGetCharacter(styleBuf
, p
-1);
1443 if (!EQUIVALENT_STYLE(style
, beginStyle
, firstPass2Style
)) {
1449 for (beginSafety
=max(0,beginParse
-1); beginSafety
>0; beginSafety
--) {
1450 style
= BufGetCharacter(styleBuf
, beginSafety
);
1451 if (!EQUIVALENT_STYLE(style
, beginStyle
, firstPass2Style
) ||
1452 BufGetCharacter(buf
, beginSafety
) == '\n') {
1459 /* Parse one parse context beyond requested end to gurantee that parsing
1460 at endParse is complete, unless patterns can't cross line boundaries,
1461 in which case the end of the line is fine */
1464 if (CAN_CROSS_LINE_BOUNDARIES(contextRequirements
))
1465 endSafety
= forwardOneContext(buf
, contextRequirements
, endParse
);
1466 else if (endParse
>=buf
->length
|| (BufGetCharacter(buf
,endParse
-1)=='\n'))
1467 endSafety
= endParse
;
1469 endSafety
= min(buf
->length
, BufEndOfLine(buf
, endParse
) + 1);
1471 /* copy the buffer range into a string */
1472 string
= BufGetRange(buf
, beginSafety
, endSafety
);
1473 styleString
= BufGetRange(styleBuf
, beginSafety
, endSafety
);
1475 /* Parse it with pass 1 patterns */
1476 /* printf("parsing from %d thru %d\n", beginSafety, endSafety); */
1477 prevChar
= getPrevChar(buf
, beginParse
);
1478 stringPtr
= &string
[beginParse
-beginSafety
];
1479 stylePtr
= &styleString
[beginParse
-beginSafety
];
1480 parseString(pass1Patterns
, &stringPtr
, &stylePtr
, endParse
-beginParse
,
1481 &prevChar
, False
, delimiters
, string
);
1483 /* On non top-level patterns, parsing can end early */
1484 endParse
= min(endParse
, stringPtr
-string
+ beginSafety
);
1486 /* If there are no pass 2 patterns, we're done */
1487 if (pass2Patterns
== NULL
)
1490 /* Parsing of pass 2 patterns is done only as necessary for determining
1491 where styles have changed. Find the area to avoid, which is already
1492 marked as changed (all inserted text and previously modified areas) */
1493 if (styleBuf
->primary
.selected
) {
1494 modStart
= styleBuf
->primary
.start
;
1495 modEnd
= styleBuf
->primary
.end
;
1497 modStart
= modEnd
= 0;
1499 /* Re-parse the areas before the modification with pass 2 patterns, from
1500 beginSafety to far enough beyond modStart to gurantee that parsing at
1501 modStart is correct (pass 2 patterns must match entirely within one
1502 context distance, and only on the top level). If the parse region
1503 ends entirely before the modification or at or beyond modEnd, parse
1504 the whole thing and take advantage of the safety region which will be
1505 thrown away below. Otherwise save the contents of the safety region
1506 temporarily, and restore it after the parse. */
1507 if (beginSafety
< modStart
) {
1508 if (endSafety
> modStart
) {
1509 endPass2Safety
= forwardOneContext(buf
, contextRequirements
,
1511 if (endPass2Safety
+ PASS_2_REPARSE_CHUNK_SIZE
>= modEnd
)
1512 endPass2Safety
= endSafety
;
1514 endPass2Safety
= endSafety
;
1515 prevChar
= getPrevChar(buf
, beginSafety
);
1516 if (endPass2Safety
== endSafety
) {
1517 passTwoParseString(pass2Patterns
, string
, styleString
,
1518 endParse
- beginSafety
, &prevChar
, False
, delimiters
, string
);
1521 tempLen
= endPass2Safety
- modStart
;
1522 temp
= XtMalloc(tempLen
);
1523 strncpy(temp
, &styleString
[modStart
-beginSafety
], tempLen
);
1524 passTwoParseString(pass2Patterns
, string
, styleString
,
1525 modStart
- beginSafety
, &prevChar
, False
, delimiters
, string
);
1526 strncpy(&styleString
[modStart
-beginSafety
], temp
, tempLen
);
1531 /* Re-parse the areas after the modification with pass 2 patterns, from
1532 modEnd to endSafety, with an additional safety region before modEnd
1533 to ensure that parsing at modEnd is correct. */
1534 if (endParse
> modEnd
) {
1535 if (beginSafety
> modEnd
) {
1536 prevChar
= getPrevChar(buf
, beginSafety
);
1537 passTwoParseString(pass2Patterns
, string
, styleString
,
1538 endParse
- beginSafety
, &prevChar
, False
, delimiters
, string
);
1540 startPass2Safety
= max(beginSafety
,
1541 backwardOneContext(buf
, contextRequirements
, modEnd
));
1542 tempLen
= modEnd
- startPass2Safety
;
1543 temp
= XtMalloc(tempLen
);
1544 strncpy(temp
, &styleString
[startPass2Safety
-beginSafety
], tempLen
);
1545 prevChar
= getPrevChar(buf
, startPass2Safety
);
1546 passTwoParseString(pass2Patterns
,
1547 &string
[startPass2Safety
-beginSafety
],
1548 &styleString
[startPass2Safety
-beginSafety
],
1549 endParse
-startPass2Safety
, &prevChar
, False
, delimiters
, string
);
1550 strncpy(&styleString
[startPass2Safety
-beginSafety
], temp
, tempLen
);
1557 /* Update the style buffer with the new style information, but only
1558 through endParse. Skip the safety region at the end */
1559 styleString
[endParse
-beginSafety
] = '\0';
1560 modifyStyleBuf(styleBuf
, &styleString
[beginParse
-beginSafety
],
1561 beginParse
, endParse
, firstPass2Style
);
1562 XtFree(styleString
);
1569 ** Parses "string" according to compiled regular expressions in "pattern"
1570 ** until endRE is or errorRE are matched, or end of string is reached.
1571 ** Advances "string", "styleString" pointers to the next character past
1572 ** the end of the parsed section, and updates "prevChar" to reflect
1573 ** the new character before "string".
1574 ** If "anchored" is true, just scan the sub-pattern starting at the beginning
1575 ** of the string. "length" is how much of the string must be parsed, but
1576 ** "string" must still be null terminated, the termination indicating how
1577 ** far the string should be searched, and "length" the part which is actually
1578 ** required (the string may or may not be parsed beyond "length").
1580 ** Returns True if parsing was done and the parse succeeded. Returns False if
1581 ** the error pattern matched, if the end of the string was reached without
1582 ** matching the end expression, or in the unlikely event of an internal error.
1584 static int parseString(highlightDataRec
*pattern
, char **string
,
1585 char **styleString
, int length
, char *prevChar
, int anchored
,
1586 const char *delimiters
, const char* lookBehindTo
)
1588 int i
, subExecuted
, subIndex
;
1589 char *stringPtr
, *stylePtr
, *startingStringPtr
, *savedStartPtr
;
1590 signed char *subExpr
;
1592 highlightDataRec
*subPat
= NULL
, *subSubPat
;
1597 stringPtr
= *string
;
1598 stylePtr
= *styleString
;
1600 while(ExecRE(pattern
->subPatternRE
, NULL
, stringPtr
, anchored
? *string
+1 :
1601 *string
+length
+1, False
, *prevChar
, '\0', delimiters
, lookBehindTo
)) {
1602 /* Beware of the case where only one real branch exists, but that
1603 branch has sub-branches itself. In that case the top_branch refers
1604 to the matching sub-branch and must be ignored. */
1605 subIndex
= (pattern
->nSubBranches
> 1) ?
1606 pattern
->subPatternRE
->top_branch
: 0;
1607 /* Combination of all sub-patterns and end pattern matched */
1608 /* printf("combined patterns RE matched at %d\n",
1609 pattern->subPatternRE->startp[0] - *string); */
1610 startingStringPtr
= stringPtr
;
1612 /* Fill in the pattern style for the text that was skipped over before
1613 the match, and advance the pointers to the start of the pattern */
1614 fillStyleString(&stringPtr
, &stylePtr
, pattern
->subPatternRE
->startp
[0],
1615 pattern
->style
, prevChar
);
1617 /* If the combined pattern matched this pattern's end pattern, we're
1618 done. Fill in the style string, update the pointers, color the
1619 end expression if there were coloring sub-patterns, and return */
1620 savedStartPtr
= stringPtr
;
1621 savedPrevChar
= *prevChar
;
1622 if (pattern
->endRE
!= NULL
) {
1623 if (subIndex
== 0) {
1624 fillStyleString(&stringPtr
, &stylePtr
,
1625 pattern
->subPatternRE
->endp
[0], pattern
->style
, prevChar
);
1626 subExecuted
= False
;
1627 for (i
=0;i
<pattern
->nSubPatterns
; i
++) {
1628 subPat
= pattern
->subPatterns
[i
];
1629 if (subPat
->colorOnly
) {
1631 if (!ExecRE(pattern
->endRE
, NULL
, savedStartPtr
,
1632 savedStartPtr
+1, False
, savedPrevChar
,
1633 '\0', delimiters
, lookBehindTo
)) {
1634 fprintf(stderr
, "Internal error, failed to "
1635 "recover end match in parseString\n");
1640 for (subExpr
=subPat
->endSubexprs
; *subExpr
!=-1; subExpr
++)
1641 recolorSubexpr(pattern
->endRE
, *subExpr
,
1642 subPat
->style
, *string
, *styleString
);
1645 *string
= stringPtr
;
1646 *styleString
= stylePtr
;
1652 /* If the combined pattern matched this pattern's error pattern, we're
1653 done. Fill in the style string, update the pointers, and return */
1654 if (pattern
->errorRE
!= NULL
) {
1655 if (subIndex
== 0) {
1656 fillStyleString(&stringPtr
, &stylePtr
,
1657 pattern
->subPatternRE
->startp
[0], pattern
->style
, prevChar
);
1658 *string
= stringPtr
;
1659 *styleString
= stylePtr
;
1665 /* Figure out which sub-pattern matched */
1666 for (i
=0; i
<pattern
->nSubPatterns
; i
++) {
1667 subPat
= pattern
->subPatterns
[i
];
1668 if (subPat
->colorOnly
) ++subIndex
;
1669 else if (i
== subIndex
) break;
1671 if (i
== pattern
->nSubPatterns
) {
1672 fprintf(stderr
, "Internal error, failed to match in parseString\n");
1676 /* the sub-pattern is a simple match, just color it */
1677 if (subPat
->subPatternRE
== NULL
) {
1678 fillStyleString(&stringPtr
, &stylePtr
, pattern
->subPatternRE
->endp
[0], /* subPat->startRE->endp[0],*/
1679 subPat
->style
, prevChar
);
1681 /* Parse the remainder of the sub-pattern */
1684 /* If parsing should start after the start pattern, advance
1686 if (!(subPat
->flags
& PARSE_SUBPATS_FROM_START
))
1687 fillStyleString(&stringPtr
, &stylePtr
, pattern
->subPatternRE
->endp
[0], /* subPat->startRE->endp[0],*/
1688 subPat
->style
, prevChar
);
1690 /* Parse to the end of the subPattern */
1691 parseString(subPat
, &stringPtr
, &stylePtr
, length
-
1692 (stringPtr
- *string
), prevChar
, False
, delimiters
, lookBehindTo
);
1695 /* If the sub-pattern has color-only sub-sub-patterns, add color
1696 based on the coloring sub-expression references */
1697 subExecuted
= False
;
1698 for (i
=0; i
<subPat
->nSubPatterns
; i
++) {
1699 subSubPat
= subPat
->subPatterns
[i
];
1700 if (subSubPat
->colorOnly
) {
1702 if (!ExecRE(subPat
->startRE
, NULL
, savedStartPtr
,
1703 savedStartPtr
+1, False
, savedPrevChar
, '\0',
1704 delimiters
, lookBehindTo
)) {
1705 fprintf(stderr
, "Internal error, failed to recover "
1706 "start match in parseString\n");
1711 for (subExpr
=subSubPat
->startSubexprs
; *subExpr
!=-1; subExpr
++)
1712 recolorSubexpr(subPat
->startRE
, *subExpr
, subSubPat
->style
,
1713 *string
, *styleString
);
1717 /* Make sure parsing progresses. If patterns match the empty string,
1718 they can get stuck and hang the process */
1719 if (stringPtr
== startingStringPtr
) {
1720 /* Avoid stepping over the end of the string (possible for
1721 zero-length matches at end of the string) */
1722 if (*stringPtr
== '\0')
1724 fillStyleString(&stringPtr
, &stylePtr
, stringPtr
+1,
1725 pattern
->style
, prevChar
);
1729 /* If this is an anchored match (must match on first character), and
1730 nothing matched, return False */
1731 if (anchored
&& stringPtr
== *string
)
1734 /* Reached end of string, fill in the remaining text with pattern style
1735 (unless this was an anchored match) */
1737 fillStyleString(&stringPtr
, &stylePtr
, *string
+length
, pattern
->style
,
1740 /* Advance the string and style pointers to the end of the parsed text */
1741 *string
= stringPtr
;
1742 *styleString
= stylePtr
;
1743 return pattern
->endRE
== NULL
;
1747 ** Takes a string which has already been parsed through pass1 parsing and
1748 ** re-parses the areas where pass two patterns are applicable. Parameters
1749 ** have the same meaning as in parseString, except that strings aren't doubly
1750 ** indirect and string pointers are not updated.
1752 static void passTwoParseString(highlightDataRec
*pattern
, char *string
,
1753 char *styleString
, int length
, char *prevChar
, int anchored
,
1754 const char *delimiters
, const char *lookBehindTo
)
1756 int inParseRegion
= False
;
1757 char *stylePtr
, *stringPtr
, temp
, *parseStart
= NULL
, *parseEnd
, *s
, *c
;
1759 int firstPass2Style
= (unsigned char)pattern
[1].style
;
1761 for (c
= string
, s
= styleString
; ; c
++, s
++) {
1762 if (!inParseRegion
&& *c
!= '\0' && (*s
== UNFINISHED_STYLE
||
1763 *s
== PLAIN_STYLE
|| (unsigned char)*s
>= firstPass2Style
)) {
1765 inParseRegion
= True
;
1767 if (inParseRegion
&& (*c
== '\0' || !(*s
== UNFINISHED_STYLE
||
1768 *s
== PLAIN_STYLE
|| (unsigned char)*s
>= firstPass2Style
))) {
1770 if (parseStart
!= string
)
1771 *prevChar
= *(parseStart
-1);
1772 stringPtr
= parseStart
;
1773 stylePtr
= &styleString
[parseStart
- string
];
1776 /* printf("pass2 parsing %d chars\n", strlen(stringPtr)); */
1777 parseString(pattern
, &stringPtr
, &stylePtr
,
1778 min(parseEnd
- parseStart
, length
- (parseStart
- string
)),
1779 prevChar
, False
, delimiters
, lookBehindTo
);
1781 inParseRegion
= False
;
1783 if (*c
== '\0' || (!inParseRegion
&& c
- string
>= length
))
1789 ** Advance "stringPtr" and "stylePtr" until "stringPtr" == "toPtr", filling
1790 ** "stylePtr" with style "style". Can also optionally update the pre-string
1791 ** character, prevChar, which is fed to regular the expression matching
1792 ** routines for determining word and line boundaries at the start of the string.
1794 static void fillStyleString(char **stringPtr
, char **stylePtr
, char *toPtr
,
1795 char style
, char *prevChar
)
1797 int i
, len
= toPtr
-*stringPtr
;
1799 if (*stringPtr
>= toPtr
)
1802 for (i
=0; i
<len
; i
++)
1803 *(*stylePtr
)++ = style
;
1804 if (prevChar
!= NULL
) *prevChar
= *(toPtr
-1);
1809 ** Incorporate changes from styleString into styleBuf, tracking changes
1810 ** in need of redisplay, and marking them for redisplay by the text
1811 ** modification callback in textDisp.c. "firstPass2Style" is necessary
1812 ** for distinguishing pass 2 styles which compare as equal to the unfinished
1813 ** style in the original buffer, from pass1 styles which signal a change.
1815 static void modifyStyleBuf(textBuffer
*styleBuf
, char *styleString
,
1816 int startPos
, int endPos
, int firstPass2Style
)
1819 int pos
, modStart
, modEnd
, minPos
= INT_MAX
, maxPos
= 0;
1820 selection
*sel
= &styleBuf
->primary
;
1822 /* Skip the range already marked for redraw */
1823 if (sel
->selected
) {
1824 modStart
= sel
->start
;
1827 modStart
= modEnd
= startPos
;
1829 /* Compare the original style buffer (outside of the modified range) with
1830 the new string with which it will be updated, to find the extent of
1831 the modifications. Unfinished styles in the original match any
1833 for (c
=styleString
, pos
=startPos
; pos
<modStart
&& pos
<endPos
; c
++, pos
++) {
1834 bufChar
= BufGetCharacter(styleBuf
, pos
);
1835 if (*c
!= bufChar
&& !(bufChar
== UNFINISHED_STYLE
&&
1836 (*c
== PLAIN_STYLE
|| (unsigned char)*c
>= firstPass2Style
))) {
1837 if (pos
< minPos
) minPos
= pos
;
1838 if (pos
> maxPos
) maxPos
= pos
;
1841 for (c
=&styleString
[max(0, modEnd
-startPos
)], pos
=max(modEnd
, startPos
);
1842 pos
<endPos
; c
++, pos
++) {
1843 bufChar
= BufGetCharacter(styleBuf
, pos
);
1844 if (*c
!= bufChar
&& !(bufChar
== UNFINISHED_STYLE
&&
1845 (*c
== PLAIN_STYLE
|| (unsigned char)*c
>= firstPass2Style
))) {
1846 if (pos
< minPos
) minPos
= pos
;
1847 if (pos
+1 > maxPos
) maxPos
= pos
+1;
1851 /* Make the modification */
1852 BufReplace(styleBuf
, startPos
, endPos
, styleString
);
1854 /* Mark or extend the range that needs to be redrawn. Even if no
1855 change was made, it's important to re-establish the selection,
1856 because it can get damaged by the BufReplace above */
1857 BufSelect(styleBuf
, min(modStart
, minPos
), max(modEnd
, maxPos
));
1861 ** Return the last modified position in styleBuf (as marked by modifyStyleBuf
1862 ** by the convention used for conveying modification information to the
1863 ** text widget, which is selecting the text)
1865 static int lastModified(textBuffer
*styleBuf
)
1867 if (styleBuf
->primary
.selected
)
1868 return max(0, styleBuf
->primary
.end
);
1873 ** Compute the distance between two colors.
1876 static double colorDistance(const XColor
*c1
, const XColor
*c2
)
1878 /* This is done in RGB space, which is close, but not optimal. It's
1879 probably better to do it in HSV or YIQ space, however, that means
1880 a whole lot of extra conversions. This would allow us to weight
1881 the coordinates differently, e.g, prefer to match hue over
1884 static const double scale
= 65535;
1886 double tred
= c1
->red
/ scale
- c2
->red
/ scale
;
1887 double tgreen
= c1
->green
/ scale
- c2
->green
/ scale
;
1888 double tblue
= c1
->blue
/ scale
- c2
->blue
/ scale
;
1890 /* use square Euclidian distance */
1891 return tred
* tred
+ tgreen
* tgreen
+ tblue
* tblue
;
1895 ** Allocate a read-only (shareable) colormap cell for a named color, from the
1896 ** the default colormap of the screen on which the widget (w) is displayed. If
1897 ** the colormap is full and there's no suitable substitute, print an error on
1898 ** stderr, and return the widget's foreground color as a backup.
1901 Pixel
AllocColor(Widget w
, const char *colorName
, int *r
, int *g
, int *b
)
1904 XColor
*allColorDefs
;
1905 Display
*display
= XtDisplay(w
);
1907 Pixel foreground
, bestPixel
;
1908 double small
= 1.0e9
;
1910 unsigned int ncolors
;
1911 unsigned long i
, best
= 0; /* pixel value */
1913 /* Get the correct colormap for compatability with the "best" visual
1914 feature in 5.2. Default visual of screen is no good here. */
1919 XtNforeground
, &foreground
,
1922 bestPixel
= foreground
; /* Our last fallback */
1924 /* First, check for valid syntax */
1925 if (! XParseColor(display
, cMap
, colorName
, &colorDef
)) {
1926 fprintf(stderr
, "NEdit: Color name %s not in database\n", colorName
);
1927 colorDef
.pixel
= foreground
;
1928 if (XQueryColor(display
, cMap
, &colorDef
)) {
1930 *g
= colorDef
.green
;
1936 /* Attempt allocation of the exact color. */
1937 if (XAllocColor(display
, cMap
, &colorDef
)) {
1939 *g
= colorDef
.green
;
1941 return colorDef
.pixel
;
1944 /* ---------- Allocation failed, the colormap may be full. ---------- */
1947 printf("Couldn't allocate %d %d %d\n", colorDef
.red
, colorDef
.green
, colorDef
.blue
);
1950 /* We can't do the nearest-match on other than 8 bit visuals because
1951 it just takes too long. */
1953 if (depth
> 8) { /* Oh no! */
1954 colorDef
.pixel
= foreground
;
1955 if (XQueryColor(display
, cMap
, &colorDef
)) {
1957 *g
= colorDef
.green
;
1963 /* Get the entire colormap so we can find the closest one. */
1964 ncolors
= (1 << depth
);
1965 allColorDefs
= malloc(ncolors
* sizeof(XColor
));
1966 memset(allColorDefs
, 0, ncolors
* sizeof(XColor
));
1968 for (i
= 0; i
< ncolors
; i
++)
1969 allColorDefs
[i
].pixel
= i
;
1971 XQueryColors(display
, cMap
, allColorDefs
, ncolors
);
1973 /* Scan through each color, looking for the closest one. */
1974 for (i
= 0; i
< ncolors
; i
++)
1976 double dist
= colorDistance(&allColorDefs
[i
], &colorDef
);
1985 /* Legally try to acquire the shared color- we should loop through
1986 the shortest distances here. We could sort the map in order
1987 of decreasing distances and loop through it until one works. */
1989 if (XAllocColor(display
, cMap
, &allColorDefs
[best
]))
1990 bestPixel
= allColorDefs
[best
].pixel
;
1993 printf("Got %d %d %d, ", allColorDefs
[best
].red
,
1994 allColorDefs
[best
].green
,
1995 allColorDefs
[best
].blue
);
1996 printf("That's %f off\n", small
);
1999 *r
= allColorDefs
[best
].red
;
2000 *g
= allColorDefs
[best
].green
;
2001 *b
= allColorDefs
[best
].blue
;
2007 ** Get the character before position "pos" in buffer "buf"
2009 static char getPrevChar(textBuffer
*buf
, int pos
)
2011 return pos
== 0 ? '\0' : BufGetCharacter(buf
, pos
-1);
2015 ** compile a regular expression and present a user friendly dialog on failure.
2017 static regexp
*compileREAndWarn(Widget parent
, const char *re
)
2022 compiledRE
= CompileRE(re
, &compileMsg
, REDFLT_STANDARD
);
2023 if (compiledRE
== NULL
)
2025 char *boundedRe
= XtNewString(re
);
2026 size_t maxLength
= DF_MAX_MSG_LENGTH
- strlen(compileMsg
) - 60;
2028 /* Prevent buffer overflow in DialogF. If the re is too long,
2029 truncate it and append ... */
2030 if (strlen(boundedRe
) > maxLength
)
2032 strcpy(&boundedRe
[maxLength
-3], "...");
2035 DialogF(DF_WARN
, parent
, 1, "Error in Regex",
2036 "Error in syntax highlighting regular expression:\n%s\n%s",
2037 "Dismiss", boundedRe
, compileMsg
);
2044 static int parentStyleOf(const char *parentStyles
, int style
)
2046 return parentStyles
[(unsigned char)style
-UNFINISHED_STYLE
];
2049 static int isParentStyle(const char *parentStyles
, int style1
, int style2
)
2053 for (p
= parentStyleOf(parentStyles
, style2
); p
!= '\0';
2054 p
= parentStyleOf(parentStyles
, p
))
2061 ** Discriminates patterns which can be used with parseString from those which
2062 ** can't. Leaf patterns are not suitable for parsing, because patterns
2063 ** contain the expressions used for parsing within the context of their own
2064 ** operation, i.e. the parent pattern initiates, and leaf patterns merely
2065 ** confirm and color. Returns TRUE if the pattern is suitable for parsing.
2067 static int patternIsParsable(highlightDataRec
*pattern
)
2069 return pattern
!= NULL
&& pattern
->subPatternRE
!= NULL
;
2073 ** Back up position pointed to by "pos" enough that parsing from that point
2074 ** on will satisfy context gurantees for pattern matching for modifications
2075 ** at pos. Returns the style with which to begin parsing. The caller is
2076 ** guranteed that parsing may safely BEGIN with that style, but not that it
2077 ** will continue at that level.
2079 ** This routine can be fooled if a continuous style run of more than one
2080 ** context distance in length is produced by multiple pattern matches which
2081 ** abut, rather than by a single continuous match. In this case the
2082 ** position returned by this routine may be a bad starting point which will
2083 ** result in an incorrect re-parse. However this will happen very rarely,
2084 ** and, if it does, is unlikely to result in incorrect highlighting.
2086 static int findSafeParseRestartPos(textBuffer
*buf
,
2087 windowHighlightData
*highlightData
, int *pos
)
2089 int style
, startStyle
, runningStyle
, checkBackTo
, safeParseStart
, i
;
2090 char *parentStyles
= highlightData
->parentStyles
;
2091 highlightDataRec
*pass1Patterns
= highlightData
->pass1Patterns
;
2092 reparseContext
*context
= &highlightData
->contextRequirements
;
2094 /* We must begin at least one context distance back from the change */
2095 *pos
= backwardOneContext(buf
, context
, *pos
);
2097 /* If the new position is outside of any styles or at the beginning of
2098 the buffer, this is a safe place to begin parsing, and we're done */
2101 startStyle
= BufGetCharacter(highlightData
->styleBuffer
, *pos
);
2102 if (IS_PLAIN(startStyle
))
2106 ** The new position is inside of a styled region, meaning, its pattern
2107 ** could potentially be affected by the modification.
2109 ** Follow the style back by enough context to ensure that if we don't find
2110 ** its beginning, at least we've found a safe place to begin parsing
2111 ** within the styled region.
2113 ** A safe starting position within a style is either at a style
2114 ** boundary, or far enough from the beginning and end of the style run
2115 ** to ensure that it's not within the start or end expression match
2116 ** (unfortunately, abutting styles can produce false runs so we're not
2117 ** really ensuring it, just making it likely).
2119 if (patternIsParsable(patternOfStyle(pass1Patterns
, startStyle
))) {
2120 safeParseStart
= backwardOneContext(buf
, context
, *pos
);
2121 checkBackTo
= backwardOneContext(buf
, context
, safeParseStart
);
2126 runningStyle
= startStyle
;
2127 for (i
= *pos
-1; ; i
--) {
2129 /* The start of the buffer is certainly a safe place to parse from */
2135 /* If the style is preceded by a parent style, it's safe to parse
2136 with the parent style, provided that the parent is parsable. */
2137 style
= BufGetCharacter(highlightData
->styleBuffer
, i
);
2138 if (isParentStyle(parentStyles
, style
, runningStyle
)) {
2139 if (patternIsParsable(patternOfStyle(pass1Patterns
, style
))) {
2143 /* The parent is not parsable, so well have to continue
2144 searching. The parent now becomes the running style. */
2145 runningStyle
= style
;
2149 /* If the style is preceded by a child style, it's safe to resume
2150 parsing with the running style, provided that the running
2151 style is parsable. */
2152 else if (isParentStyle(parentStyles
, runningStyle
, style
)) {
2153 if (patternIsParsable
2154 (patternOfStyle(pass1Patterns
, runningStyle
))) {
2156 return runningStyle
;
2158 /* Else: keep searching; it's no use switching to the child style
2159 because even the running one is not parsable. */
2162 /* If the style is preceded by a sibling style, it's safe to resume
2163 parsing with the common ancestor style, provided that the ancestor
2164 is parsable. Checking for siblings is very hard; checking whether
2165 the style has the same parent will probably catch 99% of the cases
2167 else if (runningStyle
!= style
&&
2168 isParentStyle(parentStyles
,
2169 parentStyleOf(parentStyles
, runningStyle
), style
)) {
2170 int parentStyle
= parentStyleOf(parentStyles
, runningStyle
);
2171 if (patternIsParsable(patternOfStyle(pass1Patterns
, parentStyle
))) {
2175 /* Switch to the new style */
2176 runningStyle
= style
;
2180 /* If the style is preceded by an unrelated style, it's safe to
2181 resume parsing with PLAIN_STYLE. (Actually, it isn't, because
2182 we didn't really check for all possible sibling relations; but
2183 it will be ok in practice.) */
2184 else if (runningStyle
!= style
) {
2189 /* If the style is parsable and didn't change for one whole context
2190 distance on either side of safeParseStart, safeParseStart is a
2191 reasonable guess at a place to start parsing.
2192 Note: No 'else' here! We may come from one of the 'fall-through
2194 if (i
== checkBackTo
) {
2195 *pos
= safeParseStart
;
2197 /* We should never return a non-parsable style, because it will
2198 result in an internal error. If the current style is not
2199 parsable, the pattern set most probably contains a context
2200 distance violation. In that case we can only avoid internal
2201 errors (by climbing the pattern hierarchy till we find a
2202 parsable ancestor) and hope that the highlighting errors are
2204 while (!patternIsParsable
2205 (patternOfStyle(pass1Patterns
, runningStyle
)))
2206 runningStyle
= parentStyleOf(parentStyles
, runningStyle
);
2208 return runningStyle
;
2214 ** Return a position far enough back in "buf" from "fromPos" to give patterns
2215 ** their guranteed amount of context for matching (from "context"). If
2216 ** backing up by lines yields the greater distance, the returned position will
2217 ** be to the newline character before the start of the line, rather than to
2218 ** the first character of the line. (I did this because earlier prototypes of
2219 ** the syntax highlighting code, which were based on single-line context, used
2220 ** this to ensure that line-spanning expressions would be detected. I think
2221 ** it may reduce some 2 line context requirements to one line, at a cost of
2222 ** only one extra character, but I'm not sure, and my brain hurts from
2223 ** thinking about it).
2225 static int backwardOneContext(textBuffer
*buf
, reparseContext
*context
,
2228 if (context
->nLines
== 0)
2229 return max(0, fromPos
- context
->nChars
);
2230 else if (context
->nChars
== 0)
2232 BufCountBackwardNLines(buf
, fromPos
, context
->nLines
-1) - 1);
2234 return max(0, min(max(0, BufCountBackwardNLines(buf
, fromPos
,
2235 context
->nLines
-1) -1), fromPos
- context
->nChars
));
2239 ** Return a position far enough forward in "buf" from "fromPos" to ensure
2240 ** that patterns are given their required amount of context for matching
2241 ** (from "context"). If moving forward by lines yields the greater
2242 ** distance, the returned position will be the first character of of the
2243 ** next line, rather than the newline character at the end (see notes in
2244 ** backwardOneContext).
2246 static int forwardOneContext(textBuffer
*buf
, reparseContext
*context
,
2249 if (context
->nLines
== 0)
2250 return min(buf
->length
, fromPos
+ context
->nChars
);
2251 else if (context
->nChars
== 0)
2252 return min(buf
->length
,
2253 BufCountForwardNLines(buf
, fromPos
, context
->nLines
));
2255 return min(buf
->length
, max(BufCountForwardNLines(buf
, fromPos
,
2256 context
->nLines
), fromPos
+ context
->nChars
));
2260 ** Change styles in the portion of "styleString" to "style" where a particular
2261 ** sub-expression, "subExpr", of regular expression "re" applies to the
2262 ** corresponding portion of "string".
2264 static void recolorSubexpr(regexp
*re
, int subexpr
, int style
, char *string
,
2267 char *stringPtr
, *stylePtr
;
2269 stringPtr
= re
->startp
[subexpr
];
2270 stylePtr
= &styleString
[stringPtr
- string
];
2271 fillStyleString(&stringPtr
, &stylePtr
, re
->endp
[subexpr
], style
, NULL
);
2275 ** Search for a pattern in pattern list "patterns" with style "style"
2277 static highlightDataRec
*patternOfStyle(highlightDataRec
*patterns
, int style
)
2280 for (i
=0; patterns
[i
].style
!=0; i
++)
2281 if (patterns
[i
].style
== style
)
2282 return &patterns
[i
];
2283 if (style
== PLAIN_STYLE
|| style
== UNFINISHED_STYLE
)
2284 return &patterns
[0];
2288 static int max(int i1
, int i2
)
2290 return i1
>= i2
? i1
: i2
;
2293 static int min(int i1
, int i2
)
2295 return i1
<= i2
? i1
: i2
;
2298 static int indexOfNamedPattern(highlightPattern
*patList
, int nPats
,
2299 const char *patName
)
2303 if (patName
== NULL
)
2305 for (i
=0; i
<nPats
; i
++)
2306 if (!strcmp(patList
[i
].name
, patName
))
2311 static int findTopLevelParentIndex(highlightPattern
*patList
, int nPats
,
2317 while (patList
[topIndex
].subPatternOf
!= NULL
) {
2318 topIndex
= indexOfNamedPattern(patList
, nPats
,
2319 patList
[topIndex
].subPatternOf
);
2320 if (index
==topIndex
)
2321 return -1; /* amai: circular dependency ?! */
2327 ** Re-size (or re-height, anyhow) a window after adding or removing
2328 ** highlight fonts has changed the required vertical spacing (horizontal
2329 ** spacing is determined by the primary font, which doesn't change).
2331 ** Note that this messes up the window manager's height increment hint,
2332 ** which must be subsequently reset by UpdateWMSizeHints.
2334 static void updateWindowHeight(WindowInfo
*window
, int oldFontHeight
)
2336 int i
, borderHeight
, marginHeight
;
2337 Dimension windowHeight
, textAreaHeight
, textHeight
, newWindowHeight
;
2339 /* Decompose the window height into the part devoted to displaying
2340 text (textHeight) and the non-text part (boderHeight) */
2341 XtVaGetValues(window
->shell
, XmNheight
, &windowHeight
, NULL
);
2342 XtVaGetValues(window
->textArea
, XmNheight
, &textAreaHeight
,
2343 textNmarginHeight
, &marginHeight
, NULL
);
2344 textHeight
= textAreaHeight
- 2*marginHeight
;
2345 for (i
=0; i
<window
->nPanes
; i
++) {
2346 XtVaGetValues(window
->textPanes
[i
], XmNheight
, &textAreaHeight
, NULL
);
2347 textHeight
+= textAreaHeight
- 2*marginHeight
;
2349 borderHeight
= windowHeight
- textHeight
;
2351 /* Calculate a new window height appropriate for the new font */
2352 newWindowHeight
= (textHeight
*getFontHeight(window
)) / oldFontHeight
+
2355 /* Many window managers enforce window size increments even on client resize
2356 requests. Our height increment is probably wrong because it is still
2357 set for the previous font. Set the new height in advance, before
2358 attempting to resize. */
2359 XtVaSetValues(window
->shell
, XmNheightInc
, getFontHeight(window
), NULL
);
2361 /* Re-size the window */
2362 XtVaSetValues(window
->shell
, XmNheight
, newWindowHeight
, NULL
);
2366 ** Find the height currently being used to display text, which is
2367 ** a composite of all of the active highlighting fonts as determined by the
2368 ** text display component
2370 static int getFontHeight(WindowInfo
*window
)
2372 textDisp
*textD
= ((TextWidget
)window
->textArea
)->text
.textD
;
2374 return textD
->ascent
+ textD
->descent
;