File: programming/cocoa/UKSyntaxColoredTextDocument.zip/UKSyntaxColoredTextDocument/UKSyntaxColoredTextDocument.m


/* =============================================================================
	FILE:		UKSyntaxColoredTextDocument.m
	PROJECT:	CocoaTads
	
	AUTHORS:	M. Uli Kusterer <witness_at_zathras_dot_de>, (c) 2003, all rights
				reserved.
	
	REVISIONS:
		2003-05-31	UK	Created.
   ========================================================================== */
 
#import "UKSyntaxColoredTextDocument.h"
#import "NSArray+Color.h"
#import "NSScanner+SkipUpToCharset.h"
/* -----------------------------------------------------------------------------
	init:
		Constructor that inits sourceCode member variable as a flag. It's
		storage for the text until the NIB's been loaded.
   -------------------------------------------------------------------------- *//* -----------------------------------------------------------------------------
	windowNibName:
		Name of NIB file to use.
   -------------------------------------------------------------------------- */// Override returning the nib file name of the document
    // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
"UKSyntaxColoredTextDocument""SyntaxColorDefaults" ofType: @"plist"/* -----------------------------------------------------------------------------
	windowControllerDidLoadNib:
		NIB has been loaded, fill the text view with our text and apply
		initial syntax coloring.
   -------------------------------------------------------------------------- */// Set up some sensible defaults for syntax coloring:
// Load source code into text view, if necessary:
// Set up our progress indicator:
// NIB forgets that :-(
"Finished."];
	
	// Register for "text changed" notifications of our text storage:
// Put selection at top like Project Builder has it, so user sees it:
// Make sure text isn't wrapped:
// Do initial syntax coloring of our file:
// Make sure we can use "find" if we're on 10.3:
/* -----------------------------------------------------------------------------
	dataRepresentationOfType:
		Save raw text to a file as MacRoman text.
   -------------------------------------------------------------------------- *//* -----------------------------------------------------------------------------
	loadDataRepresentation:
		Load plain MacRoman text from a text file.
   -------------------------------------------------------------------------- */// sourceCode is a member variable:
// Release any old text.
// Load the new text.
	
	/* Try to load it into textView and syntax colorize it:
		Since this may be called before the NIB has been loaded, we keep around
		sourceCode as a data member and try these two calls again in windowControllerDidLoadNib: */// Try to get selection info if possible:
// This is always false when xCode calls us???
/* -----------------------------------------------------------------------------
	processEditing:
		Part of the text was changed. Recolor it.
   -------------------------------------------------------------------------- */// Was delete op or undo that could have changed text length?
//	Try to get chars around this to recolor any identifier we're in:
// Perform the syntax coloring:
/* TODO: If we're in a multi-line comment and we're typing a comment-end
			character, or we're in a string and we're typing a quote character,
			this should include the rest of the text up to the next comment/string
			end character in the recalc. */
		
		// Scan up to prev line break:
'\n' || theCh == '\r'// Scan up to next line break:
'\n' || theCh == '\r'// Open identifier, comment etc.? Make sure we include the whole range.
// Actually recolor the changed part:
/* -----------------------------------------------------------------------------
	textView:shouldChangeTextinRange:replacementString:
		Perform indentation-maintaining if we're supposed to.
   -------------------------------------------------------------------------- */// Queue this up on the event loop. If we change the text here, we only confuse the undo stack.
// This actually does what we want to do in textView:shouldChangeTextInRange:
"\n""\r"'\n''\r'// Terminate the loop.
' ''\t'/* -----------------------------------------------------------------------------
	toggleAutoSyntaxColoring:
		Action for menu item that toggles automatic syntax coloring on and off.
   -------------------------------------------------------------------------- *//* -----------------------------------------------------------------------------
	setAutoSyntaxColoring:
		Accessor to turn automatic syntax coloring on or off.
   -------------------------------------------------------------------------- *//* -----------------------------------------------------------------------------
	autoSyntaxColoring:
		Accessor for determining whether automatic syntax coloring is on or off.
   -------------------------------------------------------------------------- *//* -----------------------------------------------------------------------------
	toggleMaintainIndentation:
		Action for menu item that toggles indentation maintaining on and off.
   -------------------------------------------------------------------------- *//* -----------------------------------------------------------------------------
	setMaintainIndentation:
		Accessor to turn indentation maintaining on or off.
   -------------------------------------------------------------------------- *//* -----------------------------------------------------------------------------
	maintainIndentation:
		Accessor for determining whether indentation maintaining is on or off.
   -------------------------------------------------------------------------- *//* -----------------------------------------------------------------------------
	showGoToPanel:
		Action for menu item that shows the "Go to line" panel.
   -------------------------------------------------------------------------- *//* -----------------------------------------------------------------------------
	goToLine:
		This selects the specified line of the document.
   -------------------------------------------------------------------------- */"\n\r"// Skip non-linebreak chars:
