ctags.rb 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/Rakefile +23 -0
- data/ctags.rb.gemspec +23 -0
- data/ext/.gitignore +3 -0
- data/ext/extconf.rb +15 -0
- data/ext/vendor/exuberant-ctags/.gitignore +6 -0
- data/ext/vendor/exuberant-ctags/.indent.pro +31 -0
- data/ext/vendor/exuberant-ctags/COPYING +340 -0
- data/ext/vendor/exuberant-ctags/EXTENDING.html +386 -0
- data/ext/vendor/exuberant-ctags/FAQ +371 -0
- data/ext/vendor/exuberant-ctags/INSTALL +215 -0
- data/ext/vendor/exuberant-ctags/INSTALL.oth +73 -0
- data/ext/vendor/exuberant-ctags/MAINTAINERS +88 -0
- data/ext/vendor/exuberant-ctags/Makefile.in +222 -0
- data/ext/vendor/exuberant-ctags/NEWS +871 -0
- data/ext/vendor/exuberant-ctags/README +73 -0
- data/ext/vendor/exuberant-ctags/ant.c +42 -0
- data/ext/vendor/exuberant-ctags/argproc.c +505 -0
- data/ext/vendor/exuberant-ctags/args.c +274 -0
- data/ext/vendor/exuberant-ctags/args.h +63 -0
- data/ext/vendor/exuberant-ctags/asm.c +387 -0
- data/ext/vendor/exuberant-ctags/asp.c +328 -0
- data/ext/vendor/exuberant-ctags/awk.c +81 -0
- data/ext/vendor/exuberant-ctags/basic.c +203 -0
- data/ext/vendor/exuberant-ctags/beta.c +321 -0
- data/ext/vendor/exuberant-ctags/c.c +2932 -0
- data/ext/vendor/exuberant-ctags/cobol.c +50 -0
- data/ext/vendor/exuberant-ctags/config.h.in +277 -0
- data/ext/vendor/exuberant-ctags/configure +7704 -0
- data/ext/vendor/exuberant-ctags/configure.ac +532 -0
- data/ext/vendor/exuberant-ctags/ctags.1 +1186 -0
- data/ext/vendor/exuberant-ctags/ctags.h +28 -0
- data/ext/vendor/exuberant-ctags/ctags.html +2087 -0
- data/ext/vendor/exuberant-ctags/ctags.spec +40 -0
- data/ext/vendor/exuberant-ctags/debug.c +113 -0
- data/ext/vendor/exuberant-ctags/debug.h +70 -0
- data/ext/vendor/exuberant-ctags/descrip.mms +68 -0
- data/ext/vendor/exuberant-ctags/dosbatch.c +42 -0
- data/ext/vendor/exuberant-ctags/e_amiga.h +24 -0
- data/ext/vendor/exuberant-ctags/e_djgpp.h +47 -0
- data/ext/vendor/exuberant-ctags/e_mac.h +143 -0
- data/ext/vendor/exuberant-ctags/e_msoft.h +76 -0
- data/ext/vendor/exuberant-ctags/e_os2.h +37 -0
- data/ext/vendor/exuberant-ctags/e_qdos.h +34 -0
- data/ext/vendor/exuberant-ctags/e_riscos.h +58 -0
- data/ext/vendor/exuberant-ctags/e_vms.h +31 -0
- data/ext/vendor/exuberant-ctags/eiffel.c +1352 -0
- data/ext/vendor/exuberant-ctags/entry.c +847 -0
- data/ext/vendor/exuberant-ctags/entry.h +103 -0
- data/ext/vendor/exuberant-ctags/erlang.c +189 -0
- data/ext/vendor/exuberant-ctags/flex.c +2243 -0
- data/ext/vendor/exuberant-ctags/fortran.c +2197 -0
- data/ext/vendor/exuberant-ctags/general.h +127 -0
- data/ext/vendor/exuberant-ctags/get.c +669 -0
- data/ext/vendor/exuberant-ctags/get.h +50 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/all-wcprops +47 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/entries +112 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/README.txt.svn-base +5 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regcomp.c.svn-base +3818 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex.c.svn-base +74 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex.h.svn-base +575 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex_internal.c.svn-base +1713 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex_internal.h.svn-base +773 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regexec.c.svn-base +4338 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/README.txt +5 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/regcomp.c +3818 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/regex.c +74 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/regex.h +575 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/regex_internal.c +1713 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/regex_internal.h +773 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/regexec.c +4338 -0
- data/ext/vendor/exuberant-ctags/html.c +49 -0
- data/ext/vendor/exuberant-ctags/jscript.c +1572 -0
- data/ext/vendor/exuberant-ctags/keyword.c +258 -0
- data/ext/vendor/exuberant-ctags/keyword.h +34 -0
- data/ext/vendor/exuberant-ctags/lisp.c +139 -0
- data/ext/vendor/exuberant-ctags/lregex.c +704 -0
- data/ext/vendor/exuberant-ctags/lua.c +133 -0
- data/ext/vendor/exuberant-ctags/mac.c +273 -0
- data/ext/vendor/exuberant-ctags/magic.diff +21 -0
- data/ext/vendor/exuberant-ctags/main.c +584 -0
- data/ext/vendor/exuberant-ctags/main.h +32 -0
- data/ext/vendor/exuberant-ctags/maintainer.mak +476 -0
- data/ext/vendor/exuberant-ctags/make.c +217 -0
- data/ext/vendor/exuberant-ctags/matlab.c +44 -0
- data/ext/vendor/exuberant-ctags/mk_bc3.mak +46 -0
- data/ext/vendor/exuberant-ctags/mk_bc5.mak +49 -0
- data/ext/vendor/exuberant-ctags/mk_djg.mak +18 -0
- data/ext/vendor/exuberant-ctags/mk_manx.mak +65 -0
- data/ext/vendor/exuberant-ctags/mk_mingw.mak +31 -0
- data/ext/vendor/exuberant-ctags/mk_mpw.mak +130 -0
- data/ext/vendor/exuberant-ctags/mk_mvc.mak +40 -0
- data/ext/vendor/exuberant-ctags/mk_os2.mak +104 -0
- data/ext/vendor/exuberant-ctags/mk_qdos.mak +100 -0
- data/ext/vendor/exuberant-ctags/mk_sas.mak +63 -0
- data/ext/vendor/exuberant-ctags/mkinstalldirs +40 -0
- data/ext/vendor/exuberant-ctags/ocaml.c +1842 -0
- data/ext/vendor/exuberant-ctags/options.c +1842 -0
- data/ext/vendor/exuberant-ctags/options.h +155 -0
- data/ext/vendor/exuberant-ctags/parse.c +677 -0
- data/ext/vendor/exuberant-ctags/parse.h +129 -0
- data/ext/vendor/exuberant-ctags/parsers.h +63 -0
- data/ext/vendor/exuberant-ctags/pascal.c +267 -0
- data/ext/vendor/exuberant-ctags/perl.c +382 -0
- data/ext/vendor/exuberant-ctags/php.c +237 -0
- data/ext/vendor/exuberant-ctags/python.c +771 -0
- data/ext/vendor/exuberant-ctags/qdos.c +106 -0
- data/ext/vendor/exuberant-ctags/read.c +569 -0
- data/ext/vendor/exuberant-ctags/read.h +116 -0
- data/ext/vendor/exuberant-ctags/readtags.c +959 -0
- data/ext/vendor/exuberant-ctags/readtags.h +252 -0
- data/ext/vendor/exuberant-ctags/rexx.c +39 -0
- data/ext/vendor/exuberant-ctags/routines.c +891 -0
- data/ext/vendor/exuberant-ctags/routines.h +134 -0
- data/ext/vendor/exuberant-ctags/ruby.c +408 -0
- data/ext/vendor/exuberant-ctags/scheme.c +111 -0
- data/ext/vendor/exuberant-ctags/sh.c +115 -0
- data/ext/vendor/exuberant-ctags/slang.c +41 -0
- data/ext/vendor/exuberant-ctags/sml.c +212 -0
- data/ext/vendor/exuberant-ctags/sort.c +230 -0
- data/ext/vendor/exuberant-ctags/sort.h +32 -0
- data/ext/vendor/exuberant-ctags/source.mak +122 -0
- data/ext/vendor/exuberant-ctags/sql.c +2112 -0
- data/ext/vendor/exuberant-ctags/strlist.c +281 -0
- data/ext/vendor/exuberant-ctags/strlist.h +54 -0
- data/ext/vendor/exuberant-ctags/tcl.c +116 -0
- data/ext/vendor/exuberant-ctags/tex.c +524 -0
- data/ext/vendor/exuberant-ctags/verilog.c +340 -0
- data/ext/vendor/exuberant-ctags/vhdl.c +835 -0
- data/ext/vendor/exuberant-ctags/vim.c +636 -0
- data/ext/vendor/exuberant-ctags/vstring.c +232 -0
- data/ext/vendor/exuberant-ctags/vstring.h +85 -0
- data/ext/vendor/exuberant-ctags/yacc.c +40 -0
- data/lib/ctags/exuberant.rb +45 -0
- data/lib/ctags/version.rb +3 -0
- data/lib/ctags.rb +6 -0
- data/test/test_ctags.rb +24 -0
- 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: */
|