ctags.rb 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. data/Gemfile +2 -0
  2. data/Rakefile +23 -0
  3. data/ctags.rb.gemspec +23 -0
  4. data/ext/.gitignore +3 -0
  5. data/ext/extconf.rb +15 -0
  6. data/ext/vendor/exuberant-ctags/.gitignore +6 -0
  7. data/ext/vendor/exuberant-ctags/.indent.pro +31 -0
  8. data/ext/vendor/exuberant-ctags/COPYING +340 -0
  9. data/ext/vendor/exuberant-ctags/EXTENDING.html +386 -0
  10. data/ext/vendor/exuberant-ctags/FAQ +371 -0
  11. data/ext/vendor/exuberant-ctags/INSTALL +215 -0
  12. data/ext/vendor/exuberant-ctags/INSTALL.oth +73 -0
  13. data/ext/vendor/exuberant-ctags/MAINTAINERS +88 -0
  14. data/ext/vendor/exuberant-ctags/Makefile.in +222 -0
  15. data/ext/vendor/exuberant-ctags/NEWS +871 -0
  16. data/ext/vendor/exuberant-ctags/README +73 -0
  17. data/ext/vendor/exuberant-ctags/ant.c +42 -0
  18. data/ext/vendor/exuberant-ctags/argproc.c +505 -0
  19. data/ext/vendor/exuberant-ctags/args.c +274 -0
  20. data/ext/vendor/exuberant-ctags/args.h +63 -0
  21. data/ext/vendor/exuberant-ctags/asm.c +387 -0
  22. data/ext/vendor/exuberant-ctags/asp.c +328 -0
  23. data/ext/vendor/exuberant-ctags/awk.c +81 -0
  24. data/ext/vendor/exuberant-ctags/basic.c +203 -0
  25. data/ext/vendor/exuberant-ctags/beta.c +321 -0
  26. data/ext/vendor/exuberant-ctags/c.c +2932 -0
  27. data/ext/vendor/exuberant-ctags/cobol.c +50 -0
  28. data/ext/vendor/exuberant-ctags/config.h.in +277 -0
  29. data/ext/vendor/exuberant-ctags/configure +7704 -0
  30. data/ext/vendor/exuberant-ctags/configure.ac +532 -0
  31. data/ext/vendor/exuberant-ctags/ctags.1 +1186 -0
  32. data/ext/vendor/exuberant-ctags/ctags.h +28 -0
  33. data/ext/vendor/exuberant-ctags/ctags.html +2087 -0
  34. data/ext/vendor/exuberant-ctags/ctags.spec +40 -0
  35. data/ext/vendor/exuberant-ctags/debug.c +113 -0
  36. data/ext/vendor/exuberant-ctags/debug.h +70 -0
  37. data/ext/vendor/exuberant-ctags/descrip.mms +68 -0
  38. data/ext/vendor/exuberant-ctags/dosbatch.c +42 -0
  39. data/ext/vendor/exuberant-ctags/e_amiga.h +24 -0
  40. data/ext/vendor/exuberant-ctags/e_djgpp.h +47 -0
  41. data/ext/vendor/exuberant-ctags/e_mac.h +143 -0
  42. data/ext/vendor/exuberant-ctags/e_msoft.h +76 -0
  43. data/ext/vendor/exuberant-ctags/e_os2.h +37 -0
  44. data/ext/vendor/exuberant-ctags/e_qdos.h +34 -0
  45. data/ext/vendor/exuberant-ctags/e_riscos.h +58 -0
  46. data/ext/vendor/exuberant-ctags/e_vms.h +31 -0
  47. data/ext/vendor/exuberant-ctags/eiffel.c +1352 -0
  48. data/ext/vendor/exuberant-ctags/entry.c +847 -0
  49. data/ext/vendor/exuberant-ctags/entry.h +103 -0
  50. data/ext/vendor/exuberant-ctags/erlang.c +189 -0
  51. data/ext/vendor/exuberant-ctags/flex.c +2243 -0
  52. data/ext/vendor/exuberant-ctags/fortran.c +2197 -0
  53. data/ext/vendor/exuberant-ctags/general.h +127 -0
  54. data/ext/vendor/exuberant-ctags/get.c +669 -0
  55. data/ext/vendor/exuberant-ctags/get.h +50 -0
  56. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/all-wcprops +47 -0
  57. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/entries +112 -0
  58. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/README.txt.svn-base +5 -0
  59. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regcomp.c.svn-base +3818 -0
  60. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex.c.svn-base +74 -0
  61. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex.h.svn-base +575 -0
  62. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex_internal.c.svn-base +1713 -0
  63. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex_internal.h.svn-base +773 -0
  64. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regexec.c.svn-base +4338 -0
  65. data/ext/vendor/exuberant-ctags/gnu_regex/README.txt +5 -0
  66. data/ext/vendor/exuberant-ctags/gnu_regex/regcomp.c +3818 -0
  67. data/ext/vendor/exuberant-ctags/gnu_regex/regex.c +74 -0
  68. data/ext/vendor/exuberant-ctags/gnu_regex/regex.h +575 -0
  69. data/ext/vendor/exuberant-ctags/gnu_regex/regex_internal.c +1713 -0
  70. data/ext/vendor/exuberant-ctags/gnu_regex/regex_internal.h +773 -0
  71. data/ext/vendor/exuberant-ctags/gnu_regex/regexec.c +4338 -0
  72. data/ext/vendor/exuberant-ctags/html.c +49 -0
  73. data/ext/vendor/exuberant-ctags/jscript.c +1572 -0
  74. data/ext/vendor/exuberant-ctags/keyword.c +258 -0
  75. data/ext/vendor/exuberant-ctags/keyword.h +34 -0
  76. data/ext/vendor/exuberant-ctags/lisp.c +139 -0
  77. data/ext/vendor/exuberant-ctags/lregex.c +704 -0
  78. data/ext/vendor/exuberant-ctags/lua.c +133 -0
  79. data/ext/vendor/exuberant-ctags/mac.c +273 -0
  80. data/ext/vendor/exuberant-ctags/magic.diff +21 -0
  81. data/ext/vendor/exuberant-ctags/main.c +584 -0
  82. data/ext/vendor/exuberant-ctags/main.h +32 -0
  83. data/ext/vendor/exuberant-ctags/maintainer.mak +476 -0
  84. data/ext/vendor/exuberant-ctags/make.c +217 -0
  85. data/ext/vendor/exuberant-ctags/matlab.c +44 -0
  86. data/ext/vendor/exuberant-ctags/mk_bc3.mak +46 -0
  87. data/ext/vendor/exuberant-ctags/mk_bc5.mak +49 -0
  88. data/ext/vendor/exuberant-ctags/mk_djg.mak +18 -0
  89. data/ext/vendor/exuberant-ctags/mk_manx.mak +65 -0
  90. data/ext/vendor/exuberant-ctags/mk_mingw.mak +31 -0
  91. data/ext/vendor/exuberant-ctags/mk_mpw.mak +130 -0
  92. data/ext/vendor/exuberant-ctags/mk_mvc.mak +40 -0
  93. data/ext/vendor/exuberant-ctags/mk_os2.mak +104 -0
  94. data/ext/vendor/exuberant-ctags/mk_qdos.mak +100 -0
  95. data/ext/vendor/exuberant-ctags/mk_sas.mak +63 -0
  96. data/ext/vendor/exuberant-ctags/mkinstalldirs +40 -0
  97. data/ext/vendor/exuberant-ctags/ocaml.c +1842 -0
  98. data/ext/vendor/exuberant-ctags/options.c +1842 -0
  99. data/ext/vendor/exuberant-ctags/options.h +155 -0
  100. data/ext/vendor/exuberant-ctags/parse.c +677 -0
  101. data/ext/vendor/exuberant-ctags/parse.h +129 -0
  102. data/ext/vendor/exuberant-ctags/parsers.h +63 -0
  103. data/ext/vendor/exuberant-ctags/pascal.c +267 -0
  104. data/ext/vendor/exuberant-ctags/perl.c +382 -0
  105. data/ext/vendor/exuberant-ctags/php.c +237 -0
  106. data/ext/vendor/exuberant-ctags/python.c +771 -0
  107. data/ext/vendor/exuberant-ctags/qdos.c +106 -0
  108. data/ext/vendor/exuberant-ctags/read.c +569 -0
  109. data/ext/vendor/exuberant-ctags/read.h +116 -0
  110. data/ext/vendor/exuberant-ctags/readtags.c +959 -0
  111. data/ext/vendor/exuberant-ctags/readtags.h +252 -0
  112. data/ext/vendor/exuberant-ctags/rexx.c +39 -0
  113. data/ext/vendor/exuberant-ctags/routines.c +891 -0
  114. data/ext/vendor/exuberant-ctags/routines.h +134 -0
  115. data/ext/vendor/exuberant-ctags/ruby.c +408 -0
  116. data/ext/vendor/exuberant-ctags/scheme.c +111 -0
  117. data/ext/vendor/exuberant-ctags/sh.c +115 -0
  118. data/ext/vendor/exuberant-ctags/slang.c +41 -0
  119. data/ext/vendor/exuberant-ctags/sml.c +212 -0
  120. data/ext/vendor/exuberant-ctags/sort.c +230 -0
  121. data/ext/vendor/exuberant-ctags/sort.h +32 -0
  122. data/ext/vendor/exuberant-ctags/source.mak +122 -0
  123. data/ext/vendor/exuberant-ctags/sql.c +2112 -0
  124. data/ext/vendor/exuberant-ctags/strlist.c +281 -0
  125. data/ext/vendor/exuberant-ctags/strlist.h +54 -0
  126. data/ext/vendor/exuberant-ctags/tcl.c +116 -0
  127. data/ext/vendor/exuberant-ctags/tex.c +524 -0
  128. data/ext/vendor/exuberant-ctags/verilog.c +340 -0
  129. data/ext/vendor/exuberant-ctags/vhdl.c +835 -0
  130. data/ext/vendor/exuberant-ctags/vim.c +636 -0
  131. data/ext/vendor/exuberant-ctags/vstring.c +232 -0
  132. data/ext/vendor/exuberant-ctags/vstring.h +85 -0
  133. data/ext/vendor/exuberant-ctags/yacc.c +40 -0
  134. data/lib/ctags/exuberant.rb +45 -0
  135. data/lib/ctags/version.rb +3 -0
  136. data/lib/ctags.rb +6 -0
  137. data/test/test_ctags.rb +24 -0
  138. metadata +233 -0
