2 * Copyright (c) 2019, Jiri Techet
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
7 * Provides a simple application using ctags as a library and using the same
8 * set of ctags functions as the Geany editor
11 #include "general.h" /* must always come first */
19 #include "options_p.h"
20 #include "trashbox_p.h"
29 static int writeEntry (tagWriter
*writer
, MIO
* mio
, const tagEntryInfo
*const tag
, void *clientData
);
30 static void rescanFailed (tagWriter
*writer
, unsigned long validTagNum
, void *clientData
);
32 /* we need to be able to provide a custom writer using which we collect the tags */
33 tagWriter customWriter
= {
34 .writeEntry
= writeEntry
,
35 .writePtagEntry
= NULL
,
36 .preWriteEntry
= NULL
,
37 .postWriteEntry
= NULL
,
38 .rescanFailedEntry
= rescanFailed
,
39 .treatFieldAsFixed
= NULL
,
40 .defaultFileName
= "tags_file_which_should_never_appear_anywhere",
45 /* we need to be able to provide an error printer which doesn't crash Geany on error */
46 static bool nofatalErrorPrinter (const errorSelection selection
,
47 const char *const format
,
48 va_list ap
, void *data CTAGS_ATTR_UNUSED
)
50 fprintf (stderr
, "%s: ", (selection
& WARNING
) ? "Warning: " : "Error");
51 vfprintf (stderr
, format
, ap
);
52 if (selection
& PERROR
)
54 fprintf (stderr
, " : %s", strerror (errno
));
64 /* we need to be able to enable all kinds for all languages (some are disabled by default) */
65 static void enableAllLangKinds()
69 for (lang
= 0; lang
< countParsers(); lang
++)
71 unsigned int kindNum
= countLanguageKinds(lang
);
74 for (kind
= 0; kind
< kindNum
; kind
++)
76 kindDefinition
*def
= getLanguageKind(lang
, kind
);
77 enableKind(def
, true);
83 /* we need to be able to initialize ctags like in main() but skipping some things */
84 static void ctagsInit(void)
86 initDefaultTrashBox ();
88 setErrorPrinter (nofatalErrorPrinter
, NULL
);
89 setTagWriter (WRITER_CUSTOM
, &customWriter
);
98 /* make sure all parsers are initialized */
99 initializeParser (LANG_AUTO
);
101 /* change default value which is false */
102 enableXtag(XTAG_TAGS_GENERATED_BY_GUEST_PARSERS
, true);
103 enableXtag(XTAG_REFERENCE_TAGS
, true);
105 /* some kinds we are interested in are disabled by default */
106 enableAllLangKinds();
110 /* we need to be able to get a name of a given language */
111 static const char *ctagsGetLangName(int lang
)
113 return getLanguageName(lang
);
117 /* we need to be able to get an int representing a given language */
118 static int ctagsGetNamedLang(const char *name
)
120 return getNamedLanguage(name
, 0);
124 /* we need to be able to get kind letters provided by a given language */
125 static const char *ctagsGetLangKinds(int lang
)
127 unsigned int kindNum
= countLanguageKinds(lang
);
128 static char kinds
[257];
131 for (i
= 0; i
< kindNum
; i
++)
132 kinds
[i
] = getLanguageKind(lang
, i
)->letter
;
139 /* we need to be able to get kind name from a kind letter for a given language */
140 static const char *ctagsGetKindName(char kind
, int lang
)
142 kindDefinition
*def
= getLanguageKindForLetter (lang
, kind
);
143 return def
? def
->name
: "unknown";
147 /* we need to be able to get kind letter from a kind name for a given language */
148 static char ctagsGetKindFromName(const char *name
, int lang
)
150 kindDefinition
*def
= getLanguageKindForName (lang
, name
);
151 return def
? def
->letter
: '-';
155 /* we need to be able to get kind letter from a kind index */
156 static char ctagsGetKindFromIndex(int index
, int lang
)
158 return getLanguageKind(lang
, index
)->letter
;
162 /* we need to be able to get the number of parsers */
163 static unsigned int ctagsGetLangCount(void)
165 return countParsers();
168 /*******************************************************************************
169 * So let's just use what we have for our great client...
170 ******************************************************************************/
172 /* our internal tag representation - this is all the tag information we use in Geany */
180 char *implementation
;
183 unsigned long lineNumber
;
188 static Tag
*createTag(const tagEntryInfo
*info
)
190 Tag
*tag
= xCalloc(1, Tag
);
192 tag
->name
= eStrdup(info
->name
);
193 if (info
->extensionFields
.signature
)
194 tag
->signature
= eStrdup(info
->extensionFields
.signature
);
195 if (info
->extensionFields
.scopeName
)
196 tag
->scopeName
= eStrdup(info
->extensionFields
.scopeName
);
197 if (info
->extensionFields
.inheritance
)
198 tag
->inheritance
= eStrdup(info
->extensionFields
.inheritance
);
199 if (info
->extensionFields
.typeRef
[1])
200 tag
->varType
= eStrdup(info
->extensionFields
.typeRef
[1]);
201 if (info
->extensionFields
.access
)
202 tag
->access
= eStrdup(info
->extensionFields
.access
);
203 if (info
->extensionFields
.implementation
)
204 tag
->implementation
= eStrdup(info
->extensionFields
.implementation
);
205 tag
->kindLetter
= ctagsGetKindFromIndex(info
->kindIndex
, info
->langType
);
206 tag
->isFileScope
= info
->isFileScope
;
207 tag
->lineNumber
= info
->lineNumber
;
208 tag
->lang
= info
->langType
;
213 static void destroyTag(Tag
*tag
)
218 eFree(tag
->signature
);
220 eFree(tag
->scopeName
);
221 if (tag
->inheritance
)
222 eFree(tag
->inheritance
);
227 if (tag
->implementation
)
228 eFree(tag
->implementation
);
233 /* callback from ctags informing us about a new tag */
234 static int writeEntry (tagWriter
*writer
, MIO
*mio
, const tagEntryInfo
*const tag
, void *clientData
)
238 /* apparently we have to call this to get the scope info - maybe we can
239 * specify some option during initialization so we don't have to cal this
241 getTagScopeInformation((tagEntryInfo
*)tag
, NULL
, NULL
);
243 /* convert tags into our internal representation and store them into an array */
245 ptrArrayAdd((ptrArray
*)clientData
, t
);
247 /* output length - we don't write anything to the MIO */
252 /* scan has failed so we have invalid tags in our array - validTagNum should
253 * tell us the number of valid tags so remove all the extra tags and shrink the array */
254 static void rescanFailed (tagWriter
*writer
, unsigned long validTagNum
, void *clientData
)
256 ptrArray
*tagArray
= clientData
;
257 int num
= ptrArrayCount(tagArray
);
259 if (num
> validTagNum
)
262 for (i
= validTagNum
; i
< num
; i
++)
264 Tag
*tag
= ptrArrayLast(tagArray
);
266 ptrArrayRemoveLast(tagArray
);
272 /* do whatever we want to do with the tags */
273 static void processCollectedTags(ptrArray
*tagArray
)
276 int num
= ptrArrayCount(tagArray
);
278 for (i
= 0; i
< num
; i
++)
280 Tag
*tag
= ptrArrayItem(tagArray
, i
);
281 printf("%s\tline: %lu\tkind: %s\t lang: %s\n",
284 ctagsGetKindName(tag
->kindLetter
, tag
->lang
),
285 ctagsGetLangName(tag
->lang
));
288 /* prepare for the next parsing by clearing the tag array */
289 ptrArrayClear(tagArray
);
293 extern int main (int argc
, char **argv
)
295 /* called once when Geany starts */
298 /* create empty tag array *
299 * this is where we store the collected tags
300 * NOTE: Geany doesn't use the ptrArray type - it is used just for the purpose of this demo */
301 ptrArray
*tagArray
= ptrArrayNew((ptrArrayDeleteFunc
)destroyTag
);
303 printf("This parser only parses C files - provide them as arguments on the "
304 "command line or get a hard-coded buffer parsed when no arguments "
306 if (argc
== 1) /* parsing contents of a buffer */
308 char *program
= "int foo() {}\n\n int bar() {}\n\n int main() {}\n";
309 int lang
= ctagsGetNamedLang("C");
310 const char *kinds
= ctagsGetLangKinds(lang
);
313 printf("Number of all parsers is: %d\n", ctagsGetLangCount());
314 printf("We are parsing %s which provides the following kinds:\n",
315 ctagsGetLangName(lang
));
316 for (i
= 0; kinds
[i
] != '\0'; i
++)
319 /* back and forth conversion - the same like just kinds[i] */
320 ctagsGetKindFromName(ctagsGetKindName(kinds
[i
], lang
), lang
),
321 ctagsGetKindName(kinds
[i
], lang
));
324 printf("\nParsing buffer:\n");
325 /* we always specify the language by ourselves and don't use ctags detection */
326 parseRawBuffer("whatever", (unsigned char *)program
, strlen(program
), lang
, tagArray
);
328 processCollectedTags(tagArray
);
330 else /* parsing contents of a file */
333 for (i
= 1; i
< argc
; i
++)
335 printf("\nParsing %s:\n", argv
[i
]);
336 /* parseRawBuffer() is called repeatadly during Geany execution */
337 parseRawBuffer(argv
[i
], NULL
, 0, getNamedLanguage("C", 0), tagArray
);
339 processCollectedTags(tagArray
);
343 ptrArrayDelete(tagArray
);