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,&currentPosition,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