Synchronized the python parser with the geany tagmanager, including the following geany commits:
r3788: nestlevel.c and some other stuff r3804: Parse Python import statements to get symbol completion for the imported module names. r3809: Parse Python calltips. r3831: Fix grouping functions/classes under a nested function. Many thanks to the geany devs! git-svn-id: svn://svn.code.sf.net/p/ctags/code/trunk@714 c5d04d22-be80-434c-894e-aa346cc9e8e8
This commit is contained in:
parent
969a16a8df
commit
d551db6f38
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* $Id: nestlevel.c 3788 2009-05-12 15:55:13Z ntrel $
|
||||
*
|
||||
* Copyright (c) 1999-2002, Darren Hiebert
|
||||
* Copyright 2009 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Defines external interface to scope nesting levels for tags.
|
||||
*/
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include "main.h"
|
||||
#include "nestlevel.h"
|
||||
#include "debug.h"
|
||||
#include "routines.h"
|
||||
|
||||
/*
|
||||
* FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
extern NestingLevels *nestingLevelsNew(void)
|
||||
{
|
||||
NestingLevels *nls = xCalloc (1, NestingLevels);
|
||||
return nls;
|
||||
}
|
||||
|
||||
extern void nestingLevelsFree(NestingLevels *nls)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < nls->allocated; i++)
|
||||
vStringDelete(nls->levels[i].name);
|
||||
if (nls->levels) eFree(nls->levels);
|
||||
eFree(nls);
|
||||
}
|
||||
|
||||
extern void nestingLevelsPush(NestingLevels *nls,
|
||||
const vString *name, int type)
|
||||
{
|
||||
NestingLevel *nl = NULL;
|
||||
|
||||
if (nls->n >= nls->allocated)
|
||||
{
|
||||
nls->allocated++;
|
||||
nls->levels = xRealloc(nls->levels,
|
||||
nls->allocated, NestingLevel);
|
||||
nls->levels[nls->n].name = vStringNew();
|
||||
}
|
||||
nl = &nls->levels[nls->n];
|
||||
nls->n++;
|
||||
|
||||
vStringCopy(nl->name, name);
|
||||
nl->type = type;
|
||||
}
|
||||
|
||||
extern void nestingLevelsPop(NestingLevels *nls)
|
||||
{
|
||||
const NestingLevel *nl = nestingLevelsGetCurrent(nls);
|
||||
|
||||
Assert (nl != NULL);
|
||||
vStringClear(nl->name);
|
||||
nls->n--;
|
||||
}
|
||||
|
||||
extern NestingLevel *nestingLevelsGetCurrent(NestingLevels *nls)
|
||||
{
|
||||
Assert (nls != NULL);
|
||||
|
||||
if (nls->n < 1)
|
||||
return NULL;
|
||||
|
||||
return &nls->levels[nls->n - 1];
|
||||
}
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* $Id: nestlevel.h 3788 2009-05-12 15:55:13Z ntrel $
|
||||
*
|
||||
* Copyright (c) 1999-2002, Darren Hiebert
|
||||
* Copyright 2009 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
|
||||
*
|
||||
* This source code is released for free distribution under the terms of the
|
||||
* GNU General Public License.
|
||||
*
|
||||
* Defines external interface to scope nesting levels for tags.
|
||||
*/
|
||||
#ifndef _NESTLEVEL_H
|
||||
#define _NESTLEVEL_H
|
||||
|
||||
/*
|
||||
* INCLUDE FILES
|
||||
*/
|
||||
#include "general.h" /* must always come first */
|
||||
|
||||
#include "vstring.h"
|
||||
|
||||
/*
|
||||
* DATA DECLARATIONS
|
||||
*/
|
||||
typedef struct NestingLevel NestingLevel;
|
||||
typedef struct NestingLevels NestingLevels;
|
||||
|
||||
struct NestingLevel
|
||||
{
|
||||
int indentation;
|
||||
vString *name;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct NestingLevels
|
||||
{
|
||||
NestingLevel *levels;
|
||||
int n; /* number of levels in use */
|
||||
int allocated;
|
||||
};
|
||||
|
||||
/*
|
||||
* FUNCTION PROTOTYPES
|
||||
*/
|
||||
extern NestingLevels *nestingLevelsNew(void);
|
||||
extern void nestingLevelsFree(NestingLevels *nls);
|
||||
extern void nestingLevelsPush(NestingLevels *nls,
|
||||
const vString *name, int type);
|
||||
extern void nestingLevelsPop(NestingLevels *nls);
|
||||
extern NestingLevel *nestingLevelsGetCurrent(NestingLevels *nls);
|
||||
|
||||
#endif /* _NESTLEVEL_H */
|
||||
|
||||
/* vi:set tabstop=4 shiftwidth=4: */
|
177
python.c
177
python.c
|
@ -19,38 +19,23 @@
|
|||
#include "entry.h"
|
||||
#include "options.h"
|
||||
#include "read.h"
|
||||
#include "routines.h"
|
||||
#include "main.h"
|
||||
#include "vstring.h"
|
||||
#include "nestlevel.h"
|
||||
|
||||
/*
|
||||
* DATA DEFINITIONS
|
||||
*/
|
||||
typedef enum {
|
||||
K_CLASS, K_FUNCTION, K_MEMBER, K_VARIABLE
|
||||
K_CLASS, K_FUNCTION, K_MEMBER, K_VARIABLE, K_IMPORT
|
||||
} pythonKind;
|
||||
|
||||
static kindOption PythonKinds[] = {
|
||||
{TRUE, 'c', "class", "classes"},
|
||||
{TRUE, 'f', "function", "functions"},
|
||||
{TRUE, 'm', "member", "class members"},
|
||||
{TRUE, 'v', "variable", "variables"}
|
||||
};
|
||||
|
||||
typedef struct NestingLevel NestingLevel;
|
||||
typedef struct NestingLevels NestingLevels;
|
||||
|
||||
struct NestingLevel
|
||||
{
|
||||
int indentation;
|
||||
vString *name;
|
||||
boolean is_class;
|
||||
};
|
||||
|
||||
struct NestingLevels
|
||||
{
|
||||
NestingLevel *levels;
|
||||
int n;
|
||||
int allocated;
|
||||
{TRUE, 'v', "variable", "variables"},
|
||||
{TRUE, 'i', "namespace", "imports"}
|
||||
};
|
||||
|
||||
static char const * const singletriple = "'''";
|
||||
|
@ -76,13 +61,14 @@ static boolean isIdentifierCharacter (int c)
|
|||
* extract all relevant information and create a tag.
|
||||
*/
|
||||
static void makeFunctionTag (vString *const function,
|
||||
vString *const parent, int is_class_parent)
|
||||
vString *const parent, int is_class_parent, const char *arglist)
|
||||
{
|
||||
tagEntryInfo tag;
|
||||
initTagEntry (&tag, vStringValue (function));
|
||||
|
||||
|
||||
tag.kindName = "function";
|
||||
tag.kind = 'f';
|
||||
//tag.extensionFields.arglist = arglist;
|
||||
|
||||
if (vStringLength (parent) > 0)
|
||||
{
|
||||
|
@ -263,11 +249,82 @@ static void parseClass (const char *cp, vString *const class,
|
|||
vStringDelete (inheritance);
|
||||
}
|
||||
|
||||
static void parseImports (const char *cp)
|
||||
{
|
||||
const char *pos;
|
||||
vString *name, *name_next;
|
||||
|
||||
cp = skipEverything (cp);
|
||||
|
||||
if ((pos = strstr (cp, "import")) == NULL)
|
||||
return;
|
||||
|
||||
cp = pos + 6;
|
||||
|
||||
/* continue only if there is some space between the keyword and the identifier */
|
||||
if (! isspace (*cp))
|
||||
return;
|
||||
|
||||
cp++;
|
||||
cp = skipSpace (cp);
|
||||
|
||||
name = vStringNew ();
|
||||
name_next = vStringNew ();
|
||||
|
||||
cp = skipEverything (cp);
|
||||
while (*cp)
|
||||
{
|
||||
cp = parseIdentifier (cp, name);
|
||||
|
||||
cp = skipEverything (cp);
|
||||
/* we parse the next possible import statement as well to be able to ignore 'foo' in
|
||||
* 'import foo as bar' */
|
||||
parseIdentifier (cp, name_next);
|
||||
|
||||
/* take the current tag only if the next one is not "as" */
|
||||
if (strcmp (vStringValue (name_next), "as") != 0 &&
|
||||
strcmp (vStringValue (name), "as") != 0)
|
||||
{
|
||||
makeSimpleTag (name, PythonKinds, K_IMPORT);
|
||||
}
|
||||
}
|
||||
vStringDelete (name);
|
||||
vStringDelete (name_next);
|
||||
}
|
||||
|
||||
/* modified from get.c getArglistFromStr().
|
||||
* warning: terminates rest of string past arglist!
|
||||
* note: does not ignore brackets inside strings! */
|
||||
static char *parseArglist(const char *buf)
|
||||
{
|
||||
char *start, *end;
|
||||
int level;
|
||||
if (NULL == buf)
|
||||
return NULL;
|
||||
if (NULL == (start = strchr(buf, '(')))
|
||||
return NULL;
|
||||
for (level = 1, end = start + 1; level > 0; ++end)
|
||||
{
|
||||
if ('\0' == *end)
|
||||
break;
|
||||
else if ('(' == *end)
|
||||
++ level;
|
||||
else if (')' == *end)
|
||||
-- level;
|
||||
}
|
||||
*end = '\0';
|
||||
return strdup(start);
|
||||
}
|
||||
|
||||
static void parseFunction (const char *cp, vString *const def,
|
||||
vString *const parent, int is_class_parent)
|
||||
{
|
||||
char *arglist;
|
||||
|
||||
cp = parseIdentifier (cp, def);
|
||||
makeFunctionTag (def, parent, is_class_parent);
|
||||
arglist = parseArglist (cp);
|
||||
makeFunctionTag (def, parent, is_class_parent, arglist);
|
||||
eFree (arglist);
|
||||
}
|
||||
|
||||
/* Get the combined name of a nested symbol. Classes are separated with ".",
|
||||
|
@ -295,13 +352,16 @@ static boolean constructParentString(NestingLevels *nls, int indent,
|
|||
break;
|
||||
if (prev)
|
||||
{
|
||||
if (prev->is_class)
|
||||
vStringCatS(result, "."); /* make Geany symbol list grouping work properly */
|
||||
/*
|
||||
if (prev->type == K_CLASS)
|
||||
vStringCatS(result, ".");
|
||||
else
|
||||
vStringCatS(result, "/");
|
||||
*/
|
||||
}
|
||||
vStringCat(result, nl->name);
|
||||
is_class = nl->is_class;
|
||||
is_class = (nl->type == K_CLASS);
|
||||
prev = nl;
|
||||
}
|
||||
return is_class;
|
||||
|
@ -331,27 +391,8 @@ static void checkParent(NestingLevels *nls, int indent, vString *parent)
|
|||
}
|
||||
}
|
||||
|
||||
static NestingLevels *newNestingLevels(void)
|
||||
{
|
||||
NestingLevels *nls = xCalloc (1, NestingLevels);
|
||||
return nls;
|
||||
}
|
||||
|
||||
static void freeNestingLevels(NestingLevels *nls)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < nls->allocated; i++)
|
||||
vStringDelete(nls->levels[i].name);
|
||||
if (nls->levels) eFree(nls->levels);
|
||||
eFree(nls);
|
||||
}
|
||||
|
||||
/* TODO: This is totally out of place in python.c, but strlist.h is not usable.
|
||||
* Maybe should just move these three functions to a separate file, even if no
|
||||
* other parser uses them.
|
||||
*/
|
||||
static void addNestingLevel(NestingLevels *nls, int indentation,
|
||||
vString *name, boolean is_class)
|
||||
const vString *name, boolean is_class)
|
||||
{
|
||||
int i;
|
||||
NestingLevel *nl = NULL;
|
||||
|
@ -363,20 +404,16 @@ static void addNestingLevel(NestingLevels *nls, int indentation,
|
|||
}
|
||||
if (i == nls->n)
|
||||
{
|
||||
if (i >= nls->allocated)
|
||||
{
|
||||
nls->allocated++;
|
||||
nls->levels = xRealloc(nls->levels,
|
||||
nls->allocated, NestingLevel);
|
||||
nls->levels[i].name = vStringNew();
|
||||
}
|
||||
nestingLevelsPush(nls, name, 0);
|
||||
nl = nls->levels + i;
|
||||
}
|
||||
nls->n = i + 1;
|
||||
|
||||
vStringCopy(nl->name, name);
|
||||
else
|
||||
{ /* reuse existing slot */
|
||||
nls->n = i + 1;
|
||||
vStringCopy(nl->name, name);
|
||||
}
|
||||
nl->indentation = indentation;
|
||||
nl->is_class = is_class;
|
||||
nl->type = is_class ? K_CLASS : !K_CLASS;
|
||||
}
|
||||
|
||||
/* Return a pointer to the start of the next triple string, or NULL. Store
|
||||
|
@ -467,18 +504,18 @@ static const char *findVariable(const char *line)
|
|||
}
|
||||
|
||||
/* Skip type declaration that optionally follows a cdef/cpdef */
|
||||
static const char *skipTypeDecl (const char *cp, boolean *is_class)
|
||||
{
|
||||
static const char *skipTypeDecl (const char *cp, boolean *is_class)
|
||||
{
|
||||
const char *lastStart = cp, *ptr = cp;
|
||||
int loopCount = 0;
|
||||
ptr = skipSpace(cp);
|
||||
if (!strncmp("extern", ptr, 6)) {
|
||||
ptr += 6;
|
||||
ptr = skipSpace(ptr);
|
||||
if (!strncmp("extern", ptr, 6)) {
|
||||
ptr += 6;
|
||||
ptr = skipSpace(ptr);
|
||||
if (!strncmp("from", ptr, 4)) { return NULL; }
|
||||
}
|
||||
if (!strncmp("class", ptr, 5)) {
|
||||
ptr += 5 ;
|
||||
ptr += 5 ;
|
||||
*is_class = TRUE;
|
||||
ptr = skipSpace(ptr);
|
||||
return ptr;
|
||||
|
@ -489,7 +526,7 @@ static const char *skipTypeDecl (const char *cp, boolean *is_class)
|
|||
if (!*ptr || *ptr == '=') return NULL;
|
||||
if (*ptr == '(') {
|
||||
return lastStart; /* if we stopped on a '(' we are done */
|
||||
}
|
||||
}
|
||||
ptr = skipSpace(ptr);
|
||||
lastStart = ptr;
|
||||
while (*lastStart == '*') lastStart++; /* cdef int *identifier */
|
||||
|
@ -503,7 +540,7 @@ static void findPythonTags (void)
|
|||
vString *const name = vStringNew ();
|
||||
vString *const parent = vStringNew();
|
||||
|
||||
NestingLevels *const nesting_levels = newNestingLevels();
|
||||
NestingLevels *const nesting_levels = nestingLevelsNew();
|
||||
|
||||
const char *line;
|
||||
int line_skip = 0;
|
||||
|
@ -540,7 +577,7 @@ static void findPythonTags (void)
|
|||
cp = skipSpace (cp);
|
||||
indent = cp - line;
|
||||
line_skip = 0;
|
||||
|
||||
|
||||
checkParent(nesting_levels, indent, parent);
|
||||
|
||||
/* Deal with multiline string ending. */
|
||||
|
@ -549,7 +586,7 @@ static void findPythonTags (void)
|
|||
find_triple_end(cp, &longStringLiteral);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* Deal with multiline string start. */
|
||||
longstring = find_triple_start(cp, &longStringLiteral);
|
||||
if (longstring)
|
||||
|
@ -578,7 +615,7 @@ static void findPythonTags (void)
|
|||
is_class = TRUE;
|
||||
}
|
||||
else if (!strncmp (keyword, "cdef ", 5))
|
||||
{
|
||||
{
|
||||
cp = skipSpace(keyword + 4);
|
||||
candidate = skipTypeDecl (cp, &is_class);
|
||||
if (candidate)
|
||||
|
@ -589,7 +626,7 @@ static void findPythonTags (void)
|
|||
|
||||
}
|
||||
else if (!strncmp (keyword, "cpdef ", 6))
|
||||
{
|
||||
{
|
||||
cp = skipSpace(keyword + 5);
|
||||
candidate = skipTypeDecl (cp, &is_class);
|
||||
if (candidate)
|
||||
|
@ -636,12 +673,14 @@ static void findPythonTags (void)
|
|||
|
||||
makeVariableTag (name, parent);
|
||||
}
|
||||
/* Find and parse imports */
|
||||
parseImports(line);
|
||||
}
|
||||
/* Clean up all memory we allocated. */
|
||||
vStringDelete (parent);
|
||||
vStringDelete (name);
|
||||
vStringDelete (continuation);
|
||||
freeNestingLevels (nesting_levels);
|
||||
nestingLevelsFree (nesting_levels);
|
||||
}
|
||||
|
||||
extern parserDefinition *PythonParser (void)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
HEADERS = \
|
||||
args.h ctags.h debug.h entry.h general.h get.h keyword.h \
|
||||
main.h options.h parse.h parsers.h read.h routines.h sort.h \
|
||||
strlist.h vstring.h
|
||||
strlist.h vstring.h nestlevel.h
|
||||
|
||||
SOURCES = \
|
||||
args.c \
|
||||
|
@ -33,6 +33,7 @@ SOURCES = \
|
|||
main.c \
|
||||
make.c \
|
||||
matlab.c \
|
||||
nestlevel.c \
|
||||
options.c \
|
||||
parse.c \
|
||||
pascal.c \
|
||||
|
@ -94,6 +95,7 @@ OBJECTS = \
|
|||
main.$(OBJEXT) \
|
||||
make.$(OBJEXT) \
|
||||
matlab.$(OBJEXT) \
|
||||
nestlevel.$(OBJEXT) \
|
||||
options.$(OBJEXT) \
|
||||
parse.$(OBJEXT) \
|
||||
pascal.$(OBJEXT) \
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
# Development makefile for Exuberant Ctags, used to build releases.
|
||||
# Requires GNU make.
|
||||
|
||||
CTAGS_TEST = ctags
|
||||
CTAGS_REF = ctags.ref
|
||||
CTAGS_TEST = ./ctags
|
||||
CTAGS_REF = ./ctags.ref
|
||||
TEST_OPTIONS = -nu --c-kinds=+lpx
|
||||
|
||||
DIFF_OPTIONS = -U 0 -I '^!_TAG'
|
||||
|
|
Loading…
Reference in New Issue