Skip to content

Commit 03a406d

Browse files
committed
Initial commit of IDEKit. Currently only supports [Obj]C[++] syntax highlighting (via clang).
git-svn-id: http://svn.gna.org/svn/etoile/trunk/Etoile/Languages/IDEKit@6429 7b3f36aa-e6db-49c1-8b4c-66eac1997e64
0 parents  commit 03a406d

File tree

5 files changed

+389
-0
lines changed

5 files changed

+389
-0
lines changed

GNUmakefile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
include $(GNUSTEP_MAKEFILES)/common.make
2+
3+
# We reset PROJECT_DIR provided by etoile.make to match the subproject since
4+
# etoile.make doesn't detect and handle such embedded project
5+
PROJECT_DIR = $(CURDIR)
6+
7+
#
8+
# Library
9+
#
10+
VERSION = 0.1
11+
FRAMEWORK_NAME = IDEKit
12+
13+
${FRAMEWORK_NAME}_OBJC_FILES = \
14+
IDESyntaxHighlighter.m\
15+
IDETextTypes.m
16+
17+
${FRAMEWORK_NAME}_HEADER_FILES = \
18+
IDESyntaxHighlighter.h\
19+
IDETextTypes.h
20+
21+
${FRAMEWORK_NAME}_OBJCFLAGS = -fobjc-nonfragile-abi
22+
${FRAMEWORK_NAME}_CPPFLAGS = -I`llvm-config --src-root`/tools/clang/include/
23+
${FRAMEWORK_NAME}_LDFLAGS = -L`llvm-config --libdir` -lclang
24+
25+
CC=clang
26+
27+
include $(GNUSTEP_MAKEFILES)/framework.make
28+
-include ../../etoile.make
29+
#-include ../../documentation.make

IDESyntaxHighlighter.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include <Foundation/NSObject.h>
2+
#include <Foundation/NSGeometry.h>
3+
#include <clang-c/Index.h>
4+
5+
@class NSMutableArray;
6+
@class NSMutableAttributedString;
7+
8+
/**
9+
* The IDESyntaxHighlighter class is responsible for performing lexical and
10+
* syntax highlighting on a single source file. Highlighting involves three steps:
11+
*
12+
* 1) Lexical markup.
13+
* 2) Syntax markup.
14+
* 3) Presentation markup.
15+
*
16+
* Lexical highlighting is faster than full syntax highlighting, so it can be
17+
* used more frequently in an editor. For example, you might run the lexical
18+
* highlighter after every key press but defer the syntax highlighter until
19+
* after a whitespace character had been entered.
20+
*
21+
* The third step is optional. If you are not using AppKit, then you can
22+
* ignore it and handle the presentation yourself. This is useful, for
23+
* example, when generating semantic HTML from a source file.
24+
*/
25+
@interface IDESyntaxHighlighter : NSObject
26+
{
27+
CXIndex index;
28+
NSMutableArray *args;
29+
CXFile file;
30+
CXTranslationUnit translationUnit;
31+
}
32+
/**
33+
* Text storage object representing the source file.
34+
*/
35+
@property (retain, nonatomic) NSMutableAttributedString *source;
36+
/**
37+
* Name of this source file.
38+
*/
39+
@property (retain, nonatomic) NSString *fileName;
40+
/**
41+
* Parses the contents of the file. Must be called before reapplying
42+
* highlighting after the file has changed.
43+
*/
44+
- (void)reparse;
45+
/**
46+
* Perform lexical highlighting on a specified source range.
47+
*/
48+
- (void)highlightRange: (CXSourceRange)r syntax: (BOOL)highightSyntax;
49+
/**
50+
* Performs lexical highlighting on the entire file.
51+
*/
52+
- (void)lexicalHighlightFile;
53+
/**
54+
* Perform syntax highlighting on the file.
55+
*/
56+
- (void)syntaxHighlightFile;
57+
/**
58+
* Convert the semantic markup into presentation markup in the attributed
59+
* string.
60+
*/
61+
- (void)convertSemanticToPresentationMarkup;
62+
/**
63+
* Adds an include path to search when performing syntax highlighting.
64+
*/
65+
- (void)addIncludePath: (NSString*)includePath;
66+
@end

IDESyntaxHighlighter.m

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
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+

IDETextTypes.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#import <EtoileFoundation/Macros.h>
2+
3+
@class NSString;
4+
5+
/**
6+
* The type of the token. This key indicates the type that lexical analysis
7+
* records for this token.
8+
*/
9+
EMIT_STRING(kIDETextTokenType);
10+
/**
11+
* Token is punctuation.
12+
*/
13+
EMIT_STRING(IDETextTokenTypePunctuation);
14+
/**
15+
* Token is a keyword.
16+
*/
17+
EMIT_STRING(IDETextTokenTypeKeyword);
18+
/**
19+
* Token is an identifier.
20+
*/
21+
EMIT_STRING(IDETextTokenTypeIdentifier);
22+
/**
23+
* Token is a literal value.
24+
*/
25+
EMIT_STRING(IDETextTokenTypeLiteral);
26+
/**
27+
* Token is a comment.
28+
*/
29+
EMIT_STRING(IDETextTokenTypeComment);
30+
/**
31+
* The type that semantic analysis records for this
32+
*/
33+
EMIT_STRING(kIDETextSemanticType);
34+
/**
35+
* Reference to a type declared elsewhere.
36+
*/
37+
EMIT_STRING(IDETextTypeReference);
38+
/**
39+
* Instantiation of a macro.
40+
*/
41+
EMIT_STRING(IDETextTypeMacroInstantiation);
42+
/**
43+
* Definition of a macro.
44+
*/
45+
EMIT_STRING(IDETextTypeMacroDefinition);
46+
/**
47+
* A declaration.
48+
*/
49+
EMIT_STRING(IDETextTypeDeclaration);
50+
/**
51+
* A message send expression.
52+
*/
53+
EMIT_STRING(IDETextTypeMessageSend);
54+
/**
55+
* A reference to a declaration.
56+
*/
57+
EMIT_STRING(IDETextTypeDeclRef);
58+
/**
59+
* A preprocessor directive, such as #import or #include.
60+
*/
61+
EMIT_STRING(IDETextTypePreprocessorDirective);

IDETextTypes.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#define DEFINE_STRINGS
2+
#include "IDETextTypes.h"

0 commit comments

Comments
 (0)