1 // Syntax highlighting functions
2 // Copyright (C) 2000 Core Technologies.
3
4 // This file is part of e93.
5 //
6 // e93 is free software; you can redistribute it and/or modify
7 // it under the terms of the e93 LICENSE AGREEMENT.
8 //
9 // e93 is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // e93 LICENSE AGREEMENT for more details.
13 //
14 // You should have received a copy of the e93 LICENSE AGREEMENT
15 // along with e93; see the file "LICENSE.TXT".
16
17 // Overview of how e93 performs syntax highlighting:
18 //
19 // Syntax highlighting is the process of looking at the text, searching
20 // for patterns, and highlighting those patterns by changing color,
21 // font, or style. Most editors which perform syntax highlighting have
22 // built-in rules for what to search for. By having built in rules,
23 // an editor can search out the syntax efficiently, and
24 // reduce the complexity of the process, but it limits the user from
25 // modifying rules or adding new ones. A problem occurs when one tries to
26 // generalize the rule definition process so that users can define their
27 // own -- things slow down.
28 //
29 // e93 has always been meant as a generalized text editor, not a 'C' editor.
30 // Because of this, syntax highlighting must be generalized so that
31 // users can create their own rules for whatever they might be editing.
32 //
33 // Syntax rules are given in the form of 1 or more regular expressions.
34 // The first regular expression is called the "start" expression, and the
35 // second (possibly empty) set of expressions are called the "end"
36 // expressions.
37
38 // If only a start expression is defined, e93 matches it, and applies
39 // a defined style to the text wherever it matches.
40 // If both start and end expressions are defined, e93 matches the
41 // start, then matches the nearest end. The start match has a style associated
42 // with it, the area between the start and end has a style associated
43 // with it, and the end expression has a style associated with it.
44 // So far, so good...
45 //
46 // Of course this method of highlighting has some limitations.
47 // For instance, one cannot define a set of keywords which are
48 // highlighted only inside parenthesis. To overcome this limitation,
49 // e93 allows for rules to be defined which are only active during
50 // the match of other rules (nested rules).
51 // So, you could define a rule with a start expression of
52 // open parenthesis, an end expression of close parenthesis. Then
53 // you could define a rule to match certain keywords, and tell e93
54 // that this rule only applies between the start and end parenthesis.
55 // This nesting of rules is allowed to occur to an arbitrary depth.
56
57 // Now, the only problem is getting e93 to do all of this without
58 // bringing the editor to a complete crawl on a 600MHz machine.
59 // Here's how that works:
60 // For every expression that is defined for a given syntax
61 // highlighting ruleset, e93 matches ALL occurrences of that expression
62 // and keeps them cached.
63 // When the text is changed, e93 uses information it has about the
64 // expression to limit how much searching it needs to perform to update
65 // the caches.
66 // The syntax highlighting rules use these expression-match caches
67 // to work out which rule is in effect, and when. These rules also
68 // cache their state, and attempt to update as little as possible with
69 // each change.
70
71 // Because of all the caching, and complexity caused by this solution,
72 // the number of data structures is getting out of hand.
73 // This might help clear things up:
74
75 // A SYNTAX_MAP is a data structure that contains all the information needed
76 // to look at the syntax of a document, and convert it to style information
77 // which will be applied to the document. It holds a set of compiled regular
78 // expressions, and a mapping which tells how these expressions are converted
79 // into style information.
80
81 // A SYNTAX_EXPRESSION data structure holds a compiled regular expression
82 // which is going to be matched against the text of the document.
83
84 // A SYNTAX_STYLE_MAPPING data structure describes the relationship between
85 // a set of matched regular expressions, and which styles should
86 // be applied to the text when they match. Each SYNTAX_STYLE_MAPPING points
87 // to a regular expression which needs to be matched at the start,
88 // and an optional regular expression which needs to be matched at the
89 // end. It then describes which styles should be applied to the text
90 // for the starting match, the text between the matches, and the text
91 // of the ending match.
92 // SYNTAX_STYLE_MAPPINGs also describe which nested SYNTAX_STYLE_MAPPINGs should
93 // be active between matches. This allows for recursive mappings (eg. inside
94 // a set of parenthesis, one could search for a set of keywords).
95
96 // A SYNTAX_STYLE_MAPPING_LIST structure is nothing more than a container for a list
97 // of SYNTAX_STYLE_MAPPINGs
98
99 // This is really not all that confusing, but the terminology could use
100 // some work.
101
102 #include "includes.h"
103
104 typedef struct expressionPiece
105 {
106 struct syntaxExpression
107 *parent; // pointer to the expression that this is a piece of
108 UINT32
109 pieceIndex; // index of this piece within the entire list of pieces used in the mapping (used to index array defined below)
110 bool
111 useRegisterMatch; // tells if entire expression matches, or a register matches
112 UINT32
113 registerIndex; // if useRegisterMatch is true, this gives the register index of the match
114 struct expressionPiece
115 *prev,
116 *next; // used to link the list of pieces together
117 } EXPRESSION_PIECE;
118
119 typedef struct expressionPieceList // used to create arbitrary lists of expression pieces
120 {
121 EXPRESSION_PIECE
122 *piece; // pointer to the piece given by this list entry
123 struct expressionPieceList
124 *prev,
125 *next; // used to link the list of pieces together
126 } EXPRESSION_PIECE_LIST;
127
128 typedef struct syntaxExpression // keeps named expression
129 {
130 struct syntaxMap
131 *parent; // pointer to the map this is part of
132 COMPILED_EXPRESSION
133 *compiledExpression; // compiled regular expression
134 struct syntaxExpression
135 *prev,
136 *next; // pointer to next expression
137 EXPRESSION_PIECE
138 *firstPiece; // pointer to list of pieces of this expression which are being mapped
139 char
140 expressionName[1]; // variable length array, name of this expression
141 } SYNTAX_EXPRESSION;
142
143 typedef struct syntaxStyleMapping // describes relationship between expressions and styles
144 {
145 struct syntaxMap
146 *parent; // pointer to the map this is part of
147 EXPRESSION_PIECE
148 *startExpressionPiece; // expression piece which starts this style
149 EXPRESSION_PIECE_LIST
150 *endExpressionPieces; // expression pieces which may end this style (NULL if style is begun and ended with startExpressionPiece)
151 UINT32
152 startStyle, // style index used when matching startExpressionPiece
153 betweenStyle, // style index to use between start and end expressions (used only if endExpressionPieces are defined)
154 endStyle; // style index used when matching one of the endExpressionPieces (used only if endExpressionPieces are defined)
155 struct syntaxStyleMappingList
156 *betweenList; // list of style maps which are active between the match of startExpressionPiece and the match of endExpressionPieces (used only if endExpressionPieces are defined)
157
158 struct syntaxStyleMapping // links together all the syntax style mappings for the given syntax map
159 *prev,
160 *next;
161 char
162 mappingName[1]; // variable length array, name of this mapping
163 } SYNTAX_STYLE_MAPPING;
164
165 typedef struct syntaxStyleMappingList // these are used to link together lists of pointers to SYNTAXSTYLEMAPs which are active at any given time
166 {
167 SYNTAX_STYLE_MAPPING
168 *mappingEntry; // pointer to map entry for this list element
169 struct syntaxStyleMappingList
170 *prev,
171 *next;
172 } SYNTAX_STYLE_MAPPING_LIST;
173
174 typedef struct syntaxMap
175 {
176 UINT32
177 numExpressionPieces; // number of pieces contained in the map
178 SYNTAX_EXPRESSION
179 *firstExpression; // pointer to head of list of expressions for this map
180 SYNTAX_STYLE_MAPPING
181 *firstStyleMapping; // pointer to the entire list of syntax style maps for this map
182 struct syntaxInstance
183 *firstInstance; // first instance of this map
184 SYNTAX_STYLE_MAPPING_LIST
185 *rootMapList; // list of syntax style maps which are active at the root
186 struct syntaxMap
187 *prev, // pointer to the previous syntax map in the global list
188 *next; // pointer to the next syntax map in the global list
189 char
190 mapName[1]; // variable length array, name of this map
191 } SYNTAX_MAP;
192
193
194 // Each editor universe which performs syntax highlighting, is assigned a
195 // SYNTAX_INSTANCE. The SYNTAX_INSTANCE is used to maintain syntax
196 // highlighting cache information which is specific to the editor
197 // universe.
198 // This information includes the cached array of which expressions
199 // matched where in the text, and the cached state of the syntax coloring
200 // state machine. This is kept separate from the SYNTAX_MAP, since that
201 // definition is used across all editor universes which require it.
202
203 typedef struct styleElement
204 {
205 SPARSE_ARRAY_HEADER
206 saHeader; // header needed for sparse array functions
207 UINT32
208 offset, // offset to start of piece within match
209 length; // number of characters that were matched by the piece
210 } EXPRESSION_MATCH_ELEMENT;
211
212 typedef struct expressionPieceInstance // used to keep track of matches to an expression for a given editor universe
213 {
214 EXPRESSION_PIECE
215 *expressionPiece;
216 UINT32
217 maximumStartDistance; // tells maximum distance from start we have ever witnessed, use to tell how far back searches for this expression must begin
218 SPARSE_ARRAY
219 matchArray;
220 } EXPRESSION_PIECE_INSTANCE;
221
222 // For the outermost level of syntax highlighting rules, there is a table which is maintained
223 // by the syntax highlighting state machine that tells which mapping is in effect
224 // for which section of text.
225 // This table is used to keep from having to search all the expression matches, and
226 // rebuild the entire style table with each change to the text.
227 // When a change to the text occurs, this table is consulted to determine the
228 // range of style changes which will be required to get back in sync with what
229 // existed before.
230 typedef struct mappingElement
231 {
232 SPARSE_ARRAY_HEADER
233 saHeader; // header needed for sparse array functions
234 SYNTAX_STYLE_MAPPING
235 *mapping; // pointer to the mapping which matches at the root level
236 } MAPPING_ELEMENT;
237
238 typedef struct syntaxInstance
239 {
240 SYNTAX_MAP
241 *parentMap; // map this instance is based on
242 EDITOR_BUFFER
243 *buffer; // pointer to the buffer that this instance belongs to
244 struct syntaxInstance
245 *prev, // doubly linked list of all instances on the parent map (so parent can traverse all instances linked to it)
246 *next;
247 SPARSE_ARRAY
248 mappingArray; // used to keep track of mappings at the root level
249 EXPRESSION_PIECE_INSTANCE
250 expressionPieces[1]; // variable length array of expression piece instance structures (one for each piece of an expression in the syntax map)
251 } SYNTAX_INSTANCE;
252
253
254 static SYNTAX_MAP
255 *syntaxMapHead, // head of global list of syntax maps
256 *syntaxMapTail;
257
258 enum
259 {
260 MAP_EXISTS,
261 EXPRESSION_EXISTS,
262 STYLE_MAPPING_EXISTS,
263 LIST_MAPPING_EXISTS,
264 EXPRESSION_PIECE_EXISTS
265 };
266
267 static const char *errorDescriptions[]=
268 {
269 "Syntax map already exists",
270 "Expression already exists",
271 "Style mapping already exists",
272 "List element already exists",
273 "Expression piece already exists",
274 };
275
276
DeleteOverlappingMatches(EXPRESSION_PIECE_INSTANCE * pieceInstance,UINT32 startOffset,UINT32 endOffset,UINT32 numBytes,UINT32 numBack)277 static bool DeleteOverlappingMatches(EXPRESSION_PIECE_INSTANCE *pieceInstance,UINT32 startOffset,UINT32 endOffset,UINT32 numBytes,UINT32 numBack)
278 // The text has changed between startOffset and endOffset for numBytes
279 // Call this to delete any matches that overlapped this area,
280 // and to adjust all those beyond this area
281 // numBack tells how far back we need to go when re-evaluating expressions
282 // this routine deletes all previous matches which begin that far back off the startOffset
283 {
284 DeleteSARange(&pieceInstance->matchArray,startOffset-numBack,endOffset-startOffset+numBack); // this cannot fail
285 return(InsertSARange(&pieceInstance->matchArray,startOffset-numBack,numBytes+numBack));
286 }
287
UpdateExpressionCaches(CHUNK_HEADER * chunk,UINT32 offset,SYNTAX_INSTANCE * instance,SYNTAX_EXPRESSION * expression,UINT32 startOffset,UINT32 endOffset,UINT32 numBytes,UINT32 totalBytes,UINT32 * earliestExpressonChange)288 static bool UpdateExpressionCaches(CHUNK_HEADER *chunk,UINT32 offset,SYNTAX_INSTANCE *instance,SYNTAX_EXPRESSION *expression,UINT32 startOffset,UINT32 endOffset,UINT32 numBytes,UINT32 totalBytes,UINT32 *earliestExpressonChange)
289 // text in textUniverse has changed between startOffset and endOffset for numBytes
290 // run through and search the text to update the cached set of matches
291 // NOTE: chunk could be NULL if there is no text in the universe
292 {
293 CHUNK_HEADER
294 *newChunk,
295 *outChunk;
296 UINT32
297 newOffset,
298 outOffset;
299 UINT32
300 numBack;
301 bool
302 fail;
303 bool
304 found;
305 UINT32
306 count;
307 UINT32
308 i;
309 UINT32
310 matchOffset;
311 EXPRESSION_MATCH_ELEMENT
312 newElement;
313 EXPRESSION_PIECE
314 *piece;
315
316 fail=false;
317 if(expression->firstPiece) // do not bother to do anything here if there are no pieces
318 {
319 REHuntBackToFarthestPossibleStart(chunk,offset,expression->compiledExpression,&numBack,&newChunk,&newOffset);
320
321 if(startOffset-numBack<*earliestExpressonChange)
322 {
323 *earliestExpressonChange=(startOffset-numBack);
324 }
325
326 piece=expression->firstPiece;
327 while(piece&&!fail)
328 {
329 fail=!DeleteOverlappingMatches(&instance->expressionPieces[piece->pieceIndex],startOffset,endOffset,numBytes,numBack); // adjust pieces for the change
330 piece=piece->next;
331 }
332
333 i=startOffset-numBack;
334 found=true;
335 while(!fail&&found&&(i<=startOffset+numBytes))
336 {
337 if(SearchForwardRE(newChunk,newOffset,expression->compiledExpression,i==0,true,startOffset+numBytes-i,totalBytes-i,false,&found,&matchOffset,&count,&newChunk,&newOffset,&outChunk,&outOffset,NULL))
338 {
339 if(found)
340 {
341 piece=expression->firstPiece; // update all the pieces
342 while(piece&&!fail)
343 {
344 if(!piece->useRegisterMatch)
345 {
346 newElement.offset=0;
347 newElement.length=count;
348 fail=!SetSARange(&instance->expressionPieces[piece->pieceIndex].matchArray,i+matchOffset,1,&newElement); // add this in
349 }
350 else
351 {
352 if(GetRERegisterMatch(expression->compiledExpression,piece->registerIndex,&newElement.offset,&newElement.length))
353 {
354 fail=!SetSARange(&instance->expressionPieces[piece->pieceIndex].matchArray,i+matchOffset,1,&newElement); // add this in
355
356 if(newElement.offset>instance->expressionPieces[piece->pieceIndex].maximumStartDistance)
357 {
358 // fprintf(stderr,"%s: %d\n",instance->expressionPieces[piece->pieceIndex].expressionPiece->parent->expressionName,newElement.offset);
359 instance->expressionPieces[piece->pieceIndex].maximumStartDistance=newElement.offset; // keep track of largest offset here
360 }
361 }
362 }
363 piece=piece->next;
364 }
365
366 i+=matchOffset+1; // skip to spot just after this one
367 if(i<=startOffset+numBytes) // see if we should adjust the chunk
368 {
369 if((++newOffset)>=newChunk->totalBytes) // move forward in the input 1 step
370 {
371 newChunk=newChunk->nextHeader;
372 newOffset=0;
373 }
374 }
375 }
376 }
377 else
378 {
379 fail=true;
380 }
381 }
382 }
383 return(!fail);
384 }
385
AdjustMappingTableForChange(SYNTAX_INSTANCE * instance,UINT32 startOffset,UINT32 endOffset,UINT32 numBytes)386 static bool AdjustMappingTableForChange(SYNTAX_INSTANCE *instance,UINT32 startOffset,UINT32 endOffset,UINT32 numBytes)
387 // run through the mapping table, and reduce or expand as needed to account
388 // for the text change
389 {
390 DeleteSARange(&instance->mappingArray,startOffset,endOffset-startOffset); // this cannot fail
391 return(InsertSARange(&instance->mappingArray,startOffset,numBytes));
392 }
393
LocatePieceWithOffset(EXPRESSION_PIECE_INSTANCE * instance,UINT32 position,UINT32 * startPosition,UINT32 * length)394 static bool LocatePieceWithOffset(EXPRESSION_PIECE_INSTANCE *instance,UINT32 position,UINT32 *startPosition,UINT32 *length)
395 // Locate the nearest expression match which is >= position for the given instance
396 // If there is none, return false
397 {
398 UINT32
399 currentPosition;
400 UINT32
401 numElements;
402 EXPRESSION_MATCH_ELEMENT
403 *matchElement;
404 bool
405 done,
406 found;
407
408 found=false;
409 if(instance->maximumStartDistance) // see if this piece uses start offsets at all (if not, the search can go faster)
410 {
411 if(position>instance->maximumStartDistance)
412 {
413 currentPosition=position-instance->maximumStartDistance;
414 }
415 else
416 {
417 currentPosition=0;
418 }
419 done=false;
420 while((currentPosition<=position)&&!done)
421 {
422 if(GetSARecordAtOrAfterPosition(&instance->matchArray,currentPosition,startPosition,&numElements,(void **)(&matchElement)))
423 {
424 *startPosition+=matchElement->offset;
425 if(*startPosition>=position)
426 {
427 *length=matchElement->length;
428 done=found=true;
429 }
430 else
431 {
432 currentPosition++;
433 }
434 }
435 else
436 {
437 done=true; // no more matches
438 }
439 }
440 }
441 else // search the fast way
442 {
443 if(GetSARecordAtOrAfterPosition(&instance->matchArray,position,startPosition,&numElements,(void **)(&matchElement)))
444 {
445 *length=matchElement->length;
446 found=true;
447 }
448 }
449 return(found);
450 }
451
FindStartPossiblesOrEnd(SYNTAX_INSTANCE * instance,SYNTAX_STYLE_MAPPING_LIST * possibles,SYNTAX_STYLE_MAPPING * endMapping,UINT32 position,UINT32 * matchPosition,UINT32 * matchLength,bool * wasEnd)452 static SYNTAX_STYLE_MAPPING *FindStartPossiblesOrEnd(SYNTAX_INSTANCE *instance,SYNTAX_STYLE_MAPPING_LIST *possibles,SYNTAX_STYLE_MAPPING *endMapping,UINT32 position,UINT32 *matchPosition,UINT32 *matchLength,bool *wasEnd)
453 // given a position in the text, and a list of rules to match against, see if we can find a match
454 // for any starting expression of any of the rules
455 // if so, return the matched rule, position and length
456 // if nothing matches, return NULL
457 // NOTE: if none of the starting expressions match at or before the
458 // next end expression match, return that
459 {
460 UINT32
461 pieceIndex;
462 UINT32
463 startPosition,
464 length;
465 UINT32
466 bestStartPosition,
467 bestLength;
468 SYNTAX_STYLE_MAPPING
469 *bestPossible;
470 EXPRESSION_PIECE_LIST
471 *pieceListElement;
472
473 bestStartPosition=bestLength=0;
474 bestPossible=NULL;
475 *wasEnd=false;
476
477 while(possibles)
478 {
479 pieceIndex=possibles->mappingEntry->startExpressionPiece->pieceIndex;
480
481 if(LocatePieceWithOffset(&instance->expressionPieces[pieceIndex],position,&startPosition,&length))
482 {
483 if(!bestPossible||startPosition<bestStartPosition)
484 {
485 bestPossible=possibles->mappingEntry;
486 bestStartPosition=startPosition;
487 bestLength=length;
488 }
489 }
490 possibles=possibles->next;
491 }
492
493 if(endMapping&&endMapping->endExpressionPieces) // see if there is a possible end to search for
494 {
495 pieceListElement=endMapping->endExpressionPieces;
496 while(pieceListElement)
497 {
498 pieceIndex=pieceListElement->piece->pieceIndex;
499 if(LocatePieceWithOffset(&instance->expressionPieces[pieceIndex],position,&startPosition,&length))
500 {
501 if(!bestPossible||startPosition<bestStartPosition)
502 {
503 bestPossible=endMapping;
504 bestStartPosition=startPosition;
505 bestLength=length;
506 *wasEnd=true;
507 }
508 }
509 pieceListElement=pieceListElement->next;
510 }
511 }
512
513 if(bestPossible) // found a match!
514 {
515 *matchPosition=bestStartPosition;
516 *matchLength=bestLength;
517
518 // fprintf(stderr,"Best was %s: %d:%d\n",&bestPossible->mappingName[0],bestStartPosition,bestLength);
519
520 return(bestPossible);
521 }
522 return(NULL);
523 }
524
HandleMatchesForDepth(EDITOR_BUFFER * buffer,SYNTAX_STYLE_MAPPING_LIST * startRules,SYNTAX_STYLE_MAPPING * endRule,UINT32 * currentPosition,UINT32 betweenStyle)525 static bool HandleMatchesForDepth(EDITOR_BUFFER *buffer,SYNTAX_STYLE_MAPPING_LIST *startRules,SYNTAX_STYLE_MAPPING *endRule,UINT32 *currentPosition,UINT32 betweenStyle)
526 // Recursively find nested matches, adjust styles, until we find the end match for this depth,
527 // or run out of data
528 // NOTE: this will return false when it could not locate the end match for the current depth
529 // NOTE: even if this returns false, it may have updated currentPosition
530 {
531 UINT32
532 matchPosition,
533 matchLength;
534 SYNTAX_STYLE_MAPPING
535 *mapping;
536 bool
537 wasEnd;
538
539 while(true)
540 {
541 if((mapping=FindStartPossiblesOrEnd(buffer->syntaxInstance,startRules,endRule,*currentPosition,&matchPosition,&matchLength,&wasEnd)))
542 {
543 SetStyleRange(buffer->styleUniverse,*currentPosition,matchPosition-*currentPosition,betweenStyle);
544 *currentPosition=matchPosition+matchLength; // move to the end of this match
545
546 if(!wasEnd)
547 {
548 SetStyleRange(buffer->styleUniverse,matchPosition,matchLength,mapping->startStyle);
549
550 if(mapping->endExpressionPieces) // if an ending expression was defined, go look for it
551 {
552 if(!HandleMatchesForDepth(buffer,mapping->betweenList,mapping,currentPosition,mapping->betweenStyle))
553 {
554 return(false);
555 }
556 }
557 }
558 else
559 {
560 SetStyleRange(buffer->styleUniverse,matchPosition,matchLength,mapping->endStyle);
561 return(true);
562 }
563 }
564 else // had an end expression, but unable to locate it, so this mapping goes to the end of the text, and we're done
565 {
566 SetStyleRange(buffer->styleUniverse,*currentPosition,buffer->textUniverse->totalBytes-*currentPosition,betweenStyle); // set style to end of document
567 *currentPosition=buffer->textUniverse->totalBytes;
568 return(false);
569 }
570 }
571 }
572
HandleMatchesForDepth0(EDITOR_BUFFER * buffer,SYNTAX_STYLE_MAPPING_LIST * startRules,UINT32 currentPosition,UINT32 lastPosition)573 static void HandleMatchesForDepth0(EDITOR_BUFFER *buffer,SYNTAX_STYLE_MAPPING_LIST *startRules,UINT32 currentPosition,UINT32 lastPosition)
574 // When at depth 0, this attempts to find matches and calls HandleMatchesForDepth for greater depths
575 // At depth 0, we do some additional work to keep the mapping table up to date
576 // and to see if it is possible to stop working if we are in sync with the mapping
577 // table.
578 {
579 UINT32
580 matchPosition,
581 matchLength;
582 SYNTAX_STYLE_MAPPING
583 *mapping;
584 UINT32
585 oldMappingStart,
586 oldMappingNumElements;
587 MAPPING_ELEMENT
588 *oldMappingElement,
589 newMappingElement;
590 bool
591 wasEnd,
592 done;
593
594 done=false;
595 while(!done)
596 {
597 if((mapping=FindStartPossiblesOrEnd(buffer->syntaxInstance,startRules,NULL,currentPosition,&matchPosition,&matchLength,&wasEnd)))
598 {
599 SetStyleRange(buffer->styleUniverse,currentPosition,matchPosition-currentPosition,0); // clear up to this point
600 ClearSARange(&buffer->syntaxInstance->mappingArray,currentPosition,matchPosition-currentPosition); // clear entries in the mapping table up to this point
601
602 SetStyleRange(buffer->styleUniverse,matchPosition,matchLength,mapping->startStyle); // set the starting style for the match
603 currentPosition=matchPosition+matchLength;
604
605 if(mapping->endExpressionPieces) // if an ending expression has been defined, go look for it, but be willing to step deeper if a start match from the between list is located
606 {
607 done=!HandleMatchesForDepth(buffer,mapping->betweenList,mapping,¤tPosition,mapping->betweenStyle); // hunt after the start for the end
608 matchLength=currentPosition-matchPosition; // update to get length of entire match
609 }
610 if(currentPosition>lastPosition) // see if we need to test for being back in sync
611 {
612 if(GetSARecordAtOrAfterPosition(&buffer->syntaxInstance->mappingArray,currentPosition,&oldMappingStart,&oldMappingNumElements,(void **)(&oldMappingElement)))
613 {
614 if(oldMappingStart>=currentPosition) // true if in sync with old mapping table now
615 {
616 done=true;
617 }
618 }
619 else
620 {
621 done=true; // if no old record, then also done
622 }
623 }
624 newMappingElement.mapping=mapping;
625 SetSARange(&buffer->syntaxInstance->mappingArray,matchPosition,matchLength,&newMappingElement); // drop this into the mapping array
626 }
627 else
628 {
629 SetStyleRange(buffer->styleUniverse,currentPosition,buffer->textUniverse->totalBytes-currentPosition,0); // clear style to end of document
630 ClearSARange(&buffer->syntaxInstance->mappingArray,currentPosition,buffer->textUniverse->totalBytes-currentPosition); // remove all mapping entries below here
631 done=true;
632 }
633 }
634 }
635
UpdateMappingCache(EDITOR_BUFFER * buffer,UINT32 earliestExpressionChange,UINT32 startOffset,UINT32 endOffset,UINT32 numBytes)636 static bool UpdateMappingCache(EDITOR_BUFFER *buffer,UINT32 earliestExpressionChange,UINT32 startOffset,UINT32 endOffset,UINT32 numBytes)
637 // Now that all of the expressions have been updated, back up to the location
638 // of the first expression cache change, work out which syntax rule was in effect,
639 // and start running through the rules, updating the styles as we go, until we
640 // get to the first place after the last expression cache change where the cached
641 // rules and the current rule set are the same (when we fall back into the pattern
642 // that existed before) or to the end of the document, whichever comes first
643 {
644 UINT32
645 startPosition,
646 numElements;
647 MAPPING_ELEMENT
648 *mappingElement;
649
650 if(AdjustMappingTableForChange(buffer->syntaxInstance,startOffset,endOffset,numBytes))
651 {
652 if(earliestExpressionChange)
653 {
654 earliestExpressionChange--; // step back one to get to a record which we know has data that was valid before the change
655 }
656 if(GetSARecordAtOrAfterPosition(&buffer->syntaxInstance->mappingArray,earliestExpressionChange,&startPosition,&numElements,(void **)(&mappingElement))) // find the mapping in effect at the time of the earliest expression change
657 {
658 if(startPosition<earliestExpressionChange) // if mapping was in effect, back up to the start of it
659 {
660 earliestExpressionChange=startPosition;
661 }
662 }
663 // now, earliestExpressionChange points to the spot in the text where the last known root level status was "no mapping in effect"
664 // start the search from here and run to at least startOffset+numBytes
665
666 ViewsStartStyleChange(buffer);
667 HandleMatchesForDepth0(buffer,buffer->syntaxInstance->parentMap->rootMapList,earliestExpressionChange,startOffset+numBytes);
668 ViewsEndStyleChange(buffer);
669 return(true);
670 }
671 return(false);
672 }
673
UpdateSyntaxInformation(EDITOR_BUFFER * buffer,UINT32 startOffset,UINT32 endOffset,UINT32 numBytes)674 bool UpdateSyntaxInformation(EDITOR_BUFFER *buffer,UINT32 startOffset,UINT32 endOffset,UINT32 numBytes)
675 // A change has been made in buffer.
676 // The text between startOffset and endOffset has been replaced with numBytes
677 // This will recalculate all the syntax highlighting information for the area of the change
678 // NOTE: if this is called for a universe which has no syntax highlighting, it does
679 // nothing.
680 // NOTE: since regular expressions can match ending at boundaries, we need
681 // to begin checking 1 character before the change, so we can match expressions
682 // which may now be matching/not matching because of the ending boundary change
683 // NOTE: since regular expressions can match starting at boundaries, we need to look
684 // one character past the end for the same reason.
685 {
686 bool
687 fail;
688 CHUNK_HEADER
689 *chunk;
690 UINT32
691 offset;
692 UINT32
693 earliestExpressionChange;
694 SYNTAX_EXPRESSION
695 *expression;
696
697 fail=false;
698 if(buffer->syntaxInstance) // only do work if this universe has a syntax instance
699 {
700 // fudge start and end parameters out by 1 so that boundary conditions are checked properly
701 if(startOffset)
702 {
703 startOffset--;
704 numBytes++; // account for byte we just moved back
705 }
706 if((startOffset+numBytes)<buffer->textUniverse->totalBytes)
707 {
708 endOffset++;
709 numBytes++; // account for byte we just moved out
710 }
711
712 PositionToChunkPositionPastEnd(buffer->textUniverse,startOffset,&chunk,&offset); // locate the start of the change
713
714 earliestExpressionChange=startOffset; // this is the max earliest expression change
715
716 expression=buffer->syntaxInstance->parentMap->firstExpression;
717 while(expression&&!fail) // update all the expressions
718 {
719 fail=!UpdateExpressionCaches(chunk,offset,buffer->syntaxInstance,expression,startOffset,endOffset,numBytes,buffer->textUniverse->totalBytes,&earliestExpressionChange);
720 expression=expression->next;
721 }
722
723 if(!fail)
724 {
725 fail=!UpdateMappingCache(buffer,earliestExpressionChange,startOffset,endOffset,numBytes); // update the mapping of expressions to style
726 }
727 }
728 return(!fail);
729 }
730
RegenerateAllSyntaxInformation(EDITOR_BUFFER * buffer)731 bool RegenerateAllSyntaxInformation(EDITOR_BUFFER *buffer)
732 // Run through buffer, and recreate its syntaxInstance, and update
733 // its editor universe's styleUniverse.
734 // This is done when a syntaxInstance is first added to an editor universe
735 // If there is a problem, SetError, return false
736 {
737 return(UpdateSyntaxInformation(buffer,0,buffer->textUniverse->totalBytes,buffer->textUniverse->totalBytes)); // pretend as if the whole thing changed
738 }
739
GetInstanceParentMap(SYNTAX_INSTANCE * instance)740 SYNTAX_MAP *GetInstanceParentMap(SYNTAX_INSTANCE *instance)
741 // return the parent syntax map for the given instance
742 {
743 return(instance->parentMap);
744 }
745
CloseSyntaxInstance(SYNTAX_INSTANCE * instance)746 void CloseSyntaxInstance(SYNTAX_INSTANCE *instance)
747 // unlink instance from its parent map, and destroy it
748 {
749 UINT32
750 i;
751
752 if(instance->next)
753 {
754 instance->next->prev=instance->prev;
755 }
756 if(instance->prev)
757 {
758 instance->prev->next=instance->next;
759 }
760 if(instance->parentMap->firstInstance==instance) // if at the head of the list, move the head
761 {
762 instance->parentMap->firstInstance=instance->next;
763 }
764
765 for(i=0;i<instance->parentMap->numExpressionPieces;i++)
766 {
767 UnInitSA(&instance->expressionPieces[i].matchArray);
768 }
769
770 UnInitSA(&instance->mappingArray);
771
772 MDisposePtr(instance);
773 }
774
OpenSyntaxInstance(SYNTAX_MAP * map,EDITOR_BUFFER * buffer)775 SYNTAX_INSTANCE *OpenSyntaxInstance(SYNTAX_MAP *map,EDITOR_BUFFER *buffer)
776 // Create an instance of a syntax highlight map
777 // link it to map and return it
778 // If there is a problem, SetError, return NULL
779 {
780 SYNTAX_INSTANCE
781 *instance;
782 SYNTAX_EXPRESSION
783 *expression;
784 EXPRESSION_PIECE
785 *expressionPiece;
786 bool
787 fail;
788 UINT32
789 i;
790
791 fail=false;
792 if((instance=(SYNTAX_INSTANCE *)MNewPtr(sizeof(SYNTAX_INSTANCE)+sizeof(EXPRESSION_PIECE_INSTANCE)*map->numExpressionPieces)))
793 {
794 instance->buffer=buffer;
795 fail=!InitSA(&instance->mappingArray,sizeof(MAPPING_ELEMENT));
796
797 for(i=0;(i<map->numExpressionPieces)&&!fail;i++)
798 {
799 instance->expressionPieces[i].maximumStartDistance=0; // never saw a match with an offset larger than this yet
800 fail=!InitSA(&instance->expressionPieces[i].matchArray,sizeof(EXPRESSION_MATCH_ELEMENT));
801 }
802 if(!fail)
803 {
804 expression=map->firstExpression; // assign all the expression pieces
805 while(expression)
806 {
807 expressionPiece=expression->firstPiece;
808 while(expressionPiece)
809 {
810 instance->expressionPieces[expressionPiece->pieceIndex].expressionPiece=expressionPiece;
811 expressionPiece=expressionPiece->next;
812 }
813 expression=expression->next;
814 }
815
816 instance->parentMap=map; // point at the parent map
817 instance->prev=NULL;
818 instance->next=map->firstInstance;
819 if(map->firstInstance)
820 {
821 map->firstInstance->prev=instance;
822 }
823 map->firstInstance=instance;
824 return(instance);
825 }
826 MDisposePtr(instance);
827 }
828 return(NULL);
829 }
830
831 // -----------------------------------------------------------------------------------------------------------
832
833 // Code for creating syntax maps
834
RemoveMappingList(SYNTAX_STYLE_MAPPING_LIST * list)835 void RemoveMappingList(SYNTAX_STYLE_MAPPING_LIST *list)
836 // remove all elements of list
837 {
838 SYNTAX_STYLE_MAPPING_LIST
839 *next;
840
841 while(list)
842 {
843 next=list->next;
844 MDisposePtr(list);
845 list=next;
846 }
847 }
848
AddMappingListElement(SYNTAX_STYLE_MAPPING_LIST ** listHead,SYNTAX_STYLE_MAPPING * mapping)849 SYNTAX_STYLE_MAPPING_LIST *AddMappingListElement(SYNTAX_STYLE_MAPPING_LIST **listHead,SYNTAX_STYLE_MAPPING *mapping)
850 // Create a new mapping list element, and link it to the end of the given list
851 {
852 SYNTAX_STYLE_MAPPING_LIST
853 *previousElement;
854 SYNTAX_STYLE_MAPPING_LIST
855 *mappingListElement;
856
857 previousElement=*listHead;
858 while(previousElement&&(previousElement->mappingEntry!=mapping)&&previousElement->next)
859 {
860 previousElement=previousElement->next;
861 }
862 if((!previousElement)||(previousElement->mappingEntry!=mapping))
863 {
864 if((mappingListElement=(SYNTAX_STYLE_MAPPING_LIST *)MNewPtr(sizeof(SYNTAX_STYLE_MAPPING_LIST))))
865 {
866 mappingListElement->mappingEntry=mapping;
867 if(previousElement)
868 {
869 previousElement->next=mappingListElement;
870 mappingListElement->prev=previousElement;
871 mappingListElement->next=NULL;
872 }
873 else
874 {
875 *listHead=mappingListElement;
876 mappingListElement->prev=NULL;
877 mappingListElement->next=NULL;
878 }
879 return(mappingListElement);
880 }
881 }
882 else
883 {
884 SetError(errorDescriptions[LIST_MAPPING_EXISTS]);
885 }
886 return(NULL);
887 }
888
AddBetweenListElementToMapping(SYNTAX_STYLE_MAPPING * mapping,SYNTAX_STYLE_MAPPING * betweenMapping)889 SYNTAX_STYLE_MAPPING_LIST *AddBetweenListElementToMapping(SYNTAX_STYLE_MAPPING *mapping,SYNTAX_STYLE_MAPPING *betweenMapping)
890 // Add betweenMapping to mapping
891 // if there is a problem, SetError, and return false
892 {
893 return(AddMappingListElement(&mapping->betweenList,betweenMapping));
894 }
895
AddRootListElementToMap(SYNTAX_MAP * map,SYNTAX_STYLE_MAPPING * rootMapping)896 SYNTAX_STYLE_MAPPING_LIST *AddRootListElementToMap(SYNTAX_MAP *map,SYNTAX_STYLE_MAPPING *rootMapping)
897 // Add rootMapping to map
898 // if there is a problem, SetError, and return false
899 {
900 return(AddMappingListElement(&map->rootMapList,rootMapping));
901 }
902
LocateSyntaxStyleMapping(SYNTAX_MAP * map,const char * name)903 SYNTAX_STYLE_MAPPING *LocateSyntaxStyleMapping(SYNTAX_MAP *map,const char *name)
904 // locate a syntax style mapping with name in map, and return a pointer to it
905 // if none can be found, return NULL
906 // NOTE: this could probably benefit from a hash table
907 {
908 SYNTAX_STYLE_MAPPING
909 *mapping;
910
911 mapping=map->firstStyleMapping;
912 while(mapping&&strcmp(mapping->mappingName,name)!=0)
913 {
914 mapping=mapping->next;
915 }
916 return(mapping);
917 }
918
SetMappingStartExpressionPiece(SYNTAX_STYLE_MAPPING * mapping,EXPRESSION_PIECE * startExpressionPiece)919 void SetMappingStartExpressionPiece(SYNTAX_STYLE_MAPPING *mapping,EXPRESSION_PIECE *startExpressionPiece)
920 // Set the pointer to the start expression piece for mapping
921 {
922 mapping->startExpressionPiece=startExpressionPiece;
923 }
924
RemoveExpressionList(EXPRESSION_PIECE_LIST * list)925 void RemoveExpressionList(EXPRESSION_PIECE_LIST *list)
926 // remove all elements of list
927 {
928 EXPRESSION_PIECE_LIST
929 *next;
930
931 while(list)
932 {
933 next=list->next;
934 MDisposePtr(list);
935 list=next;
936 }
937 }
938
AddMappingEndExpressionPiece(SYNTAX_STYLE_MAPPING * mapping,EXPRESSION_PIECE * endExpressionPiece)939 EXPRESSION_PIECE_LIST *AddMappingEndExpressionPiece(SYNTAX_STYLE_MAPPING *mapping,EXPRESSION_PIECE *endExpressionPiece)
940 // Add endExpression piece to the end of the list of ending expression pieces hanging off of mapping
941 // NOTE: this considers it an error to have two of the same piece on the list
942 // if there is a problem, SetError, and return NULL
943 {
944 EXPRESSION_PIECE_LIST
945 *previousElement;
946 EXPRESSION_PIECE_LIST
947 *listElement;
948
949 previousElement=mapping->endExpressionPieces;
950 while(previousElement&&(previousElement->piece!=endExpressionPiece)&&previousElement->next)
951 {
952 previousElement=previousElement->next;
953 }
954 if((!previousElement)||(previousElement->piece!=endExpressionPiece))
955 {
956
957 if((listElement=(EXPRESSION_PIECE_LIST *)MNewPtr(sizeof(EXPRESSION_PIECE_LIST))))
958 {
959 listElement->piece=endExpressionPiece;
960 if(previousElement)
961 {
962 previousElement->next=listElement;
963 listElement->prev=previousElement;
964 listElement->next=NULL;
965 }
966 else
967 {
968 mapping->endExpressionPieces=listElement;
969 listElement->prev=NULL;
970 listElement->next=NULL;
971 }
972 }
973 return(listElement);
974 }
975 else
976 {
977 SetError(errorDescriptions[EXPRESSION_PIECE_EXISTS]);
978 }
979 return(NULL);
980 }
981
SetMappingStyles(SYNTAX_STYLE_MAPPING * mapping,UINT32 startStyle,UINT32 betweenStyle,UINT32 endStyle)982 void SetMappingStyles(SYNTAX_STYLE_MAPPING *mapping,UINT32 startStyle,UINT32 betweenStyle,UINT32 endStyle)
983 // Set the pointer to the start expression piece for mapping
984 {
985 mapping->startStyle=startStyle;
986 mapping->betweenStyle=betweenStyle;
987 mapping->endStyle=endStyle;
988 }
989
RemoveMappingFromSyntaxMap(SYNTAX_MAP * map,SYNTAX_STYLE_MAPPING * mapping)990 void RemoveMappingFromSyntaxMap(SYNTAX_MAP *map,SYNTAX_STYLE_MAPPING *mapping)
991 // pull mapping out of the map and delete it
992 {
993 RemoveExpressionList(mapping->endExpressionPieces); // get rid of list of ending expressions
994
995 RemoveMappingList(mapping->betweenList); // get rid of sub-lists
996
997 if(mapping->next)
998 {
999 mapping->next->prev=mapping->prev;
1000 }
1001 if(mapping->prev)
1002 {
1003 mapping->prev->next=mapping->next;
1004 }
1005 if(map->firstStyleMapping==mapping) // if at the head of the list, move the head
1006 {
1007 map->firstStyleMapping=mapping->next;
1008 }
1009 MDisposePtr(mapping);
1010 }
1011
AddMappingToSyntaxMap(SYNTAX_MAP * map,const char * mappingName)1012 SYNTAX_STYLE_MAPPING *AddMappingToSyntaxMap(SYNTAX_MAP *map,const char *mappingName)
1013 // Add a mapping of expressions to styles to map
1014 // Initially, the mapping is empty, and must be filled in by calls to the above routines
1015 // if there is a problem, SetError, and return NULL
1016 {
1017 SYNTAX_STYLE_MAPPING
1018 *mapping;
1019
1020 if(!LocateSyntaxStyleMapping(map,mappingName))
1021 {
1022 if((mapping=(SYNTAX_STYLE_MAPPING *)MNewPtr(sizeof(SYNTAX_STYLE_MAPPING)+strlen(mappingName)+1)))
1023 {
1024 mapping->parent=map;
1025
1026 strcpy(&mapping->mappingName[0],mappingName); // copy over the name
1027
1028 mapping->startExpressionPiece=NULL;
1029 mapping->endExpressionPieces=NULL;
1030
1031 mapping->startStyle=0;
1032 mapping->betweenStyle=0;
1033 mapping->endStyle=0;
1034
1035 mapping->betweenList=NULL;
1036
1037 mapping->prev=NULL;
1038 mapping->next=map->firstStyleMapping; // link it onto the list
1039 if(map->firstStyleMapping)
1040 {
1041 map->firstStyleMapping->prev=mapping;
1042 }
1043 map->firstStyleMapping=mapping;
1044 return(mapping);
1045 }
1046 }
1047 else
1048 {
1049 SetError(errorDescriptions[STYLE_MAPPING_EXISTS]);
1050 }
1051 return(NULL);
1052 }
1053
LocateExpressionPiece(SYNTAX_EXPRESSION * expression,bool useRegisterMatch,UINT32 registerIndex)1054 EXPRESSION_PIECE *LocateExpressionPiece(SYNTAX_EXPRESSION *expression,bool useRegisterMatch,UINT32 registerIndex)
1055 // Attempt to find the piece of expression which has the given attributes,
1056 // If no piece can be located, return NULL
1057 {
1058 EXPRESSION_PIECE
1059 *piece;
1060 bool
1061 found;
1062
1063 found=false;
1064 piece=expression->firstPiece;
1065 while(piece&&!found)
1066 {
1067 if((!useRegisterMatch&&!piece->useRegisterMatch)||(useRegisterMatch&&piece->useRegisterMatch&&(piece->registerIndex==registerIndex)))
1068 {
1069 found=true;
1070 }
1071 else
1072 {
1073 piece=piece->next;
1074 }
1075 }
1076 return(piece);
1077 }
1078
RemovePieceFromExpression(SYNTAX_EXPRESSION * expression,EXPRESSION_PIECE * piece)1079 void RemovePieceFromExpression(SYNTAX_EXPRESSION *expression,EXPRESSION_PIECE *piece)
1080 // Remove piece from expression
1081 {
1082 if(piece->next)
1083 {
1084 piece->next->prev=piece->prev;
1085 }
1086 if(piece->prev)
1087 {
1088 piece->prev->next=piece->next;
1089 }
1090 if(expression->firstPiece==piece) // if at the head of the list, move the head
1091 {
1092 expression->firstPiece=piece->next;
1093 }
1094 MDisposePtr(piece);
1095 }
1096
AddPieceToExpression(SYNTAX_EXPRESSION * expression,bool useRegisterMatch,UINT32 registerIndex)1097 EXPRESSION_PIECE *AddPieceToExpression(SYNTAX_EXPRESSION *expression,bool useRegisterMatch,UINT32 registerIndex)
1098 // Add a piece to expression
1099 // NOTE: if there is already a piece on expression matching the given one, just return it
1100 // if there is a problem, SetError, return NULL
1101 {
1102 EXPRESSION_PIECE
1103 *piece;
1104
1105 if(!(piece=LocateExpressionPiece(expression,useRegisterMatch,registerIndex)))
1106 {
1107 if((piece=(EXPRESSION_PIECE *)MNewPtr(sizeof(EXPRESSION_PIECE))))
1108 {
1109 piece->parent=expression;
1110 piece->prev=NULL;
1111 piece->next=expression->firstPiece;
1112 if(expression->firstPiece)
1113 {
1114 expression->firstPiece->prev=piece;
1115 }
1116 expression->firstPiece=piece;
1117
1118 piece->useRegisterMatch=useRegisterMatch; // tells if entire expression matches, or a register matches
1119 piece->registerIndex=registerIndex; // if useRegisterMatch is true, this gives the register index of the match
1120
1121 piece->pieceIndex=expression->parent->numExpressionPieces;
1122 expression->parent->numExpressionPieces++;
1123 }
1124 }
1125 return(piece);
1126 }
1127
LocateSyntaxMapExpression(SYNTAX_MAP * map,const char * name)1128 SYNTAX_EXPRESSION *LocateSyntaxMapExpression(SYNTAX_MAP *map,const char *name)
1129 // locate an expression with name in map, and return a pointer to it
1130 // if none can be found, return NULL
1131 {
1132 SYNTAX_EXPRESSION
1133 *expression;
1134
1135 expression=map->firstExpression;
1136 while(expression&&strcmp(expression->expressionName,name)!=0)
1137 {
1138 expression=expression->next;
1139 }
1140 return(expression);
1141 }
1142
RemoveExpressionFromSyntaxMap(SYNTAX_MAP * map,SYNTAX_EXPRESSION * expression)1143 void RemoveExpressionFromSyntaxMap(SYNTAX_MAP *map,SYNTAX_EXPRESSION *expression)
1144 // pull expression out of the map and delete it
1145 // NOTE: it is the caller's responsibility to make sure there are no
1146 // mappings which might be left pointing to the given expression
1147 // after it is deleted
1148 {
1149 REFree(expression->compiledExpression);
1150
1151 if(expression->next)
1152 {
1153 expression->next->prev=expression->prev;
1154 }
1155 if(expression->prev)
1156 {
1157 expression->prev->next=expression->next;
1158 }
1159 if(map->firstExpression==expression) // if at the head of the list, move the head
1160 {
1161 map->firstExpression=expression->next;
1162 }
1163 MDisposePtr(expression);
1164 }
1165
AddExpressionToSyntaxMap(SYNTAX_MAP * map,const char * expressionName,const char * expressionText)1166 SYNTAX_EXPRESSION *AddExpressionToSyntaxMap(SYNTAX_MAP *map,const char *expressionName,const char *expressionText)
1167 // Add a new expression to map
1168 // If there is a problem, SetError, and return NULL
1169 {
1170 SYNTAX_EXPRESSION
1171 *expression;
1172
1173 if(!LocateSyntaxMapExpression(map,expressionName))
1174 {
1175 if((expression=(SYNTAX_EXPRESSION *)MNewPtr(sizeof(SYNTAX_EXPRESSION)+strlen(expressionName)+1)))
1176 {
1177 if((expression->compiledExpression=RECompile((UINT8 *)expressionText,(UINT32)strlen(expressionText),NULL)))
1178 {
1179 strcpy(&expression->expressionName[0],expressionName); // copy over the name
1180 expression->parent=map;
1181 expression->prev=NULL;
1182 expression->next=map->firstExpression; // link it onto the list
1183 if(map->firstExpression)
1184 {
1185 map->firstExpression->prev=expression;
1186 }
1187 map->firstExpression=expression;
1188 expression->firstPiece=NULL; // no pieces defined to match yet
1189 return(expression);
1190 }
1191 MDisposePtr(expression);
1192 }
1193 }
1194 else
1195 {
1196 SetError(errorDescriptions[EXPRESSION_EXISTS]);
1197 }
1198 return(NULL);
1199 }
1200
GetSyntaxMapName(SYNTAX_MAP * map)1201 char *GetSyntaxMapName(SYNTAX_MAP *map)
1202 // return a pointer to the name of the given syntax map
1203 {
1204 return(&map->mapName[0]);
1205 }
1206
LocateNextSyntaxMap(SYNTAX_MAP * map)1207 SYNTAX_MAP *LocateNextSyntaxMap(SYNTAX_MAP *map)
1208 // return a pointer to the syntax map defined after the passed one
1209 // if none can be found, return NULL
1210 // if NULL is passed in, return the first one
1211 {
1212 if(map)
1213 {
1214 return(map->next);
1215 }
1216 else
1217 {
1218 return(syntaxMapHead);
1219 }
1220 }
1221
LocateSyntaxMap(char * name)1222 SYNTAX_MAP *LocateSyntaxMap(char *name)
1223 // locate a syntax map with name, and return a pointer to it
1224 // if none can be found, return NULL
1225 {
1226 SYNTAX_MAP
1227 *map;
1228
1229 map=syntaxMapHead;
1230 while(map&&strcmp(map->mapName,name)!=0)
1231 {
1232 map=map->next;
1233 }
1234 return(map);
1235 }
1236
UnlinkSyntaxMap(SYNTAX_MAP * map)1237 void UnlinkSyntaxMap(SYNTAX_MAP *map)
1238 // unlink map from the global list of maps
1239 {
1240 if(map->next)
1241 {
1242 map->next->prev=map->prev;
1243 }
1244 else
1245 {
1246 syntaxMapTail=map->prev;
1247 }
1248
1249 if(map->prev)
1250 {
1251 map->prev->next=map->next;
1252 }
1253 else
1254 {
1255 syntaxMapHead=map->next;
1256 }
1257 }
1258
LinkSyntaxMap(SYNTAX_MAP * map)1259 bool LinkSyntaxMap(SYNTAX_MAP *map)
1260 // link map to the global list of maps
1261 // NOTE: this must be called only once for each map
1262 // NOTE: this will fail only if a map already exists in the global list
1263 // which has the same name as map.
1264 {
1265 if(!LocateSyntaxMap(map->mapName))
1266 {
1267 map->prev=syntaxMapTail;
1268 map->next=NULL;
1269 if(syntaxMapTail)
1270 {
1271 syntaxMapTail->next=map;
1272 syntaxMapTail=map;
1273 }
1274 else
1275 {
1276 syntaxMapHead=syntaxMapTail=map;
1277 }
1278 return(true);
1279 }
1280 else
1281 {
1282 SetError(errorDescriptions[MAP_EXISTS]);
1283 }
1284 return(false);
1285 }
1286
CloseSyntaxMap(SYNTAX_MAP * map)1287 void CloseSyntaxMap(SYNTAX_MAP *map)
1288 // dispose of a syntax map
1289 // NOTE: the map must not be in use by any buffer when this is called
1290 {
1291 RemoveMappingList(map->rootMapList); // get rid of the root mapping list
1292
1293 while(map->firstStyleMapping)
1294 {
1295 RemoveMappingFromSyntaxMap(map,map->firstStyleMapping);
1296 }
1297
1298 while(map->firstExpression)
1299 {
1300 while(map->firstExpression->firstPiece)
1301 {
1302 RemovePieceFromExpression(map->firstExpression,map->firstExpression->firstPiece);
1303 }
1304 RemoveExpressionFromSyntaxMap(map,map->firstExpression);
1305 }
1306
1307 MDisposePtr(map);
1308 }
1309
OpenSyntaxMap(char * name)1310 SYNTAX_MAP *OpenSyntaxMap(char *name)
1311 // create an empty syntax map with the given name, link it to the
1312 // end of the global list
1313 // if there is a problem, set the error, and return NULL
1314 {
1315 SYNTAX_MAP
1316 *map;
1317
1318 if((map=(SYNTAX_MAP *)MNewPtr(sizeof(SYNTAX_MAP)+strlen(name)+1)))
1319 {
1320 map->numExpressionPieces=0;
1321 map->firstExpression=NULL;
1322 map->firstStyleMapping=NULL;
1323 map->firstInstance=NULL;
1324 map->rootMapList=NULL;
1325 strcpy(&map->mapName[0],name); // copy over the name
1326 map->prev=NULL;
1327 map->next=NULL;
1328 return(map);
1329 }
1330 return(NULL);
1331 }
1332
LocateNextEditorBufferOnMap(SYNTAX_MAP * map,EDITOR_BUFFER * buffer)1333 EDITOR_BUFFER *LocateNextEditorBufferOnMap(SYNTAX_MAP *map,EDITOR_BUFFER *buffer)
1334 // Locate the next editor universe linked to map
1335 // If buffer is passed as NULL, return the first buffer
1336 // If there are no more buffers linked to the map, return NULL
1337 {
1338 if(buffer)
1339 {
1340 if(buffer->syntaxInstance->next)
1341 {
1342 return(buffer->syntaxInstance->next->buffer);
1343 }
1344 }
1345 else
1346 {
1347 if(map->firstInstance)
1348 {
1349 return(map->firstInstance->buffer);
1350 }
1351 }
1352 return(NULL);
1353 }
1354
GetAssignedSyntaxMap(EDITOR_BUFFER * buffer)1355 SYNTAX_MAP *GetAssignedSyntaxMap(EDITOR_BUFFER *buffer)
1356 // Return the syntax map assigned to buffer
1357 // if none has been assigned, return NULL
1358 {
1359 if(buffer->syntaxInstance) // get rid of anything that may have been there
1360 {
1361 return(GetInstanceParentMap(buffer->syntaxInstance));
1362 }
1363 return(NULL);
1364 }
1365
AssignSyntaxMap(EDITOR_BUFFER * buffer,SYNTAX_MAP * map)1366 bool AssignSyntaxMap(EDITOR_BUFFER *buffer,SYNTAX_MAP *map)
1367 // Assign or remove a SYNTAX_MAP from buffer
1368 // if map is passed as NULL, remove any current map
1369 {
1370 bool
1371 fail;
1372
1373 fail=false;
1374 if(buffer->syntaxInstance) // get rid of anything that may have been there
1375 {
1376 CloseSyntaxInstance(buffer->syntaxInstance);
1377 buffer->syntaxInstance=NULL; // remember this is gone
1378
1379 if(!map)
1380 {
1381 // Clear style information for the universe
1382 ViewsStartStyleChange(buffer);
1383 SetStyleRange(buffer->styleUniverse,0,buffer->textUniverse->totalBytes,0); // clear out all style info
1384 ViewsEndStyleChange(buffer);
1385 }
1386 }
1387 if(map)
1388 {
1389 if((buffer->syntaxInstance=OpenSyntaxInstance(map,buffer)))
1390 {
1391 fail=!RegenerateAllSyntaxInformation(buffer); // update all the information
1392 }
1393 else
1394 {
1395 fail=true; // something went wrong trying to get the instance
1396 }
1397 }
1398 return(!fail);
1399 }
1400
UnInitSyntaxMaps()1401 void UnInitSyntaxMaps()
1402 // undo what InitSyntaxMaps did
1403 // NOTE: the caller much make sure no maps are in use
1404 // before calling this routine.
1405 {
1406 SYNTAX_MAP
1407 *map;
1408
1409 while((map=syntaxMapHead))
1410 {
1411 UnlinkSyntaxMap(map);
1412 CloseSyntaxMap(map);
1413 }
1414 }
1415
InitSyntaxMaps()1416 bool InitSyntaxMaps()
1417 // initialize the global table of syntax maps
1418 {
1419 syntaxMapHead=syntaxMapTail=NULL;
1420 return(true);
1421 }
1422