// If this is the LF in a CRLF sequence, only count it as one line break:
'\n''\r'// Calc range and increase line number:
"Characters %u to %u"/* -----------------------------------------------------------------------------
	turnOffWrapping:
		Makes the view so wide that text won't wrap anymore.
   -------------------------------------------------------------------------- */// Make sure we can see right edge of line:
    [scrollView setHasHorizontalScroller:YES];
	
	// Make text container so wide it won't wrap:
// Make sure text view is wide enough:
/* -----------------------------------------------------------------------------
	goToCharacter:
		This selects the specified character in the document.
   -------------------------------------------------------------------------- */"Characters %u to %u"// Unselect any trailing returns so we don't indent the next line after a full-line selection.
'\n''\r''\n''\r'"\t""\t"// Unselect any trailing returns so we don't indent the next line after a full-line selection.
'\n''\r''\n''\r''\t'' ''\t'' '/* -----------------------------------------------------------------------------
	validateMenuItem:
		Make sure check marks of the "Toggle auto syntax coloring" and "Maintain
		indentation" menu items are set up properly.
   -------------------------------------------------------------------------- *//* -----------------------------------------------------------------------------
	recolorCompleteFile:
		IBAction to do a complete recolor of the whole friggin' document.
		This is called once after the document's been loaded and leaves some
		custom styles in the document which are used by recolorRange to properly
		perform recoloring of parts.
   -------------------------------------------------------------------------- */// Causes recoloring notification.
/* -----------------------------------------------------------------------------
	recolorRange:
		Try to apply syntax coloring to the text in our text view. This
		overwrites any styles the text may have had before. This function
		guarantees that it'll preserve the selection.
		
		Note that the order in which the different things are colorized is
		important. E.g. identifiers go first, followed by comments, since that
		way colors are removed from identifiers inside a comment and replaced
		with the comment color, etc. 
		
		The range passed in here is special, and may not include partial
		identifiers or the end of a comment. Make sure you include the entire
		multi-line comment etc. or it'll lose color.
		
		This calls oldRecolorRange to handle old-style syntax definitions.
   -------------------------------------------------------------------------- */// Prevent endless loop when recoloring's replacement of text causes processEditing to fire again.
// Don't like doing useless stuff.
// And don't like recoloring partially if a full recolorization is pending.
// Kludge fix for case where we sometimes exceed text length:ra
"Recoloring syntax in %@"// Get the text we'll be working with:
// Load colors and fonts to use from preferences:
		
		// Load our dictionary which contains info on coloring this language:
"Components"// No new-style list of components to colorize? Use old code.
		{
			#if TD_BACKWARDS_COMPATIBLE
#endif
// Loop over all available components:
"Type""Name""SyntaxColoring:Color:""Color""BlockComment""Start""End""OneLineComment""Start""String""Start""End""EscapeChar""Tag""Start""End""IgnoredComponent""Keywords""Keywords""SyntaxColoring:Keywords:""UserIdentifiers""Charset"// Replace the range with our recolored part:
/* -----------------------------------------------------------------------------
	oldRecolorRange:
		Try to apply syntax coloring to the text in our text view. This
		overwrites any styles the text may have had before. This function
		guarantees that it'll preserve the selection.
		
		Note that the order in which the different things are colorized is
		important. E.g. identifiers go first, followed by comments, since that
		way colors are removed from identifiers inside a comment and replaced
		with the comment color, etc. 
		
		The range passed in here is special, and may not include partial
		identifiers or the end of a comment. Make sure you include the entire
		multi-line comment etc. or it'll lose color.
		
		TODO: Anybody have any bright ideas how to refactor this?
   -------------------------------------------------------------------------- */
 
#if TD_BACKWARDS_COMPATIBLE
// Prevent endless loop when recoloring's replacement of text causes processEditing to fire again.
// Don't like doing useless stuff.
// And don't like recoloring partially if a full recolorization is pending.
"Recoloring syntax in %@"// Get the text we'll be working with:
// The following should probably be loaded from a dictionary in some file, to allow adaptation to various languages:
"BlockComment:Start""BlockComment:End""BlockComment2:Start""BlockComment2:End""OneLineComment:Start""Tag:Start""Tag:End""Tag:IgnoredStyle""String:EscapeChar""Identifiers:Charset"// Load colors and fonts to use from preferences:
"SyntaxColoring:Color:Preprocessor""SyntaxColoring:Color:Comments""SyntaxColoring:Color:Comments2""SyntaxColoring:Color:Strings""SyntaxColoring:Color:Identifiers""SyntaxColoring:Color:Identifiers2""SyntaxColoring:Color:Tags"// Color identifiers listed in "Identifiers" entry:
"Identifiers"// Color identifiers listed in "Identifiers2" entry:
"Identifiers2"// Colorize comments, strings etc, obliterating any identifiers inside them:
"\"" to: @"\""// Strings.
	
		// Colorize colorize any tags:
// Preprocessor directives:
"PreprocessorDirectives"// TODO Preprocessor directives should make sure they're at the start of a line, and that whitespace follows the directive.
		}
		
		// Comments:
