UNIXworkcode

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