27
K_CLASS, K_FUNCTION, K_METHOD
27
K_CLASS, K_FUNCTION, K_METHOD, K_VARIABLE
30
30
static kindOption PythonKinds [] = {
31
31
{ TRUE, 'c', "class", "classes" },
32
32
{ TRUE, 'f', "function", "functions" },
33
{ TRUE, 'm', "member", "methods" }
33
{ TRUE, 'm', "member", "methods" },
34
{ TRUE, 'v', "variable", "variables" }
37
typedef struct _lastClass {
37
43
* FUNCTION DEFINITIONS
46
static boolean isIdentifierFirstCharacter (int c)
48
return (boolean) (isalpha (c) || c == '_');
51
static boolean isIdentifierCharacter (int c)
53
return (boolean) (isalnum (c) || c == '_');
57
/* remove all previous classes with more indent than the current one */
58
static GList *clean_class_list(GList *list, gint indent)
62
tmp = g_list_first(list);
65
if (((lastClass*)tmp->data)->indent >= indent)
67
g_free(((lastClass*)tmp->data)->name);
71
list = g_list_remove(list, tmp->data);
40
84
static void findPythonTags (void)
86
GList *parents = NULL, *tmp; /* list of classes which are around the token */
42
87
vString *name = vStringNew ();
43
vString *lastClass = vStringNew();
44
89
const unsigned char *line;
45
90
boolean inMultilineString = FALSE;
91
boolean wasInMultilineString = FALSE;
92
lastClass *lastclass = NULL;
93
boolean inFunction = FALSE;
47
96
while ((line = fileReadLine ()) != NULL)
49
98
const unsigned char *cp = line;
51
100
while (*cp != '\0')
54
103
strncmp ((const char*) cp, "\"\"\"", (size_t) 3) == 0)
56
105
inMultilineString = (boolean) !inMultilineString;
106
if (! inMultilineString)
107
wasInMultilineString = TRUE;
60
111
strncmp ((const char*) cp, "'''", (size_t) 3) == 0)
62
113
inMultilineString = (boolean) !inMultilineString;
114
if (! inMultilineString)
115
wasInMultilineString = TRUE;
65
if (inMultilineString || isspace ((int) *cp))
119
if (*cp == '\0' || wasInMultilineString)
121
wasInMultilineString = FALSE;
122
break; /* at end of multiline string */
125
/* update indent-sensitive things */
126
if (!inMultilineString && !isspace(*cp))
130
if (indent < fn_indent)
133
if (lastclass != NULL)
135
if (indent <= lastclass->indent)
139
parents = clean_class_list(parents, indent);
140
last = g_list_last(parents);
142
lastclass = last->data;
149
if (inMultilineString)
151
else if (isspace ((int) *cp))
153
/* count indentation amount of current line
154
* the indentation has to be made with tabs only _or_ spaces only, if they are mixed
155
* the code below gets confused */
162
} while (isspace(*cp));
165
cp++; /* non-indent whitespace */
67
167
else if (*cp == '#')
69
169
else if (strncmp ((const char*) cp, "class", (size_t) 5) == 0)
72
if (isspace ((int) *cp))
74
while (isspace ((int) *cp))
76
while (isalnum ((int) *cp) || *cp == '_')
78
vStringPut (name, (int) *cp);
81
vStringTerminate (name);
82
makeSimpleTag (name, PythonKinds, K_CLASS);
83
vStringCopy (lastClass, name);
172
if (isspace ((int) *cp))
174
lastClass *newclass = g_new(lastClass, 1);
176
while (isspace ((int) *cp))
178
while (isalnum ((int) *cp) || *cp == '_')
180
vStringPut (name, (int) *cp);
183
vStringTerminate (name);
185
newclass->name = g_strdup(vStringValue(name));
186
newclass->indent = indent;
187
parents = g_list_append(parents, newclass);
188
if (lastclass == NULL)
189
makeSimpleTag (name, PythonKinds, K_CLASS);
191
makeSimpleScopedTag (name, PythonKinds, K_CLASS,
192
PythonKinds[K_CLASS].name, lastclass->name, "public");
195
lastclass = newclass;
196
break; /* ignore rest of line so that lastclass is not reset immediately */
87
199
else if (strncmp ((const char*) cp, "def", (size_t) 3) == 0)
99
211
vStringTerminate (name);
100
if (!isspace(*line) || vStringLength(lastClass) <= 0)
212
if (!isspace(*line) || lastclass == NULL || strlen(lastclass->name) <= 0)
101
213
makeSimpleTag (name, PythonKinds, K_FUNCTION);
103
215
makeSimpleScopedTag (name, PythonKinds, K_METHOD,
104
PythonKinds[K_CLASS].name,
105
vStringValue(lastClass), "public");
216
PythonKinds[K_CLASS].name, lastclass->name, "public");
106
217
vStringClear (name);
220
fn_indent = indent + 1;
221
break; /* ignore rest of line so inFunction is not cancelled immediately */
224
else if (!inFunction && *(const char*)cp == '=')
226
/* Parse global and class variable names (C.x) from assignment statements.
227
* Object attributes (obj.x) are ignored.
228
* Assignment to a tuple 'x, y = 2, 3' not supported.
229
* TODO: ignore duplicate tags from reassignment statements. */
230
const guchar *sp, *eq, *start;
236
goto skipvar; /* ignore '==' operator and 'x=5,y=6)' function lines */
238
break; /* allow 'x = func(b=2,y=2,' lines */
241
/* go backwards to the start of the line, checking we have valid chars */
243
while (start >= line && isspace ((int) *start))
245
while (start >= line && isIdentifierCharacter ((int) *start))
247
if (!isIdentifierFirstCharacter(*(start + 1)))
250
while (sp >= line && isspace ((int) *sp))
252
if ((sp + 1) != line) /* the line isn't a simple variable assignment */
254
/* the line is valid, parse the variable name */
256
while (isIdentifierCharacter ((int) *start))
258
vStringPut (name, (int) *start);
261
vStringTerminate (name);
263
if (lastclass == NULL)
264
makeSimpleTag (name, PythonKinds, K_VARIABLE);
266
makeSimpleScopedTag (name, PythonKinds, K_VARIABLE,
267
PythonKinds[K_CLASS].name, lastclass->name, "public"); /* class member variables */
109
274
else if (*cp != '\0')