// Replace the range with our recolored part:
// Restore selection.
#endif
 
 
/* -----------------------------------------------------------------------------
	textView:willChangeSelectionFromCharacterRange:toCharacterRange:
		Delegate method called when our selection changes. Updates our status
		display to indicate which characters are selected.
   -------------------------------------------------------------------------- */// Calc line number:
'\n''\r' )   // LF in CRLF sequence? Treat this as a single line break.
// Else fall through!
'\r'// Display info:
// Insertion mark!
"InsertionMark""char %u, line %u (char %u in document)"// Selection
"SelectionRange""char %u to %u, line %u (char %u to %u in document)"/* -----------------------------------------------------------------------------
	syntaxDefinitionFilename:
		Like windowNibName, this should return the name of the syntax
		definition file to use. Advanced users may use this to allow different
		coloring to take place depending on the file extension by returning
		different file names here.
		
		Note that the ".plist" extension is automatically appended to the file
		name.
   -------------------------------------------------------------------------- */"SyntaxDefinition";
}
 
 
/* -----------------------------------------------------------------------------
	syntaxDefinitionDictionary:
		This returns the syntax definition dictionary to use, which indicates
		what ranges of text to colorize. Advanced users may use this to allow
		different coloring to take place depending on the file extension by
		returning different dictionaries here.
		
		By default, this simply reads a dictionary from the .plist file
		indicated by -syntaxDefinitionFilename.
   -------------------------------------------------------------------------- */"plist"]];
}
 
 
/* -----------------------------------------------------------------------------
	colorStringsFrom:
		Apply syntax coloring to all strings. This is basically the same code
		as used for multi-line comments, except that it ignores the end
		character if it is preceded by a backslash.
   -------------------------------------------------------------------------- */'\\'// Look for start of string:
// Loop until we find end-of-string marker or our text to color is finished:
// Backslash before the end marker? That means ignore the end marker.
// A real one! Terminate loop.
// But skip this char before that.
// Now mess with the string's styles:
// Just ignore it, syntax coloring isn't that important.
/* -----------------------------------------------------------------------------
	colorCommentsFrom:
		Colorize block-comments in the text view.
	
	REVISIONS:
		2004-05-18  witness Documented.
   -------------------------------------------------------------------------- */// Look for start of multi-line comment:
// Look for associated end-of-comment marker:
/*NS_VOIDRETURN*/;  // Don't exit. If user forgot trailing marker, indicate this by "bleeding" until end of string.
// Now mess with the string's styles:
// Just ignore it, syntax coloring isn't that important.
/* -----------------------------------------------------------------------------
	colorOneLineComment:
		Colorize one-line-comments in the text view.
	
	REVISIONS:
		2004-05-18  witness Documented.
   -------------------------------------------------------------------------- */// Look for start of one-line comment:
// Look for associated line break:
"\n\r"// Now mess with the string's styles:
// Just ignore it, syntax coloring isn't that important.
/* -----------------------------------------------------------------------------
	colorIdentifier:
		Colorize keywords in the text view.
	
	REVISIONS:
		2004-05-18  witness Documented.
   -------------------------------------------------------------------------- */// Skip any leading whitespace chars, somehow NSScanner doesn't do that:
// Look for start of identifier:
// Check that we're not in the middle of an identifier:
			{
				// Alphanum character before identifier start?
// If charset is NIL, this evaluates to NO.
// Alphanum character following our identifier?
// If charset is NIL, this evaluates to NO.
// Now mess with the string's styles:
// Just ignore it, syntax coloring isn't that important.
/* -----------------------------------------------------------------------------
	colorTagFrom:
		Colorize HTML tags or similar constructs in the text view.
	
	REVISIONS:
		2004-05-18  witness Documented.
   -------------------------------------------------------------------------- */// Look for start of one-line comment:
// If start lies in range of ignored style, don't colorize it:
// Look for matching end marker:
// Scan up to the next occurence of the terminating sequence:
// Now, if the mode of the end marker is not the mode we were told to ignore,
				//  we're finished now and we can exit the inner loop:
// Also skip the terminating sequence.
// Otherwise we keep going, look for the next occurence of endCh and hope it isn't in that style.
// Now mess with the string's styles:
// Just ignore it, syntax coloring isn't that important.
/* -----------------------------------------------------------------------------
	defaultTextAttributes:
		Return the text attributes to use for the text in our text view.
	
	REVISIONS:
		2004-05-18  witness Documented.
   -------------------------------------------------------------------------- */

This code uses the PclZip Zip File reading code, which is subject to the GNU LGPL. It also uses the GeSHi syntax highlighter, subject to the GPL. Ask if you want this for your own web site, it's free.