diff --git a/AUTHORS b/AUTHORS index fdd16be..0fdbeb4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,4 +2,5 @@ ajwillia.ms (Andy Williams) With sections from Ecrire by Tom Hacohen +Clang syntax higlighting from cedric and TAsn in the edi PROTO project diff --git a/configure.ac b/configure.ac index f3912bf..b3cf135 100644 --- a/configure.ac +++ b/configure.ac @@ -46,6 +46,30 @@ AC_ARG_WITH([tests], [build_tests=${withval}], [build_tests=auto]) + +HAVE_LIBCLANG=0 + +AC_ARG_ENABLE(libclang, + [ --disable-libclang Disable use of libclang.],,) + +build_clang="no" +if test "$enable_libclang" != "no"; then + AC_CHECK_HEADERS("clang-c/Index.h") + if test "$ac_cv_header_clang_c_Index_h" = "yes"; then + AC_CHECK_LIB(clang, clang_Cursor_getArgument) + if test "$ac_cv_lib_clang_clang_Cursor_getArgument" = "yes"; then + HAVE_LIBCLANG=1; + MORE_LIBS="$MORE_LIBS -lclang" + build_clang="yes" + fi + fi + if test "$HAVE_LIBCLANG" = "0"; then + AC_MSG_WARN([Could not find a usable libclang. Please install libclang to enable syntax highlighting features.]) + fi +fi + +AC_SUBST(HAVE_LIBCLANG) + EFL_TESTS([${build_tests}]) # doxygen program for documentation building @@ -90,6 +114,7 @@ echo echo "Compilation..............: make (or gmake)" echo " CFLAGS.................: $CFLAGS" echo " edje_cc................: ${edje_cc}" +echo " clang..................: ${build_clang}" echo echo "Building documentation...: ${build_doc}" echo "Building tests...........: ${have_tests}" diff --git a/src/bin/editor/clang_debug.h b/src/bin/editor/clang_debug.h new file mode 100644 index 0000000..7a9e6c6 --- /dev/null +++ b/src/bin/editor/clang_debug.h @@ -0,0 +1,198 @@ +#ifndef _CLANG_DEBUG_H +#define _CLANG_DEBUG_H +#include + +static void PrintExtent(FILE *out, unsigned begin_line, unsigned begin_column, + unsigned end_line, unsigned end_column) { + fprintf(out, "[%d:%d - %d:%d]", begin_line, begin_column, + end_line, end_column); +} + +static void PrintRange(CXSourceRange R, const char *str) { + CXFile begin_file, end_file; + unsigned begin_line, begin_column, end_line, end_column; + + clang_getSpellingLocation(clang_getRangeStart(R), + &begin_file, &begin_line, &begin_column, 0); + clang_getSpellingLocation(clang_getRangeEnd(R), + &end_file, &end_line, &end_column, 0); + if (!begin_file || !end_file) + return; + + if (str) + printf(" %s=", str); + PrintExtent(stdout, begin_line, begin_column, end_line, end_column); +} + +void PrintToken(CXTranslationUnit tx, CXToken token) +{ + CXString str = clang_getTokenSpelling(tx, token); + PrintRange(clang_getTokenExtent(tx, token), clang_getCString(str)); + clang_disposeString(str); +} + +int want_display_name = 0; + +void PrintCursor(CXCursor Cursor) { + CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor); + if (clang_isInvalid(Cursor.kind)) { + CXString ks = clang_getCursorKindSpelling(Cursor.kind); + printf("Invalid Cursor => %s", clang_getCString(ks)); + clang_disposeString(ks); + } + else { + CXString string, ks; + CXCursor Referenced; + unsigned line, column; + CXCursor SpecializationOf; + CXCursor *overridden; + unsigned num_overridden; + unsigned RefNameRangeNr; + CXSourceRange CursorExtent; + CXSourceRange RefNameRange; + + ks = clang_getCursorKindSpelling(Cursor.kind); + string = want_display_name? clang_getCursorDisplayName(Cursor) + : clang_getCursorSpelling(Cursor); + printf("%s=%s", clang_getCString(ks), + clang_getCString(string)); + clang_disposeString(ks); + clang_disposeString(string); + + Referenced = clang_getCursorReferenced(Cursor); + if (!clang_equalCursors(Referenced, clang_getNullCursor())) { + if (clang_getCursorKind(Referenced) == CXCursor_OverloadedDeclRef) { + unsigned I, N = clang_getNumOverloadedDecls(Referenced); + printf("["); + for (I = 0; I != N; ++I) { + CXCursor Ovl = clang_getOverloadedDecl(Referenced, I); + CXSourceLocation Loc; + if (I) + printf(", "); + + Loc = clang_getCursorLocation(Ovl); + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + printf("%d:%d", line, column); + } + printf("]"); + } else { + CXFile cfile; + CXSourceLocation Loc = clang_getCursorLocation(Referenced); + clang_getSpellingLocation(Loc, &cfile, &line, &column, 0); + CXString str = clang_getFileName(cfile); + printf(":%s:%d:%d", clang_getCString(str), line, column); + clang_disposeString(str); + } + } + + if (clang_isCursorDefinition(Cursor)) + printf(" (Definition)"); + + switch (clang_getCursorAvailability(Cursor)) { + case CXAvailability_Available: + break; + + case CXAvailability_Deprecated: + printf(" (deprecated)"); + break; + + case CXAvailability_NotAvailable: + printf(" (unavailable)"); + break; + + case CXAvailability_NotAccessible: + printf(" (inaccessible)"); + break; + } + + if (clang_CXXMethod_isStatic(Cursor)) + printf(" (static)"); + if (clang_CXXMethod_isVirtual(Cursor)) + printf(" (virtual)"); + + if (Cursor.kind == CXCursor_IBOutletCollectionAttr) { + CXType T = + clang_getCanonicalType(clang_getIBOutletCollectionType(Cursor)); + CXString S = clang_getTypeKindSpelling(T.kind); + printf(" [IBOutletCollection=%s]", clang_getCString(S)); + clang_disposeString(S); + } + + if (Cursor.kind == CXCursor_CXXBaseSpecifier) { + enum CX_CXXAccessSpecifier access = clang_getCXXAccessSpecifier(Cursor); + unsigned isVirtual = clang_isVirtualBase(Cursor); + const char *accessStr = 0; + + switch (access) { + case CX_CXXInvalidAccessSpecifier: + accessStr = "invalid"; break; + case CX_CXXPublic: + accessStr = "public"; break; + case CX_CXXProtected: + accessStr = "protected"; break; + case CX_CXXPrivate: + accessStr = "private"; break; + } + + printf(" [access=%s isVirtual=%s]", accessStr, + isVirtual ? "true" : "false"); + } + + SpecializationOf = clang_getSpecializedCursorTemplate(Cursor); + if (!clang_equalCursors(SpecializationOf, clang_getNullCursor())) { + CXSourceLocation Loc = clang_getCursorLocation(SpecializationOf); + CXString Name = clang_getCursorSpelling(SpecializationOf); + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + printf(" [Specialization of %s:%d:%d]", + clang_getCString(Name), line, column); + clang_disposeString(Name); + } + + clang_getOverriddenCursors(Cursor, &overridden, &num_overridden); + if (num_overridden) { + unsigned I; + printf(" [Overrides "); + for (I = 0; I != num_overridden; ++I) { + CXSourceLocation Loc = clang_getCursorLocation(overridden[I]); + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + if (I) + printf(", "); + printf("@%d:%d", line, column); + } + printf("]"); + clang_disposeOverriddenCursors(overridden); + } + + if (Cursor.kind == CXCursor_InclusionDirective) { + CXFile File = clang_getIncludedFile(Cursor); + CXString Included = clang_getFileName(File); + printf(" (%s)", clang_getCString(Included)); + clang_disposeString(Included); + + if (clang_isFileMultipleIncludeGuarded(TU, File)) + printf(" [multi-include guarded]"); + } + + CursorExtent = clang_getCursorExtent(Cursor); + RefNameRange = clang_getCursorReferenceNameRange(Cursor, + CXNameRange_WantQualifier + | CXNameRange_WantSinglePiece + | CXNameRange_WantTemplateArgs, + 0); + if (!clang_equalRanges(CursorExtent, RefNameRange)) + PrintRange(RefNameRange, "SingleRefName"); + + for (RefNameRangeNr = 0; 1; RefNameRangeNr++) { + RefNameRange = clang_getCursorReferenceNameRange(Cursor, + CXNameRange_WantQualifier + | CXNameRange_WantTemplateArgs, + RefNameRangeNr); + if (clang_equalRanges(clang_getNullRange(), RefNameRange)) + break; + if (!clang_equalRanges(CursorExtent, RefNameRange)) + PrintRange(RefNameRange, "RefName"); + } + } +} +#endif + diff --git a/src/bin/editor/edi_editor.c b/src/bin/editor/edi_editor.c index 23ee567..36699c4 100644 --- a/src/bin/editor/edi_editor.c +++ b/src/bin/editor/edi_editor.c @@ -9,12 +9,31 @@ #include "edi_editor.h" +#define CLANG_DEBUG 0 +#if CLANG_DEBUG +#include "clang_debug.h" +#endif + #include "mainview/edi_mainview.h" #include "edi_private.h" #define EDITOR_FONT "DEFAULT='font=Monospace font_size=12'" +#define Edi_Color const char * + +typedef struct +{ + unsigned int line; + unsigned int col; +} Edi_Location; + +typedef struct +{ + Edi_Location start; + Edi_Location end; +} Edi_Range; + static void _update_lines(Edi_Editor *editor); @@ -236,6 +255,244 @@ _edi_editor_statusbar_add(Evas_Object *panel, Edi_Editor *editor, Edi_Mainview_I evas_object_smart_callback_add(editor->entry, "cursor,changed", _edit_cursor_moved, position); } +static void +_edi_range_color_set(Edi_Editor *editor, Edi_Range range, Edi_Color color) +{ + Evas_Object *textblock; + printf("Setting color %s for range(%d:%d, %d:%d)\n", color, range.start.line, range.start.col, range.end.line, range.end.col); + + textblock = elm_entry_textblock_get(editor->entry); +// TODO actually set some styling on our textblock +} + +#if HAVE_LIBCLANG +static void +_clang_load_highlighting(const char *path, Edi_Editor *editor) +{ + CXToken *tokens = NULL; + unsigned int n = 0; + unsigned int i = 0; + CXCursor *cursors = NULL; + + { + CXFile cfile = clang_getFile(editor->tx_unit, path); + int tgridw = 0, tgridh = 0; + evas_object_textblock_size_native_get(elm_entry_textblock_get(editor->entry), &tgridw, &tgridh); + +#if 0 + /* FIXME: Should be used, I don't know why tokenize doesn't work in mid + * comment cases and etc. */ + int range_start, range_end; + range_start = editor->offset; + range_end = editor->offset + tgridh; + CXSourceRange range = clang_getRange(clang_getLocation(editor->tx_unit, cfile, range_start, 1), clang_getLocation(editor->tx_unit, cfile, range_end, tgridw)); +#else + CXSourceRange range = clang_getRange( + clang_getLocationForOffset(editor->tx_unit, cfile, 0), + clang_getLocationForOffset(editor->tx_unit, cfile, eina_file_size_get(eina_file_open(path, EINA_FALSE)))); +#endif + clang_tokenize(editor->tx_unit, range, &tokens, &n); + /* FIXME: We should use annotate tokens and then use a lot more + * color classes. I don't know why it's broken ATM... :( */ + cursors = (CXCursor *) malloc(n * sizeof(CXCursor)); + clang_annotateTokens(editor->tx_unit, tokens, n, cursors); + } + + for (i = 0 ; i < n ; i++) + { + Edi_Range range; + Edi_Color color = ""; +//EDI_COLOR_FOREGROUND_DEFAULT; + + CXSourceRange tkrange = clang_getTokenExtent(editor->tx_unit, tokens[i]); + clang_getSpellingLocation(clang_getRangeStart(tkrange), NULL, + &range.start.line, &range.start.col, NULL); + clang_getSpellingLocation(clang_getRangeEnd(tkrange), NULL, + &range.end.line, &range.end.col, NULL); + /* FIXME: Should probably do something fancier, this is only a limited + * number of types. */ + switch (clang_getTokenKind(tokens[i])) + { + case CXToken_Punctuation: + case CXToken_Identifier: + switch (cursors[i].kind) + { + case CXCursor_DeclRefExpr: + /* Handle different ref kinds */ +// color = EDI_COLOR_FOREGROUND_REF; + break; + case CXCursor_MacroDefinition: +// color = EDI_COLOR_FOREGROUND_MACRO_DEFINITION; + break; + case CXCursor_InclusionDirective: + case CXCursor_PreprocessingDirective: +// color = EDI_COLOR_FOREGROUND_PREPROCESSING_DIRECTIVE; + break; + case CXCursor_TypeRef: +// color = EDI_COLOR_FOREGROUND_USER_TYPE; + break; + case CXCursor_MacroExpansion: +// color = EDI_COLOR_FOREGROUND_MACRO_EXPANSION; + break; + default: +// color = EDI_COLOR_FOREGROUND_DEFAULT; + break; + } + break; + case CXToken_Keyword: + switch (cursors[i].kind) + { + case CXCursor_PreprocessingDirective: +// color = EDI_COLOR_FOREGROUND_PREPROCESSING_DIRECTIVE; + break; + case CXCursor_CaseStmt: + case CXCursor_DefaultStmt: + case CXCursor_IfStmt: + case CXCursor_SwitchStmt: + case CXCursor_WhileStmt: + case CXCursor_DoStmt: + case CXCursor_ForStmt: + case CXCursor_GotoStmt: + case CXCursor_IndirectGotoStmt: + case CXCursor_ContinueStmt: + case CXCursor_BreakStmt: + case CXCursor_ReturnStmt: + case CXCursor_AsmStmt: + case CXCursor_ObjCAtTryStmt: + case CXCursor_ObjCAtCatchStmt: + case CXCursor_ObjCAtFinallyStmt: + case CXCursor_ObjCAtThrowStmt: + case CXCursor_ObjCAtSynchronizedStmt: + case CXCursor_ObjCAutoreleasePoolStmt: + case CXCursor_ObjCForCollectionStmt: + case CXCursor_CXXCatchStmt: + case CXCursor_CXXTryStmt: + case CXCursor_CXXForRangeStmt: + case CXCursor_SEHTryStmt: + case CXCursor_SEHExceptStmt: + case CXCursor_SEHFinallyStmt: +color = "stmt"; +// color = EDI_COLOR_FOREGROUND_KEYWORD_STMT; + break; + default: +color = "keyword"; +// color = EDI_COLOR_FOREGROUND_KEYWORD; + break; + } + break; + case CXToken_Literal: +color = "literal"; +// color = EDI_COLOR_FOREGROUND_LITERAL; + break; + case CXToken_Comment: +color = "comment"; +// color = EDI_COLOR_FOREGROUND_COMMENT; + break; + } + + _edi_range_color_set(editor, range, color); + +#if CLANG_DEBUG + const char *kind = NULL; + switch (clang_getTokenKind(tokens[i])) { + case CXToken_Punctuation: kind = "Punctuation"; break; + case CXToken_Keyword: kind = "Keyword"; break; + case CXToken_Identifier: kind = "Identifier"; break; + case CXToken_Literal: kind = "Literal"; break; + case CXToken_Comment: kind = "Comment"; break; + } + + printf("%s ", kind); + PrintToken(editor->tx_unit, tokens[i]); + + if (!clang_isInvalid(cursors[i].kind)) { + printf(" "); + PrintCursor(cursors[i]); + } + + printf("\n"); +#endif + } + + free(cursors); + clang_disposeTokens(editor->tx_unit, tokens, n); +} + +static void +_clang_load_errors(const char *path, Edi_Editor *editor) +{ + unsigned n = clang_getNumDiagnostics(editor->tx_unit); + unsigned i = 0; + + for(i = 0, n = clang_getNumDiagnostics(editor->tx_unit); i != n; ++i) + { + CXDiagnostic diag = clang_getDiagnostic(editor->tx_unit, i); + Edi_Range range; + + clang_getSpellingLocation(clang_getDiagnosticLocation(diag), NULL, &range.start.line, &range.start.col, NULL); + range.end = range.start; + + /* FIXME: Also handle ranges and fix suggestions. */ + + Edi_Color color = ""; +// EDI_COLOR_BACKGROUND_DEFAULT; + + switch (clang_getDiagnosticSeverity(diag)) + { + case CXDiagnostic_Ignored: +// color = EDI_COLOR_BACKGROUND_SEVIRITY_IGNORED; + break; + case CXDiagnostic_Note: +// color = EDI_COLOR_BACKGROUND_SEVIRITY_NOTE; + break; + case CXDiagnostic_Warning: +// color = EDI_COLOR_BACKGROUND_SEVIRITY_WARNING; + break; + case CXDiagnostic_Error: +// color = EDI_COLOR_BACKGROUND_SEVIRITY_ERROR; + break; + case CXDiagnostic_Fatal: +// color = EDI_COLOR_BACKGROUND_SEVIRITY_FATAL; + break; + } + + _edi_range_color_set(editor, range, color); + +#if CLANG_DEBUG + CXString str = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); + printf("DEBUG: Diag:%s\n", clang_getCString(str)); + clang_disposeString(str); +#endif + + clang_disposeDiagnostic(diag); + } +} + +static void +_edi_clang_setup(const char *path, Edi_Editor *editor) +{ + /* Clang */ + /* FIXME: index should probably be global. */ + const char const *clang_argv[] = {"-I/usr/lib/clang/3.1/include/", "-Wall", "-Wextra"}; + int clang_argc = sizeof(clang_argv) / sizeof(*clang_argv); + + editor->idx = clang_createIndex(0, 0); + + /* FIXME: Possibly activate more options? */ + editor->tx_unit = clang_parseTranslationUnit(editor->idx, path, clang_argv, clang_argc, NULL, 0, clang_defaultEditingTranslationUnitOptions() | CXTranslationUnit_DetailedPreprocessingRecord); + + _clang_load_errors(path, editor); + _clang_load_highlighting(path, editor); +} + +static void +_edi_clang_dispose(Edi_Editor *editor) +{ + clang_disposeTranslationUnit(editor->tx_unit); + clang_disposeIndex(editor->idx); +} +#endif + EAPI Evas_Object *edi_editor_add(Evas_Object *parent, Edi_Mainview_Item *item) { Evas_Object *txt, *lines, *vbox, *box, *searchbar, *statusbar; @@ -316,5 +573,10 @@ EAPI Evas_Object *edi_editor_add(Evas_Object *parent, Edi_Mainview_Item *item) evas_object_data_set(vbox, "editor", editor); _update_lines(editor); + +#if HAVE_LIBCLANG + _edi_clang_setup(item->path, editor); +#endif + return vbox; } diff --git a/src/bin/editor/edi_editor.h b/src/bin/editor/edi_editor.h index 91eb3ed..163164d 100644 --- a/src/bin/editor/edi_editor.h +++ b/src/bin/editor/edi_editor.h @@ -3,6 +3,10 @@ #include +#if HAVE_LIBCLANG +#include +#endif + #include "mainview/edi_mainview_item.h" #ifdef __cplusplus @@ -40,6 +44,12 @@ struct _Edi_Editor Edi_Editor_Search *search; /* Add new members here. */ + +#if HAVE_LIBCLANG + /* Clang */ + CXIndex idx; + CXTranslationUnit tx_unit; +#endif }; /**