@implementation XMLTextView
-- (id)init {
- if (self = [super init]) {
- [self setRichText:NO];
- }
-
- return self;
+-(void)awakeFromNib {
+ [self setRichText:NO];
+ resultstack = NULL;
+ defaults = [NSUserDefaults standardUserDefaults];
+
+ // register our two input text views to receive file drags
+ //
+ // [xmlView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
+ // [xsltView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
+
+ NSFont *computerFont = [NSFont fontWithName:@"Courier" size:12.0];
+ [self setFont:computerFont];
+
+ [self setAllowsUndo:YES];
+
}
+- (void)dealloc {
+
+ if (resultstack)
+ free(resultstack);
+ [errorString release];
+}
-(void)selectLineByNumber:(int)line {
--(void)pasteAsRichText:(id)sender {
-
-
- NSLog(@"paste as rich text");
- // [self pasteAsPlainText:sender];
-
-}
-
-
- (void)keyDown:(NSEvent *)event {
NSRange selectedRange;
+ if (![defaults boolForKey:@"enableSyntaxAnalysis"]) {
+ [super keyDown:event];
+ return;
+ }
+
if ([[event characters] isEqual:@"\033"]) {
if ([event modifierFlags] || [event isARepeat]) {
}
}
+
+ /*
+ if ([event modifierFlags] & NSFunctionKeyMask) {
+ [self didChangeText];
+ }
+ */
+
+ //NSFunctionKeyMask
+
+
[super keyDown:event];
}
- (BOOL)completeAfterSlash {
- NSRange selectedRange = [self selectedRange], tagNameRange;
- NSString *tagName = nil;
-
- tagNameRange = [self scanBackwardsForOpeningTagNameInRange:NSMakeRange(0, selectedRange.location - 1)];
-
- if (tagNameRange.location == NSNotFound) {
+ int location = [self selectedRange].location - 1;
+
+ [self calculateTagStackAtLocation:location];
+
+ if (stackresult) {
return NO;
}
- tagName = [[self string] substringWithRange:tagNameRange];
-
-// NSLog(@"completion!");
+ NSTextStorage *storage = [self textStorage];
+ [storage beginEditing];
+ [storage deleteCharactersInRange:NSMakeRange(location, 1)];
+ [storage endEditing];
- if (tagName == nil) {
- return NO;
- }
+ [self setSelectedRange:NSMakeRange(location, 0)];
- [self insertText:[NSString stringWithFormat:@"/%@>", tagName]];
+ [self complete:nil];
- [self flashRange:tagNameRange];
-
+
return YES;
}
+
- (void)flashRange:(NSRange)range {
NSRect tagNameRect;
}
+-(NSString *)calculateTagStack {
+ return [self calculateTagStackAtLocation:[self selectedRange].location];
+
+}
-- (void)complete:(id)sender {
-
- NSRange selectedRange, leftRange, rightRange, tagNameRange;
- NSString *tagName = nil;
- NSString *data = nil;
- int location;
+-(BOOL)checkWellFormed {
- data = [self string];
+ int result;
+ NSData *data = [XMLUtils getDataWithEncodingFromString:[self string]];
- selectedRange = [self selectedRange];
- location = selectedRange.location;
+ parser = XML_ParserCreate(NULL);
- /* May not have a selection, and insertion point must be preceded by at
- * least one tag, which means at least 3 characters must be to its left
- */
- if (selectedRange.length > 0 || location < 3) {
- NSBeep();
- return;
+ if (!parser) {
+ NSLog(@"Unable to allocate expat parser in XMLTextView:checkWellFormed!");
+ return NO;
}
- /* leftRange is everything from beginning of data to the insertion point.
- * rightRange is everything after the insertion point to the end of the data
- */
- leftRange = NSMakeRange(0, location);
- rightRange = NSMakeRange(location, [data length] - leftRange.length);
-
+ result = XML_Parse(parser, [data bytes], [data length], 1);
-
- tagNameRange = [self scanBackwardsForOpeningTagNameInRange:leftRange];
-
- if (tagNameRange.location == NSNotFound) {
- NSBeep();
- return;
+ if (!result) {
+ [self setError:[NSString stringWithFormat:@"%s, line %d, column %d", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser), XML_GetCurrentColumnNumber(parser)] atLine:XML_GetCurrentLineNumber(parser) atColumn:XML_GetCurrentColumnNumber(parser)];
+ } else if (error) {
+ [self clearError];
}
-
- tagName = [data substringWithRange:tagNameRange];
- [self insertText:[NSString stringWithFormat:@"</%@>", tagName]];
- [self setSelectedRange:selectedRange];
-
- [self flashRange:tagNameRange];
+ XML_ParserFree(parser);
+ return result > 0;
}
--(NSRange)scanBackwardsForOpeningTagNameInRange:(NSRange)scanRange {
-
- NSRange tagNameEndRange, tagContentPlusEndRange, leftAngleRange, openingTagRange, slashRange;
- NSCharacterSet *angleSet = [NSCharacterSet characterSetWithCharactersInString:@"<>"];
- NSCharacterSet *tagNameDelimiterSet = [NSCharacterSet characterSetWithCharactersInString:@" <>"];
- NSString *tagName = nil;
- NSString *data = [self string];
- NSString *character;
- NSRange tagNameRange;
-
+-(void)clearError {
- /* find the first occurence of angle brackets, both opening or closing,
- * in the range to the left of the insertion point, searching backwards
- * from the insertion point
- */
- leftAngleRange = [data rangeOfCharacterFromSet:angleSet options:NSBackwardsSearch range:scanRange];
+ errorLine = errorColumn = 0;
+ error = NO;
+ [errorString release];
+ errorString = @"";
+
+}
- /* abort unless we found something */
- if (leftAngleRange.location == NSNotFound) {
- return NSMakeRange(NSNotFound, 0);
- }
+-(BOOL)hasError {
+
+ return error;
+
+}
+-(void)setError:(NSString *)errstring atLine:(int)line atColumn:(int)column {
+
+ errorLine = line;
+ errorColumn = column;
+ error = YES;
+ [errstring retain];
+ [errorString release];
+ errorString = errstring;
+
+}
- // NSLog(@"leftbracket: %d %d", leftAngleRange.location, leftAngleRange.length);
- /* We expect the nearest angle bracket to our left to be a closing one, we will
- * not try to complete an open tag if we find an opening bracket instead.
- */
- character = [data substringWithRange:leftAngleRange];
- if (![character isEqual:@">"]) {
- return NSMakeRange(NSNotFound, 0);
- }
- /* shift end of scanRange so it ends after the closing angle bracket we just found */
- scanRange = NSMakeRange(0, leftAngleRange.location);
- /* Now search again from the end of that range backward to the beginning of the data
- * and look for another angle bracket.
- */
- leftAngleRange = [data rangeOfCharacterFromSet:angleSet options:NSBackwardsSearch range:NSMakeRange(0, scanRange.length - 1)];
- /* again abort unless we found something */
- if (leftAngleRange.location == NSNotFound) {
- return NSMakeRange(NSNotFound, 0);
- }
+-(NSString *)calculateTagStackAtLocation:(int)location {
- //NSLog(@"leftbracket: %d %d", leftAngleRange.location, leftAngleRange.length);
+ const char *buffer;
+ int i;
+ NSRange selectedRange;
+ NSMutableString *mystack;
- /* This time we expect to see an opening angle bracket */
- character = [data substringWithRange:leftAngleRange];
- if (![character isEqual:@"<"]) {
- return NSMakeRange(NSNotFound, 0);
+ if (![[NSUserDefaults standardUserDefaults] boolForKey:@"enableSyntaxAnalysis"]) {
+ return @"";
}
+
+ buffer = [[self string] lossyCString];
+ selectedRange = [self selectedRange];
+
+ if (resultstack)
+ free(resultstack);
+ stackresult = 0;
+ resultstack = findCompletion(buffer, strlen(buffer), location, &stackresult, tagpositions);
- /* This will indicate the range of the entire nearest tag to our left,
- * we assume that's the opening tag */
- openingTagRange = NSMakeRange(leftAngleRange.location, (scanRange.length - leftAngleRange.location) + 1);
+ mystack = [NSMutableString stringWithCapacity:1000];
+
+ for (i = 0; resultstack && *(resultstack[i]); i++) {
+ if (*(resultstack[i+1])) {
+ [mystack appendFormat:@"%s/", resultstack[i]];
+ } else {
+ if (stackresult) {
+ [mystack appendFormat:@"%s", resultstack[i]];
+ } else {
+ [mystack appendFormat:@"<%s>", resultstack[i]];
+ }
+
+
+ }
- /* check if it is indeed an opening tag, i.e. no slash after the opening bracket */
- slashRange = NSMakeRange(openingTagRange.location + 1, 1);
- character = [data substringWithRange:slashRange];
- if ([character isEqual:@"/"]) {
- return NSMakeRange(NSNotFound, 0);
}
- /* Don't complete processing instructions, comments etc. */
- if ([character isEqual:@"?"] || [character isEqual:@"!"]) {
- return NSMakeRange(NSNotFound, 0);
+
+ switch (stackresult) {
+
+ case 1:
+ [mystack appendString:@" (In tag)"];
+ break;
+
+ case 3:
+ [mystack appendString:@" (In comment section)"];
+ break;
+ case 5:
+ [mystack appendString:@" (In CDATA section)"];
+ break;
+
+ case 8:
+ [mystack appendString:@" (No open tags)"];
+ break;
+
+ case 16:
+ [mystack appendString:@" (Tags are balanced)"];
+ break;
}
- /* check if it is a merged start and end tag, i.e. slash before closing bracket */
- slashRange = NSMakeRange((openingTagRange.location + openingTagRange.length) - 2, 1);
- character = [data substringWithRange:slashRange];
- if ([character isEqual:@"/"]) {
- return NSMakeRange(NSNotFound, 0);
- }
-
-
- tagContentPlusEndRange = NSMakeRange(openingTagRange.location + 1, openingTagRange.length - 1);
-
- //NSLog(@"tagcontent: %d %d", tagContentPlusEndRange.location, tagContentPlusEndRange.length);
+ return mystack;
+
+}
- tagNameEndRange = [data rangeOfCharacterFromSet:tagNameDelimiterSet options:0 range:tagContentPlusEndRange];
+- (void)complete:(id)sender {
+ NSRange selectedRange, tagNameRange;
+ NSString *tagName = nil;
+ int location, i;
- tagNameRange = NSMakeRange(tagContentPlusEndRange.location, tagNameEndRange.location - tagContentPlusEndRange.location);
+// data = [self string];
- tagName = [data substringWithRange:tagNameRange];
+ selectedRange = [self selectedRange];
+ location = selectedRange.location;
- if ([tagName length] < 1) {
- return NSMakeRange(NSNotFound, 0);
+ /* May not have a selection, and insertion point must be preceded by at
+ * least one tag, which means at least 3 characters must be to its left
+ */
+ if (selectedRange.length > 0 || location < 3 || stackresult) {
+ NSBeep();
+ return;
}
- return tagNameRange;
-
- //NSLog(@"tagrange: %d %d / %@", tagNameEndRange.location, tagNameEndRange.length, tagName);
-
+ for (i = 0; resultstack && *(resultstack[i]); i++) {
+ if (!*(resultstack[i+1])) {
+ tagName = [NSString stringWithCString:resultstack[i]];
+ tagNameRange = NSMakeRange(tagpositions[i*2], tagpositions[i*2+1]- tagpositions[i*2]);
+ }
+ }
+ [self insertText:[NSString stringWithFormat:@"</%@>", tagName]];
+ [self setSelectedRange:selectedRange];
-
-
-
+ [self flashRange:tagNameRange];
+ [self calculateTagStack];
+
}
+
@end