|
| 1 | +#import "IDESyntaxHighlighter.h" |
| 2 | +#import <Cocoa/Cocoa.h> |
| 3 | +#import <EtoileFoundation/EtoileFoundation.h> |
| 4 | +#import "IDETextTypes.h" |
| 5 | + |
| 6 | +/** |
| 7 | + * Converts a clang source range into an NSRange within its enclosing file. |
| 8 | + */ |
| 9 | +// FIXME: This probably belongs somewhere else. |
| 10 | +NSRange NSRangeFromCXSourceRange(CXSourceRange sr) |
| 11 | +{ |
| 12 | + unsigned start, end; |
| 13 | + CXSourceLocation s = clang_getRangeStart(sr); |
| 14 | + CXSourceLocation e = clang_getRangeEnd(sr); |
| 15 | + clang_getInstantiationLocation(s, 0, 0, 0, &start); |
| 16 | + clang_getInstantiationLocation(e, 0, 0, 0, &end); |
| 17 | + NSRange r = {start, end - start}; |
| 18 | + return r; |
| 19 | +} |
| 20 | + |
| 21 | + |
| 22 | +@implementation IDESyntaxHighlighter |
| 23 | +- (id)init |
| 24 | +{ |
| 25 | + SUPERINIT; |
| 26 | + index = clang_createIndex(1, 1); |
| 27 | + // Options required to compile GNUstep apps |
| 28 | + // FIXME: These should be read in from a plist or something equally |
| 29 | + // (approximately) sensible. |
| 30 | + args = [A( |
| 31 | + @"-DGNUSTEP", |
| 32 | + @"-DGNUSTEP_BASE_LIBRARY=1", |
| 33 | + @"-DGNU_GUI_LIBRARY=1", |
| 34 | + @"-DGNU_RUNTIME=1", |
| 35 | + @"-D_NATIVE_OBJC_EXCEPTIONS", |
| 36 | + @"-DGSWARN", |
| 37 | + @"-DGSDIAGNOSE", |
| 38 | + @"-fno-strict-aliasing", |
| 39 | + @"-fobjc-nonfragile-abi", |
| 40 | + @"-fexceptions", |
| 41 | + @"-Wall", |
| 42 | + @"-fgnu-runtime", |
| 43 | + @"-fconstant-string-class=NSConstantString") mutableCopy]; |
| 44 | + return self; |
| 45 | +} |
| 46 | +- (void)addIncludePath: (NSString*)includePath |
| 47 | +{ |
| 48 | + [args addObject: [NSString stringWithFormat: @"-I%@", includePath]]; |
| 49 | +} |
| 50 | + |
| 51 | +- (void)dealloc |
| 52 | +{ |
| 53 | + if (NULL != translationUnit) |
| 54 | + { |
| 55 | + clang_disposeTranslationUnit(translationUnit); |
| 56 | + } |
| 57 | + clang_disposeIndex(index); |
| 58 | + [super dealloc]; |
| 59 | +} |
| 60 | + |
| 61 | +- (void)reparse |
| 62 | +{ |
| 63 | + const char *fn = [fileName UTF8String]; |
| 64 | + struct CXUnsavedFile unsaved = { |
| 65 | + fn, [[source string] UTF8String], [source length] }; |
| 66 | + if (NULL == translationUnit) |
| 67 | + { |
| 68 | + unsigned argc = [args count]; |
| 69 | + const char *argv[argc]; |
| 70 | + int i=0; |
| 71 | + for (NSString *arg in args) |
| 72 | + { |
| 73 | + argv[i++] = [arg UTF8String]; |
| 74 | + } |
| 75 | + translationUnit = |
| 76 | + //clang_createTranslationUnitFromSourceFile(index, fn, argc, argv, 1, &unsaved); |
| 77 | + clang_createTranslationUnitFromSourceFile(index, fn, argc, argv, 1, &unsaved); |
| 78 | + file = clang_getFile(translationUnit, fn); |
| 79 | + [self syntaxHighlightFile]; |
| 80 | + [self convertSemanticToPresentationMarkup]; |
| 81 | + } |
| 82 | + else |
| 83 | + { |
| 84 | + clang_reparseTranslationUnit(translationUnit, 1, &unsaved, 0); |
| 85 | + } |
| 86 | +} |
| 87 | +- (void)lexicalHighlightFile |
| 88 | +{ |
| 89 | + CXSourceLocation start = clang_getLocation(translationUnit, file, 0, 0); |
| 90 | + CXSourceLocation end = clang_getLocation(translationUnit, file, -1, -1); |
| 91 | + [self highlightRange: clang_getRange(start, end) syntax: NO]; |
| 92 | +} |
| 93 | + |
| 94 | +- (void)highlightRange: (CXSourceRange)r syntax: (BOOL)highightSyntax; |
| 95 | +{ |
| 96 | + NSString *TokenTypes[] = {IDETextTokenTypePunctuation, IDETextTokenTypeKeyword, |
| 97 | + IDETextTokenTypeIdentifier, IDETextTokenTypeLiteral, |
| 98 | + IDETextTokenTypeComment}; |
| 99 | + if (clang_equalLocations(clang_getRangeStart(r), clang_getRangeEnd(r))) |
| 100 | + { |
| 101 | + return; |
| 102 | + } |
| 103 | + CXToken *tokens; |
| 104 | + unsigned tokenCount; |
| 105 | + clang_tokenize(translationUnit, r , &tokens, &tokenCount); |
| 106 | + if (tokenCount > 0) |
| 107 | + { |
| 108 | + CXCursor *cursors = NULL; |
| 109 | + if (highightSyntax) |
| 110 | + { |
| 111 | + cursors = calloc(sizeof(CXCursor), tokenCount); |
| 112 | + clang_annotateTokens(translationUnit, tokens, tokenCount, cursors); |
| 113 | + } |
| 114 | + for (unsigned i=0 ; i<tokenCount ; i++) |
| 115 | + { |
| 116 | + CXSourceRange sr = clang_getTokenExtent(translationUnit, tokens[i]); |
| 117 | + NSRange range = NSRangeFromCXSourceRange(sr); |
| 118 | + if (range.location > 0) |
| 119 | + { |
| 120 | + if ([[source string] characterAtIndex: range.location - 1] == '@') |
| 121 | + { |
| 122 | + range.location--; |
| 123 | + range.length++; |
| 124 | + } |
| 125 | + } |
| 126 | + if (highightSyntax) |
| 127 | + { |
| 128 | + id type; |
| 129 | + switch (cursors[i].kind) |
| 130 | + { |
| 131 | + case CXCursor_FirstRef... CXCursor_LastRef: |
| 132 | + type = IDETextTypeReference; |
| 133 | + break; |
| 134 | + case CXCursor_MacroDefinition: |
| 135 | + type = IDETextTypeMacroDefinition; |
| 136 | + break; |
| 137 | + case CXCursor_MacroInstantiation: |
| 138 | + type = IDETextTypeMacroInstantiation; |
| 139 | + break; |
| 140 | + case CXCursor_FirstDecl...CXCursor_LastDecl: |
| 141 | + type = IDETextTypeDeclaration; |
| 142 | + break; |
| 143 | + case CXCursor_ObjCMessageExpr: |
| 144 | + type = IDETextTypeMessageSend; |
| 145 | + break; |
| 146 | + case CXCursor_DeclRefExpr: |
| 147 | + type = IDETextTypeDeclRef; |
| 148 | + break; |
| 149 | + case CXCursor_PreprocessingDirective: |
| 150 | + type = IDETextTypePreprocessorDirective; |
| 151 | + break; |
| 152 | + default: |
| 153 | + type = nil; |
| 154 | + } |
| 155 | + if (nil != type) |
| 156 | + { |
| 157 | + [source addAttribute: kIDETextSemanticType |
| 158 | + value: type |
| 159 | + range: range]; |
| 160 | + } |
| 161 | + } |
| 162 | + [source addAttribute: kIDETextTokenType |
| 163 | + value: TokenTypes[clang_getTokenKind(tokens[i])] |
| 164 | + range: range]; |
| 165 | + } |
| 166 | + clang_disposeTokens(translationUnit, tokens, tokenCount); |
| 167 | + free(cursors); |
| 168 | + } |
| 169 | +} |
| 170 | +- (void)convertSemanticToPresentationMarkup |
| 171 | +{ |
| 172 | + NSUInteger end = [source length]; |
| 173 | + NSUInteger i = 0; |
| 174 | + NSRange r; |
| 175 | + NSDictionary *noAttributes = [NSDictionary dictionary]; |
| 176 | + NSDictionary *comment = D([NSColor grayColor], NSForegroundColorAttributeName); |
| 177 | + NSDictionary *keyword = D([NSColor redColor], NSForegroundColorAttributeName); |
| 178 | + NSDictionary *literal = D([NSColor redColor], NSForegroundColorAttributeName); |
| 179 | + NSDictionary *tokenAttributes = D( |
| 180 | + comment, IDETextTokenTypeComment, |
| 181 | + noAttributes, IDETextTokenTypePunctuation, |
| 182 | + keyword, IDETextTokenTypeKeyword, |
| 183 | + literal, IDETextTokenTypeLiteral); |
| 184 | + |
| 185 | + NSDictionary *semanticAttributes = D( |
| 186 | + D([NSColor blueColor], NSForegroundColorAttributeName), IDETextTypeDeclRef, |
| 187 | + D([NSColor brownColor], NSForegroundColorAttributeName), IDETextTypeMessageSend, |
| 188 | + //D([NSColor greenColor], NSForegroundColorAttributeName), IDETextTypeDeclaration, |
| 189 | + D([NSColor magentaColor], NSForegroundColorAttributeName), IDETextTypeMacroInstantiation, |
| 190 | + D([NSColor magentaColor], NSForegroundColorAttributeName), IDETextTypeMacroDefinition, |
| 191 | + D([NSColor orangeColor], NSForegroundColorAttributeName), IDETextTypePreprocessorDirective, |
| 192 | + D([NSColor purpleColor], NSForegroundColorAttributeName), IDETextTypeReference); |
| 193 | + |
| 194 | + do |
| 195 | + { |
| 196 | + NSDictionary *attrs = [source attributesAtIndex: i |
| 197 | + longestEffectiveRange: &r |
| 198 | + inRange: NSMakeRange(i, end-i)]; |
| 199 | + NSString *token = [attrs objectForKey: kIDETextTokenType]; |
| 200 | + NSString *semantic = [attrs objectForKey: kIDETextSemanticType]; |
| 201 | + if (semantic == IDETextTypePreprocessorDirective) |
| 202 | + { |
| 203 | + attrs = [semanticAttributes objectForKey: semantic]; |
| 204 | + } |
| 205 | + else if (token == nil || token != IDETextTokenTypeIdentifier) |
| 206 | + { |
| 207 | + attrs = [tokenAttributes objectForKey: token]; |
| 208 | + } |
| 209 | + else |
| 210 | + { |
| 211 | + NSString *semantic = [attrs objectForKey: kIDETextSemanticType]; |
| 212 | + attrs = [semanticAttributes objectForKey: semantic]; |
| 213 | + //NSLog(@"Applying semantic attributes: %@", semantic); |
| 214 | + } |
| 215 | + if (nil == attrs) |
| 216 | + { |
| 217 | + attrs = noAttributes; |
| 218 | + } |
| 219 | + [source setAttributes: attrs |
| 220 | + range: r]; |
| 221 | + i = r.location + r.length; |
| 222 | + } while (i < end); |
| 223 | +} |
| 224 | +- (void)syntaxHighlightFile |
| 225 | +{ |
| 226 | + CXSourceLocation start = clang_getLocation(translationUnit, file, 0, 0); |
| 227 | + CXSourceLocation end = clang_getLocation(translationUnit, file, -1, -1); |
| 228 | + [self highlightRange: clang_getRange(start, end) syntax: YES]; |
| 229 | +} |
| 230 | +@end |
| 231 | + |
0 commit comments