@@ -0,0 +1,771 @@
1
+ /*
2
+ * $Id: python.c 720 2009-07-07 03:55:23Z dhiebert $
3
+ *
4
+ * Copyright (c) 2000-2003, Darren Hiebert
5
+ *
6
+ * This source code is released for free distribution under the terms of the
7
+ * GNU General Public License.
8
+ *
9
+ * This module contains functions for generating tags for Python language
10
+ * files.
11
+ */
12
+ /*
13
+ * INCLUDE FILES
14
+ */
15
+ #include "general.h" /* must always come first */
16
+
17
+ #include <string.h>
18
+
19
+ #include "entry.h"
20
+ #include "options.h"
21
+ #include "read.h"
22
+ #include "main.h"
23
+ #include "vstring.h"
24
+ #include "routines.h"
25
+ #include "debug.h"
26
+
27
+ /*
28
+ * DATA DECLARATIONS
29
+ */
30
+ typedef struct NestingLevel NestingLevel;
31
+ typedef struct NestingLevels NestingLevels;
32
+
33
+ struct NestingLevel
34
+ {
35
+ int indentation;
36
+ vString *name;
37
+ int type;
38
+ };
39
+
40
+ struct NestingLevels
41
+ {
42
+ NestingLevel *levels;
43
+ int n; /* number of levels in use */
44
+ int allocated;
45
+ };
46
+
47
+ typedef enum {
48
+ K_CLASS, K_FUNCTION, K_MEMBER, K_VARIABLE, K_IMPORT
49
+ } pythonKind;
50
+
51
+ /*
52
+ * DATA DEFINITIONS
53
+ */
54
+ static kindOption PythonKinds[] = {
55
+ {TRUE, 'c', "class", "classes"},
56
+ {TRUE, 'f', "function", "functions"},
57
+ {TRUE, 'm', "member", "class members"},
58
+ {TRUE, 'v', "variable", "variables"},
59
+ {TRUE, 'i', "namespace", "imports"}
60
+ };
61
+
62
+ static char const * const singletriple = "'''";
63
+ static char const * const doubletriple = "\"\"\"";
64
+
65
+ /*
66
+ * FUNCTION DEFINITIONS
67
+ */
68
+
69
+ static NestingLevels *nestingLevelsNew (void)
70
+ {
71
+ NestingLevels *nls = xCalloc (1, NestingLevels);
72
+ return nls;
73
+ }
74
+
75
+ static void nestingLevelsFree (NestingLevels *nls)
76
+ {
77
+ int i;
78
+ for (i = 0; i < nls->allocated; i++)
79
+ vStringDelete(nls->levels[i].name);
80
+ if (nls->levels) eFree(nls->levels);
81
+ eFree(nls);
82
+ }
83
+
84
+ static void nestingLevelsPush (NestingLevels *nls,
85
+ const vString *name, int type)
86
+ {
87
+ NestingLevel *nl = NULL;
88
+
89
+ if (nls->n >= nls->allocated)
90
+ {
91
+ nls->allocated++;
92
+ nls->levels = xRealloc(nls->levels,
93
+ nls->allocated, NestingLevel);
94
+ nls->levels[nls->n].name = vStringNew();
95
+ }
96
+ nl = &nls->levels[nls->n];
97
+ nls->n++;
98
+
99
+ vStringCopy(nl->name, name);
100
+ nl->type = type;
101
+ }
102
+
103
+ #if 0
104
+ static NestingLevel *nestingLevelsGetCurrent (NestingLevels *nls)
105
+ {
106
+ Assert (nls != NULL);
107
+
108
+ if (nls->n < 1)
109
+ return NULL;
110
+
111
+ return &nls->levels[nls->n - 1];
112
+ }
113
+
114
+ static void nestingLevelsPop (NestingLevels *nls)
115
+ {
116
+ const NestingLevel *nl = nestingLevelsGetCurrent(nls);
117
+
118
+ Assert (nl != NULL);
119
+ vStringClear(nl->name);
120
+ nls->n--;
121
+ }
122
+ #endif
123
+
124
+ static boolean isIdentifierFirstCharacter (int c)
125
+ {
126
+ return (boolean) (isalpha (c) || c == '_');
127
+ }
128
+
129
+ static boolean isIdentifierCharacter (int c)
130
+ {
131
+ return (boolean) (isalnum (c) || c == '_');
132
+ }
133
+
134
+ /* Given a string with the contents of a line directly after the "def" keyword,
135
+ * extract all relevant information and create a tag.
136
+ */
137
+ static void makeFunctionTag (vString *const function,
138
+ vString *const parent, int is_class_parent, const char *arglist __unused__)
139
+ {
140
+ tagEntryInfo tag;
141
+ initTagEntry (&tag, vStringValue (function));
142
+
143
+ tag.kindName = "function";
144
+ tag.kind = 'f';
145
+ /* tag.extensionFields.arglist = arglist; */
146
+
147
+ if (vStringLength (parent) > 0)
148
+ {
149
+ if (is_class_parent)
150
+ {
151
+ tag.kindName = "member";
152
+ tag.kind = 'm';
153
+ tag.extensionFields.scope [0] = "class";
154
+ tag.extensionFields.scope [1] = vStringValue (parent);
155
+ }
156
+ else
157
+ {
158
+ tag.extensionFields.scope [0] = "function";
159
+ tag.extensionFields.scope [1] = vStringValue (parent);
160
+ }
161
+ }
162
+
163
+ /* If a function starts with __, we mark it as file scope.
164
+ * FIXME: What is the proper way to signal such attributes?
165
+ * TODO: What does functions/classes starting with _ and __ mean in python?
166
+ */
167
+ if (strncmp (vStringValue (function), "__", 2) == 0 &&
168
+ strcmp (vStringValue (function), "__init__") != 0)
169
+ {
170
+ tag.extensionFields.access = "private";
171
+ tag.isFileScope = TRUE;
172
+ }
173
+ else
174
+ {
175
+ tag.extensionFields.access = "public";
176
+ }
177
+ makeTagEntry (&tag);
178
+ }
179
+
180
+ /* Given a string with the contents of the line directly after the "class"
181
+ * keyword, extract all necessary information and create a tag.
182
+ */
183
+ static void makeClassTag (vString *const class, vString *const inheritance,
184
+ vString *const parent, int is_class_parent)
185
+ {
186
+ tagEntryInfo tag;
187
+ initTagEntry (&tag, vStringValue (class));
188
+ tag.kindName = "class";
189
+ tag.kind = 'c';
190
+ if (vStringLength (parent) > 0)
191
+ {
192
+ if (is_class_parent)
193
+ {
194
+ tag.extensionFields.scope [0] = "class";
195
+ tag.extensionFields.scope [1] = vStringValue (parent);
196
+ }
197
+ else
198
+ {
199
+ tag.extensionFields.scope [0] = "function";
200
+ tag.extensionFields.scope [1] = vStringValue (parent);
201
+ }
202
+ }
203
+ tag.extensionFields.inheritance = vStringValue (inheritance);
204
+ makeTagEntry (&tag);
205
+ }
206
+
207
+ static void makeVariableTag (vString *const var, vString *const parent)
208
+ {
209
+ tagEntryInfo tag;
210
+ initTagEntry (&tag, vStringValue (var));
211
+ tag.kindName = "variable";
212
+ tag.kind = 'v';
213
+ if (vStringLength (parent) > 0)
214
+ {
215
+ tag.extensionFields.scope [0] = "class";
216
+ tag.extensionFields.scope [1] = vStringValue (parent);
217
+ }
218
+ makeTagEntry (&tag);
219
+ }
220
+
221
+ /* Skip a single or double quoted string. */
222
+ static const char *skipString (const char *cp)
223
+ {
224
+ const char *start = cp;
225
+ int escaped = 0;
226
+ for (cp++; *cp; cp++)
227
+ {
228
+ if (escaped)
229
+ escaped--;
230
+ else if (*cp == '\\')
231
+ escaped++;
232
+ else if (*cp == *start)
233
+ return cp + 1;
234
+ }
235
+ return cp;
236
+ }
237
+
238
+ /* Skip everything up to an identifier start. */
239
+ static const char *skipEverything (const char *cp)
240
+ {
241
+ for (; *cp; cp++)
242
+ {
243
+ if (*cp == '"' || *cp == '\'')
244
+ {
245
+ cp = skipString(cp);
246
+ if (!*cp) break;
247
+ }
248
+ if (isIdentifierFirstCharacter ((int) *cp))
249
+ return cp;
250
+ }
251
+ return cp;
252
+ }
253
+
254
+ /* Skip an identifier. */
255
+ static const char *skipIdentifier (const char *cp)
256
+ {
257
+ while (isIdentifierCharacter ((int) *cp))
258
+ cp++;
259
+ return cp;
260
+ }
261
+
262
+ static const char *findDefinitionOrClass (const char *cp)
263
+ {
264
+ while (*cp)
265
+ {
266
+ cp = skipEverything (cp);
267
+ if (!strncmp(cp, "def", 3) || !strncmp(cp, "class", 5) ||
268
+ !strncmp(cp, "cdef", 4) || !strncmp(cp, "cpdef", 5))
269
+ {
270
+ return cp;
271
+ }
272
+ cp = skipIdentifier (cp);
273
+ }
274
+ return NULL;
275
+ }
276
+
277
+ static const char *skipSpace (const char *cp)
278
+ {
279
+ while (isspace ((int) *cp))
280
+ ++cp;
281
+ return cp;
282
+ }
283
+
284
+ /* Starting at ''cp'', parse an identifier into ''identifier''. */
285
+ static const char *parseIdentifier (const char *cp, vString *const identifier)
286
+ {
287
+ vStringClear (identifier);
288
+ while (isIdentifierCharacter ((int) *cp))
289
+ {
290
+ vStringPut (identifier, (int) *cp);
291
+ ++cp;
292
+ }
293
+ vStringTerminate (identifier);
294
+ return cp;
295
+ }
296
+
297
+ static void parseClass (const char *cp, vString *const class,
298
+ vString *const parent, int is_class_parent)
299
+ {
300
+ vString *const inheritance = vStringNew ();
301
+ vStringClear (inheritance);
302
+ cp = parseIdentifier (cp, class);
303
+ cp = skipSpace (cp);
304
+ if (*cp == '(')
305
+ {
306
+ ++cp;
307
+ while (*cp != ')')
308
+ {
309
+ if (*cp == '\0')
310
+ {
311
+ /* Closing parenthesis can be in follow up line. */
312
+ cp = (const char *) fileReadLine ();
313
+ if (!cp) break;
314
+ vStringPut (inheritance, ' ');
315
+ continue;
316
+ }
317
+ vStringPut (inheritance, *cp);
318
+ ++cp;
319
+ }
320
+ vStringTerminate (inheritance);
321
+ }
322
+ makeClassTag (class, inheritance, parent, is_class_parent);
323
+ vStringDelete (inheritance);
324
+ }
325
+
326
+ static void parseImports (const char *cp)
327
+ {
328
+ const char *pos;
329
+ vString *name, *name_next;
330
+
331
+ cp = skipEverything (cp);
332
+
333
+ if ((pos = strstr (cp, "import")) == NULL)
334
+ return;
335
+
336
+ cp = pos + 6;
337
+
338
+ /* continue only if there is some space between the keyword and the identifier */
339
+ if (! isspace (*cp))
340
+ return;
341
+
342
+ cp++;
343
+ cp = skipSpace (cp);
344
+
345
+ name = vStringNew ();
346
+ name_next = vStringNew ();
347
+
348
+ cp = skipEverything (cp);
349
+ while (*cp)
350
+ {
351
+ cp = parseIdentifier (cp, name);
352
+
353
+ cp = skipEverything (cp);
354
+ /* we parse the next possible import statement as well to be able to ignore 'foo' in
355
+ * 'import foo as bar' */
356
+ parseIdentifier (cp, name_next);
357
+
358
+ /* take the current tag only if the next one is not "as" */
359
+ if (strcmp (vStringValue (name_next), "as") != 0 &&
360
+ strcmp (vStringValue (name), "as") != 0)
361
+ {
362
+ makeSimpleTag (name, PythonKinds, K_IMPORT);
363
+ }
364
+ }
365
+ vStringDelete (name);
366
+ vStringDelete (name_next);
367
+ }
368
+
369
+ /* modified from get.c getArglistFromStr().
370
+ * warning: terminates rest of string past arglist!
371
+ * note: does not ignore brackets inside strings! */
372
+ static char *parseArglist(const char *buf)
373
+ {
374
+ char *start, *end;
375
+ int level;
376
+ if (NULL == buf)
377
+ return NULL;
378
+ if (NULL == (start = strchr(buf, '(')))
379
+ return NULL;
380
+ for (level = 1, end = start + 1; level > 0; ++end)
381
+ {
382
+ if ('\0' == *end)
383
+ break;
384
+ else if ('(' == *end)
385
+ ++ level;
386
+ else if (')' == *end)
387
+ -- level;
388
+ }
389
+ *end = '\0';
390
+ return strdup(start);
391
+ }
392
+
393
+ static void parseFunction (const char *cp, vString *const def,
394
+ vString *const parent, int is_class_parent)
395
+ {
396
+ char *arglist;
397
+
398
+ cp = parseIdentifier (cp, def);
399
+ arglist = parseArglist (cp);
400
+ makeFunctionTag (def, parent, is_class_parent, arglist);
401
+ eFree (arglist);
402
+ }
403
+
404
+ /* Get the combined name of a nested symbol. Classes are separated with ".",
405
+ * functions with "/". For example this code:
406
+ * class MyClass:
407
+ * def myFunction:
408
+ * def SubFunction:
409
+ * class SubClass:
410
+ * def Method:
411
+ * pass
412
+ * Would produce this string:
413
+ * MyClass.MyFunction/SubFunction/SubClass.Method
414
+ */
415
+ static boolean constructParentString(NestingLevels *nls, int indent,
416
+ vString *result)
417
+ {
418
+ int i;
419
+ NestingLevel *prev = NULL;
420
+ int is_class = FALSE;
421
+ vStringClear (result);
422
+ for (i = 0; i < nls->n; i++)
423
+ {
424
+ NestingLevel *nl = nls->levels + i;
425
+ if (indent <= nl->indentation)
426
+ break;
427
+ if (prev)
428
+ {
429
+ vStringCatS(result, "."); /* make Geany symbol list grouping work properly */
430
+ /*
431
+ if (prev->type == K_CLASS)
432
+ vStringCatS(result, ".");
433
+ else
434
+ vStringCatS(result, "/");
435
+ */
436
+ }
437
+ vStringCat(result, nl->name);
438
+ is_class = (nl->type == K_CLASS);
439
+ prev = nl;
440
+ }
441
+ return is_class;
442
+ }
443
+
444
+ /* Check whether parent's indentation level is higher than the current level and
445
+ * if so, remove it.
446
+ */
447
+ static void checkParent(NestingLevels *nls, int indent, vString *parent)
448
+ {
449
+ int i;
450
+ NestingLevel *n;
451
+
452
+ for (i = 0; i < nls->n; i++)
453
+ {
454
+ n = nls->levels + i;
455
+ /* is there a better way to compare two vStrings? */
456
+ if (strcmp(vStringValue(parent), vStringValue(n->name)) == 0)
457
+ {
458
+ if (n && indent <= n->indentation)
459
+ {
460
+ /* remove this level by clearing its name */
461
+ vStringClear(n->name);
462
+ }
463
+ break;
464
+ }
465
+ }
466
+ }
467
+
468
+ static void addNestingLevel(NestingLevels *nls, int indentation,
469
+ const vString *name, boolean is_class)
470
+ {
471
+ int i;
472
+ NestingLevel *nl = NULL;
473
+
474
+ for (i = 0; i < nls->n; i++)
475
+ {
476
+ nl = nls->levels + i;
477
+ if (indentation <= nl->indentation) break;
478
+ }
479
+ if (i == nls->n)
480
+ {
481
+ nestingLevelsPush(nls, name, 0);
482
+ nl = nls->levels + i;
483
+ }
484
+ else
485
+ { /* reuse existing slot */
486
+ nls->n = i + 1;
487
+ vStringCopy(nl->name, name);
488
+ }
489
+ nl->indentation = indentation;
490
+ nl->type = is_class ? K_CLASS : !K_CLASS;
491
+ }
492
+
493
+ /* Return a pointer to the start of the next triple string, or NULL. Store
494
+ * the kind of triple string in "which" if the return is not NULL.
495
+ */
496
+ static char const *find_triple_start(char const *string, char const **which)
497
+ {
498
+ char const *cp = string;
499
+
500
+ for (; *cp; cp++)
501
+ {
502
+ if (*cp == '"' || *cp == '\'')
503
+ {
504
+ if (strncmp(cp, doubletriple, 3) == 0)
505
+ {
506
+ *which = doubletriple;
507
+ return cp;
508
+ }
509
+ if (strncmp(cp, singletriple, 3) == 0)
510
+ {
511
+ *which = singletriple;
512
+ return cp;
513
+ }
514
+ cp = skipString(cp);
515
+ if (!*cp) break;
516
+ }
517
+ }
518
+ return NULL;
519
+ }
520
+
521
+ /* Find the end of a triple string as pointed to by "which", and update "which"
522
+ * with any other triple strings following in the given string.
523
+ */
524
+ static void find_triple_end(char const *string, char const **which)
525
+ {
526
+ char const *s = string;
527
+ while (1)
528
+ {
529
+ /* Check if the string ends in the same line. */
530
+ s = strstr (s, *which);
531
+ if (!s) break;
532
+ s += 3;
533
+ *which = NULL;
534
+ /* If yes, check if another one starts in the same line. */
535
+ s = find_triple_start(s, which);
536
+ if (!s) break;
537
+ s += 3;
538
+ }
539
+ }
540
+
541
+ static const char *findVariable(const char *line)
542
+ {
543
+ /* Parse global and class variable names (C.x) from assignment statements.
544
+ * Object attributes (obj.x) are ignored.
545
+ * Assignment to a tuple 'x, y = 2, 3' not supported.
546
+ * TODO: ignore duplicate tags from reassignment statements. */
547
+ const char *cp, *sp, *eq, *start;
548
+
549
+ cp = strstr(line, "=");
550
+ if (!cp)
551
+ return NULL;
552
+ eq = cp + 1;
553
+ while (*eq)
554
+ {
555
+ if (*eq == '=')
556
+ return NULL; /* ignore '==' operator and 'x=5,y=6)' function lines */
557
+ if (*eq == '(' || *eq == '#')
558
+ break; /* allow 'x = func(b=2,y=2,' lines and comments at the end of line */
559
+ eq++;
560
+ }
561
+
562
+ /* go backwards to the start of the line, checking we have valid chars */
563
+ start = cp - 1;
564
+ while (start >= line && isspace ((int) *start))
565
+ --start;
566
+ while (start >= line && isIdentifierCharacter ((int) *start))
567
+ --start;
568
+ if (!isIdentifierFirstCharacter(*(start + 1)))
569
+ return NULL;
570
+ sp = start;
571
+ while (sp >= line && isspace ((int) *sp))
572
+ --sp;
573
+ if ((sp + 1) != line) /* the line isn't a simple variable assignment */
574
+ return NULL;
575
+ /* the line is valid, parse the variable name */
576
+ ++start;
577
+ return start;
578
+ }
579
+
580
+ /* Skip type declaration that optionally follows a cdef/cpdef */
581
+ static const char *skipTypeDecl (const char *cp, boolean *is_class)
582
+ {
583
+ const char *lastStart = cp, *ptr = cp;
584
+ int loopCount = 0;
585
+ ptr = skipSpace(cp);
586
+ if (!strncmp("extern", ptr, 6)) {
587
+ ptr += 6;
588
+ ptr = skipSpace(ptr);
589
+ if (!strncmp("from", ptr, 4)) { return NULL; }
590
+ }
591
+ if (!strncmp("class", ptr, 5)) {
592
+ ptr += 5 ;
593
+ *is_class = TRUE;
594
+ ptr = skipSpace(ptr);
595
+ return ptr;
596
+ }
597
+ /* limit so that we don't pick off "int item=obj()" */
598
+ while (*ptr && loopCount++ < 2) {
599
+ while (*ptr && *ptr != '=' && *ptr != '(' && !isspace(*ptr)) ptr++;
600
+ if (!*ptr || *ptr == '=') return NULL;
601
+ if (*ptr == '(') {
602
+ return lastStart; /* if we stopped on a '(' we are done */
603
+ }
604
+ ptr = skipSpace(ptr);
605
+ lastStart = ptr;
606
+ while (*lastStart == '*') lastStart++; /* cdef int *identifier */
607
+ }
608
+ return NULL;
609
+ }
610
+
611
+ static void findPythonTags (void)
612
+ {
613
+ vString *const continuation = vStringNew ();
614
+ vString *const name = vStringNew ();
615
+ vString *const parent = vStringNew();
616
+
617
+ NestingLevels *const nesting_levels = nestingLevelsNew();
618
+
619
+ const char *line;
620
+ int line_skip = 0;
621
+ char const *longStringLiteral = NULL;
622
+
623
+ while ((line = (const char *) fileReadLine ()) != NULL)
624
+ {
625
+ const char *cp = line, *candidate;
626
+ char const *longstring;
627
+ char const *keyword, *variable;
628
+ int indent;
629
+
630
+ cp = skipSpace (cp);
631
+
632
+ if (*cp == '\0') /* skip blank line */
633
+ continue;
634
+
635
+ /* Skip comment if we are not inside a multi-line string. */
636
+ if (*cp == '#' && !longStringLiteral)
637
+ continue;
638
+
639
+ /* Deal with line continuation. */
640
+ if (!line_skip) vStringClear(continuation);
641
+ vStringCatS(continuation, line);
642
+ vStringStripTrailing(continuation);
643
+ if (vStringLast(continuation) == '\\')
644
+ {
645
+ vStringChop(continuation);
646
+ vStringCatS(continuation, " ");
647
+ line_skip = 1;
648
+ continue;
649
+ }
650
+ cp = line = vStringValue(continuation);
651
+ cp = skipSpace (cp);
652
+ indent = cp - line;
653
+ line_skip = 0;
654
+
655
+ checkParent(nesting_levels, indent, parent);
656
+
657
+ /* Deal with multiline string ending. */
658
+ if (longStringLiteral)
659
+ {
660
+ find_triple_end(cp, &longStringLiteral);
661
+ continue;
662
+ }
663
+
664
+ /* Deal with multiline string start. */
665
+ longstring = find_triple_start(cp, &longStringLiteral);
666
+ if (longstring)
667
+ {
668
+ longstring += 3;
669
+ find_triple_end(longstring, &longStringLiteral);
670
+ /* We don't parse for any tags in the rest of the line. */
671
+ continue;
672
+ }
673
+
674
+ /* Deal with def and class keywords. */
675
+ keyword = findDefinitionOrClass (cp);
676
+ if (keyword)
677
+ {
678
+ boolean found = FALSE;
679
+ boolean is_class = FALSE;
680
+ if (!strncmp (keyword, "def ", 4))
681
+ {
682
+ cp = skipSpace (keyword + 3);
683
+ found = TRUE;
684
+ }
685
+ else if (!strncmp (keyword, "class ", 6))
686
+ {
687
+ cp = skipSpace (keyword + 5);
688
+ found = TRUE;
689
+ is_class = TRUE;
690
+ }
691
+ else if (!strncmp (keyword, "cdef ", 5))
692
+ {
693
+ cp = skipSpace(keyword + 4);
694
+ candidate = skipTypeDecl (cp, &is_class);
695
+ if (candidate)
696
+ {
697
+ found = TRUE;
698
+ cp = candidate;
699
+ }
700
+
701
+ }
702
+ else if (!strncmp (keyword, "cpdef ", 6))
703
+ {
704
+ cp = skipSpace(keyword + 5);
705
+ candidate = skipTypeDecl (cp, &is_class);
706
+ if (candidate)
707
+ {
708
+ found = TRUE;
709
+ cp = candidate;
710
+ }
711
+ }
712
+
713
+ if (found)
714
+ {
715
+ boolean is_parent_class;
716
+
717
+ is_parent_class =
718
+ constructParentString(nesting_levels, indent, parent);
719
+
720
+ if (is_class)
721
+ parseClass (cp, name, parent, is_parent_class);
722
+ else
723
+ parseFunction(cp, name, parent, is_parent_class);
724
+
725
+ addNestingLevel(nesting_levels, indent, name, is_class);
726
+ }
727
+ }
728
+ /* Find global and class variables */
729
+ variable = findVariable(line);
730
+ if (variable)
731
+ {
732
+ const char *start = variable;
733
+ boolean parent_is_class;
734
+
735
+ vStringClear (name);
736
+ while (isIdentifierCharacter ((int) *start))
737
+ {
738
+ vStringPut (name, (int) *start);
739
+ ++start;
740
+ }
741
+ vStringTerminate (name);
742
+
743
+ parent_is_class = constructParentString(nesting_levels, indent, parent);
744
+ /* skip variables in methods */
745
+ if (! parent_is_class && vStringLength(parent) > 0)
746
+ continue;
747
+
748
+ makeVariableTag (name, parent);
749
+ }
750
+ /* Find and parse imports */
751
+ parseImports(line);
752
+ }
753
+ /* Clean up all memory we allocated. */
754
+ vStringDelete (parent);
755
+ vStringDelete (name);
756
+ vStringDelete (continuation);
757
+ nestingLevelsFree (nesting_levels);
758
+ }
759
+
760
+ extern parserDefinition *PythonParser (void)
761
+ {
762
+ static const char *const extensions[] = { "py", "pyx", "pxd", "pxi" ,"scons", NULL };
763
+ parserDefinition *def = parserNew ("Python");
764
+ def->kinds = PythonKinds;
765
+ def->kindCount = KIND_COUNT (PythonKinds);
766
+ def->extensions = extensions;
767
+ def->parser = findPythonTags;
768
+ return def;
769
+ }
770
+
771
+ /* vi:set tabstop=4 shiftwidth=4: */