[geany/geany] 2fdd94: Merge pull request #3157 from techee/tcl_sync

Jiří Techet git-noreply at geany.org
Thu May 12 22:45:31 UTC 2022


Branch:      refs/heads/master
Author:      Jiří Techet <techet at gmail.com>
Committer:   GitHub <noreply at github.com>
Date:        Thu, 12 May 2022 22:45:31 UTC
Commit:      2fdd948c57dfd6677b18b8e49e0d7665dee6ae3e
             https://github.com/geany/geany/commit/2fdd948c57dfd6677b18b8e49e0d7665dee6ae3e

Log Message:
-----------
Merge pull request #3157 from techee/tcl_sync

Use the upstream TCL parser


Modified Paths:
--------------
    ctags/Makefile.am
    ctags/parsers/geany_tcl.c
    ctags/parsers/tcl.c
    ctags/parsers/tcl.h
    ctags/parsers/tcloo.c
    meson.build
    src/filetypes.c
    src/tagmanager/tm_parser.c
    src/tagmanager/tm_parser.h
    src/tagmanager/tm_parsers.h
    tests/ctags/simple.tcl
    tests/ctags/simple.tcl.tags

Modified: ctags/Makefile.am
4 lines changed, 3 insertions(+), 1 deletions(-)
===================================================================
@@ -89,7 +89,9 @@ parsers = \
 	parsers/rust.c \
 	parsers/sh.c \
 	parsers/sql.c \
-	parsers/geany_tcl.c \
+	parsers/tcl.c \
+	parsers/tcl.h \
+	parsers/tcloo.c \
 	parsers/geany_tex.c \
 	parsers/txt2tags.c \
 	parsers/verilog.c \


Modified: ctags/parsers/geany_tcl.c
145 lines changed, 0 insertions(+), 145 deletions(-)
===================================================================
@@ -1,145 +0,0 @@
-/*
-*   Copyright (c) 2000-2003, Darren Hiebert
-*
-*   This source code is released for free distribution under the terms of the
-*   GNU General Public License version 2 or (at your option) any later version.
-*
-*   This module contains functions for generating tags for TCL scripts.
-*/
-
-/*
-*   INCLUDE FILES
-*/
-#include "general.h"  /* must always come first */
-
-#include <string.h>
-
-#include "parse.h"
-#include "read.h"
-#include "routines.h"
-#include "vstring.h"
-
-/*
-*   DATA DEFINITIONS
-*/
-typedef enum {
-	K_CLASS, K_METHOD, K_PROCEDURE, K_MODULE
-} tclKind;
-
-static kindDefinition TclKinds [] = {
-	{ true, 'c', "class",     "classes" },
-	{ true, 'm', "method",    "methods" },
-	{ true, 'p', "procedure", "procedures" },
-	{ true, 'n', "module",    "modules" }
-};
-
-/*
-*   FUNCTION DEFINITIONS
-*/
-
-static const unsigned char *makeTclTag (
-		const unsigned char *cp,
-		vString *const name,
-		const tclKind kind)
-{
-	vStringClear (name);
-	while ((int) *cp != '\0'  &&  ! isspace ((int) *cp))
-	{
-		vStringPut (name, (int) *cp);
-		++cp;
-	}
-	makeSimpleTag (name, kind);
-	return cp;
-}
-
-static bool match (const unsigned char *line, const char *word)
-{
-	size_t len = strlen (word);
-	bool matched = (strncmp ((const char*) line, word, len) == 0);
-
-	if (matched)
-	{
-		/* check that the word is followed by a space to avoid detecting something
-		 * like "proc_new ..." */
-		matched = isspace (*(line + len));
-	}
-	return matched;
-}
-
-static void findTclTags (void)
-{
-	vString *name = vStringNew ();
-	const unsigned char *line;
-
-	while ((line = readLineFromInputFile ()) != NULL)
-	{
-		const unsigned char *cp;
-
-		while (isspace (line [0])) 
-			++line;
-		
-		if (line [0] == '\0'  ||  line [0] == '#')
-			continue;
-
-		/* read first word */
-		for (cp = line ; *cp != '\0'  &&  ! isspace ((int) *cp) ; ++cp)
-			;
-		if (! isspace ((int) *cp))
-			continue;
-		while (isspace ((int) *cp))
-			++cp;
-		/* Now `line' points at first word and `cp' points at next word */
-
-		if (match (line, "proc"))
-			cp = makeTclTag (cp, name, K_PROCEDURE);
-		else if (match (line, "class") || match (line, "itcl::class"))
-			cp = makeTclTag (cp, name, K_CLASS);
-		else if (match (line, "public") ||
-				match (line, "protected") ||
-				match (line, "private"))
-		{
-			if (match (cp, "method"))
-			{
-				cp += 6;
-				while (isspace ((int) *cp))
-					++cp;
-				cp = makeTclTag (cp, name, K_METHOD);
-			}
-		}
-		else if (match (line, "method"))
-		{
-			cp = makeTclTag (cp, name, K_METHOD);
-		}
-		else if (match (line, "oo::class") ) {
-			if (match (cp, "create"))
-			{
-				cp += 6;
-				while (isspace ((int) *cp))
-					++cp;
-				cp = makeTclTag (cp, name, K_CLASS);
-			}
-		}
-		else if (match (line, "namespace") ) {
-			if (match (cp, "eval"))
-			{
-				cp += 4;
-				while (isspace ((int) *cp))
-					++cp;
-				cp = makeTclTag (cp, name, K_MODULE);
-			}
-		}
-
-	}
-	vStringDelete (name);
-}
-
-extern parserDefinition* TclParser (void)
-{
-	static const char *const extensions [] = { "tcl", "tk", "wish", "itcl", NULL };
-	parserDefinition* def = parserNew ("Tcl");
-	def->kindTable  = TclKinds;
-	def->kindCount  = ARRAY_SIZE (TclKinds);
-	def->extensions = extensions;
-	def->parser     = findTclTags;
-	return def;
-}


Modified: ctags/parsers/tcl.c
716 lines changed, 716 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,716 @@
+/*
+*   Copyright (c) 2000-2003, Darren Hiebert
+*   Copyright (c) 2017, Masatake YAMATO
+*   Copyright (c) 2017, Red Hat, Inc.
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License version 2 or (at your option) any later version.
+*
+*   This module contains functions for generating tags for TCL scripts.
+*/
+
+/*
+*   INCLUDE FILES
+*/
+#include "general.h"  /* must always come first */
+#include "tokeninfo.h"
+#include "parse.h"
+#include "read.h"
+#include "vstring.h"
+#include "keyword.h"
+#include "entry.h"
+#include "routines.h"
+#include "ptrarray.h"
+#include "tcl.h"
+
+#include <string.h>
+
+
+
+/*
+*   DATA DEFINITIONS
+*/
+typedef enum {
+	K_PROCEDURE, K_NAMESPACE, K_PARAMETER,
+} tclKind;
+
+static scopeSeparator TclParameterSeparators [] = {
+	{ K_PROCEDURE        , "{" },
+};
+
+static kindDefinition TclKinds [] = {
+	{ true, 'p', "procedure", "procedures", },
+	{ true, 'n', "namespace", "namespaces", },
+	{ false, 'z', "parameter", "procedure parameters",
+	  ATTACH_SEPARATORS(TclParameterSeparators)},
+};
+
+enum {
+	KEYWORD_PROC,
+	KEYWORD_NAMESPACE,
+	KEYWORD_EVAL,
+	KEYWORD_PACKAGE,
+};
+
+typedef int keywordId; /* to allow KEYWORD_NONE */
+
+
+static const keywordTable TclKeywordTable[] = {
+	/* keyword			keyword ID */
+	{ "proc",			KEYWORD_PROC		},
+	{ "namespace",		KEYWORD_NAMESPACE	},
+	{ "eval",			KEYWORD_EVAL		},
+	{ "package",        KEYWORD_PACKAGE     },
+};
+
+typedef struct sCollector collector;
+struct sCollector {
+	void (* proc) (const tokenInfo *const, collector *);
+	vString *str;
+	int depth;
+	int scopeIndex;
+	int nth;
+};
+
+/*
+*   FUNCTION DEFINITIONS
+*/
+
+static bool tokenIsEOL (const tokenInfo *const token);
+
+static void initToken (tokenInfo *token, void *data);
+static void readToken (tokenInfo *const token, void *data);
+static void clearToken (tokenInfo *token);
+static void copyToken (tokenInfo *dest, tokenInfo *src, void *data CTAGS_ATTR_UNUSED);
+
+struct sTclParserState {
+	enum TclTokenType lastTokenType;
+};
+
+typedef struct sTclToken {
+	tokenInfo base;
+	int scopeIndex;
+	struct sTclParserState *pstate;
+} tclToken;
+
+#define TCL(TOKEN) ((tclToken *)TOKEN)
+#define TCL_PSTATE(TOKEN) (TCL(TOKEN)->pstate)
+
+static struct tokenTypePair typePairs [] = {
+	{ '{', '}' },
+	{ '[', ']' },
+};
+
+
+static struct tokenInfoClass tclTokenInfoClass = {
+	.nPreAlloc = 4,
+	.typeForUndefined = TOKEN_TCL_UNDEFINED,
+	.keywordNone      = KEYWORD_NONE,
+	.typeForKeyword   = TOKEN_TCL_KEYWORD,
+	.typeForEOF       = TOKEN_TCL_EOF,
+	.extraSpace       = sizeof (tclToken) - sizeof (tokenInfo),
+	.pairs            = typePairs,
+	.pairCount        = ARRAY_SIZE (typePairs),
+	.init             = initToken,
+	.read             = readToken,
+	.clear            = clearToken,
+	.copy             = copyToken,
+};
+
+extern tokenInfo *newTclToken (void *pstate)
+{
+	return newTokenFull (&tclTokenInfoClass, pstate);
+}
+
+static void clearToken (tokenInfo *token)
+{
+	TCL (token)->scopeIndex = CORK_NIL;
+	TCL (token)->pstate = NULL;
+}
+
+static void copyToken (tokenInfo *dest, tokenInfo *src, void *data CTAGS_ATTR_UNUSED)
+{
+	TCL (dest)->scopeIndex =
+		TCL (src)->scopeIndex;
+	TCL (dest)->pstate =
+		TCL (src)->pstate;
+}
+
+static void readString (vString *string)
+{
+	int c;
+	bool escaped = false;
+
+	while (1)
+	{
+		c = getcFromInputFile ();
+		switch (c)
+		{
+		case EOF:
+			return;
+		case '\\':
+			if (escaped)
+			{
+				vStringPut (string, c);
+				escaped = false;
+			}
+			else
+				escaped = true;
+			break;
+		case '"':
+			vStringPut (string, c);
+			if (escaped)
+				escaped = false;
+			else
+				return;
+			break;
+		default:
+			escaped = false;
+			vStringPut (string, c);
+			break;
+		}
+	}
+}
+
+static void readIdentifier (vString *string)
+{
+	while (1)
+	{
+		int c = getcFromInputFile ();
+		if (isgraph (c) && (!strchr ("{}[]", c)))
+			vStringPut (string, c);
+		else
+		{
+			ungetcToInputFile (c);
+			break;
+		}
+	}
+}
+
+static keywordId resolveKeyword (vString *string)
+{
+	char *s = vStringValue (string);
+	static langType lang = LANG_AUTO;
+
+	if (lang == LANG_AUTO)
+		lang = getInputLanguage ();
+
+	return lookupKeyword (s, lang);
+}
+
+static void initToken (tokenInfo *token, void *data)
+{
+	TCL (token)->pstate = data;
+}
+
+static void readToken0 (tokenInfo *const token, struct sTclParserState *pstate)
+{
+	int c = EOF;
+	bool escaped;
+	bool bol = (pstate->lastTokenType == TOKEN_TCL_EOL
+				|| pstate->lastTokenType == ';'
+				|| pstate->lastTokenType == TOKEN_TCL_UNDEFINED);
+	token->type		= TOKEN_TCL_UNDEFINED;
+	token->keyword	= KEYWORD_NONE;
+	vStringClear (token->string);
+
+ getNextChar:
+	escaped = false;
+
+	do {
+		c = getcFromInputFile ();
+	} while (c == ' ' || c== '\t' || c == '\f');
+
+	if (c == '\\')
+	{
+		bol = false;
+		int c0 = getcFromInputFile ();
+		switch (c0)
+		{
+		case '\n':
+		case '\r':
+			goto getNextChar;
+		default:
+			escaped = true;
+			c = c0;
+			break;
+		}
+	}
+
+	token->lineNumber   = getInputLineNumber ();
+	token->filePosition = getInputFilePosition ();
+
+	switch (c)
+	{
+	case EOF:
+		token->type = TOKEN_TCL_EOF;
+		break;
+	case '\n':
+	case '\r':
+		token->type = TOKEN_TCL_EOL;
+		break;
+	case '#':
+		if (!escaped)
+		{
+			if (bol)
+			{
+				do
+					c = getcFromInputFile ();
+				while (c != EOF && c != '\r' && c != '\n');
+			}
+			goto getNextChar;
+		}
+	case '"':
+		if (!escaped)
+		{
+			token->type = TOKEN_TCL_STRING;
+			tokenPutc (token, c);
+			readString (token->string);
+			break;
+		}
+	case ';':
+	case '{':
+	case '}':
+	case '[':
+	case ']':
+		if (!escaped)
+		{
+			tokenPutc (token, c);
+			token->type = c;
+			break;
+		}
+	case '$':
+		if (!escaped)
+		{
+			tokenPutc (token, c);
+			token->type = TOKEN_TCL_VARIABLE;
+
+			int c0 = getcFromInputFile ();
+			if (c0 == EOF)
+				break;
+
+			if (c0 == '{')
+			{
+				tokenPutc (token, c0);
+				while ((c0 = getcFromInputFile ()) != EOF)
+				{
+					tokenPutc (token, c0);
+					if (c0 == '}')
+						break;
+				}
+			}
+			else if (isalnum (c0))
+			{
+				tokenPutc (token, c0);
+				readIdentifier (token->string);
+			}
+			else
+				ungetcToInputFile (c0);
+			break;
+		}
+	default:
+			tokenPutc (token, c);
+			readIdentifier (token->string);
+
+			token->keyword = resolveKeyword (token->string);
+			if (token->keyword == KEYWORD_NONE)
+				token->type = TOKEN_TCL_IDENTIFIER;
+			else
+				token->type = TOKEN_TCL_KEYWORD;
+			break;
+	}
+}
+
+static void readToken (tokenInfo *const token, void *data)
+{
+	struct sTclParserState *pstate = TCL_PSTATE(token);
+
+	readToken0 (token, pstate);
+
+	pstate->lastTokenType = token->type;
+
+	if (data)
+	{
+		collector *col = data;
+		col->proc (token, col);
+	}
+}
+
+static bool tokenIsEOL (const tokenInfo *const token)
+{
+	if (token->type == ';'
+		|| tokenIsType (token, TCL_EOL)
+		|| tokenIsEOF (token))
+		return true;
+	return false;
+}
+
+static void skipToEndOfCmdline (tokenInfo *const token)
+{
+	while (!tokenIsEOL (token))
+	{
+		if ((token->type == '{')
+			|| (token->type == '['))
+			tokenSkipOverPair(token);
+		tokenRead (token);
+	}
+}
+
+extern void skipToEndOfTclCmdline (tokenInfo *const token)
+{
+	skipToEndOfCmdline (token);
+}
+
+static bool isAbsoluteIdentifier(tokenInfo *const token)
+{
+	return !strncmp (tokenString (token), "::", 2);
+}
+
+static const char* getLastComponentInIdentifier(tokenInfo *const token)
+{
+	const char* s = tokenString (token);
+	char *last = strrstr(s, "::");
+
+	if (last)
+		return last + 2;
+	else
+		return NULL;
+}
+
+
+static void notifyNamespaceImport (tokenInfo *const token)
+{
+	subparser *sub;
+
+	foreachSubparser (sub, false)
+	{
+		tclSubparser *tclsub = (tclSubparser *)sub;
+
+		if (tclsub->namespaceImportNotify)
+		{
+			enterSubparser(sub);
+			tclsub->namespaceImportNotify (tclsub, tokenString (token),
+										   TCL_PSTATE(token));
+			leaveSubparser();
+		}
+	}
+}
+
+static int notifyCommand (tokenInfo *const token, int parent)
+{
+	subparser *sub;
+	int r = CORK_NIL;
+
+	foreachSubparser (sub, false)
+	{
+		tclSubparser *tclsub = (tclSubparser *)sub;
+
+		if (tclsub->commandNotify)
+		{
+			enterSubparser(sub);
+			r = tclsub->commandNotify (tclsub, tokenString (token), parent,
+									   TCL_PSTATE(token));
+			leaveSubparser();
+			if (r != CORK_NIL)
+				break;
+		}
+	}
+	return r;
+}
+
+static void collectSignature (const tokenInfo *const token, collector * col)
+{
+	if (tokenIsEOL (token))
+		return;
+
+	if (tokenIsType (token, TCL_IDENTIFIER) &&
+		(col->depth == 1
+		 || (col->depth == 2 && tokenIsType (token, TCL_IDENTIFIER)
+			 && vStringLast (col->str) == '{')))
+	{
+		tagEntryInfo e;
+		initTagEntry (&e, tokenString (token), K_PARAMETER);
+		e.extensionFields.scopeIndex = col->scopeIndex;
+		e.extensionFields.nth = col->nth++;
+		makeTagEntry (&e);
+	}
+	else if (tokenIsTypeVal (token, '{'))
+		col->depth++;
+	else if (tokenIsTypeVal (token, '}'))
+		col->depth--;
+
+	if ((vStringLength (col->str) > 0
+		 && vStringLast (col->str) != '{'
+		 && vStringLast (col->str) != '['
+		 && vStringLast (col->str) != '(')
+		&& (!tokenIsTypeVal (token, '}'))
+		&& (!tokenIsTypeVal (token, ']'))
+		&& (!tokenIsTypeVal (token, ')')))
+		vStringPut (col->str, ' ');
+	vStringCat (col->str, token->string);
+}
+
+static void parseProc (tokenInfo *const token,
+					   int parent)
+{
+	int index = CORK_NIL;
+	int index_fq = CORK_NIL;
+
+	tokenRead (token);
+
+	if (tokenIsType(token, TCL_IDENTIFIER))
+	{
+		const char *last = getLastComponentInIdentifier (token);
+		if (last)
+		{
+			tagEntryInfo e;
+
+			initTagEntry (&e, last, K_PROCEDURE);
+			e.lineNumber = token->lineNumber;
+			e.filePosition = token->filePosition;
+
+			int len  = (last - tokenString (token));
+			vString *ns = vStringNew();
+			tagEntryInfo *e_parent = getEntryInCorkQueue (parent);
+			if (isAbsoluteIdentifier (token))
+			{
+				if (len > 2)
+					vStringNCopy (ns, token->string, len - 2);
+			}
+			else if (e_parent)
+			{
+				const char * sep = scopeSeparatorFor (getInputLanguage(),
+													  K_PROCEDURE,
+													  e_parent->kindIndex);
+				vStringCatS(ns, e_parent->name);
+				vStringCatS(ns, sep);
+				vStringNCopy(ns, token->string, len - 2);
+			}
+			else
+				vStringNCopy (ns, token->string, len - 2);
+
+			if (vStringLength(ns) > 0)
+			{
+				e.extensionFields.scopeKindIndex = K_NAMESPACE;
+				e.extensionFields.scopeName = vStringValue (ns);
+			}
+
+			e.skipAutoFQEmission = 1;
+			index = makeTagEntry (&e);
+
+			if (isXtagEnabled(XTAG_QUALIFIED_TAGS))
+			{
+				const char * sep = scopeSeparatorFor (getInputLanguage(),
+													  K_PROCEDURE,
+													  vStringIsEmpty (ns)
+													  ? KIND_GHOST_INDEX
+													  : K_NAMESPACE);
+
+				vStringCatS (ns, sep);
+				vStringCatS (ns, last);
+
+				index_fq = makeSimpleTag (ns, K_PROCEDURE);
+				tagEntryInfo *e_fq = getEntryInCorkQueue (index_fq);
+				if (e_fq)
+					markTagExtraBit (e_fq, XTAG_QUALIFIED_TAGS);
+			}
+			vStringDelete (ns);
+		}
+		else
+		{
+			tagEntryInfo *ep;
+			index = makeSimpleTag (token->string, K_PROCEDURE);
+			ep = getEntryInCorkQueue (index);
+			if (ep)
+				ep->extensionFields.scopeIndex = parent;
+		}
+	}
+
+	vString *signature = NULL;
+	if (!tokenIsEOL (token))
+	{
+		tokenRead (token);
+		if (tokenIsType (token, TCL_IDENTIFIER))
+		{
+			tagEntryInfo e;
+			initTagEntry (&e, tokenString (token), K_PARAMETER);
+			e.extensionFields.scopeIndex = index;
+			makeTagEntry (&e);
+			signature = vStringNewCopy (token->string);
+		}
+		else if (token->type == '{')
+		{
+			signature = vStringNewInit ("{");
+			collector col = {
+				.proc = collectSignature,
+				.str = signature,
+				.depth = 1,
+				.scopeIndex = index,
+				.nth = 0,
+			};
+			tokenSkipOverPairFull (token, &col);
+		}
+
+		skipToEndOfCmdline(token);
+	}
+
+	tagEntryInfo *e = getEntryInCorkQueue (index);
+	if (e)
+	{
+		e->extensionFields.endLine = token->lineNumber;
+
+		if (signature)
+		{
+			e->extensionFields.signature = vStringDeleteUnwrap (signature);
+			signature = NULL;
+		}
+
+		tagEntryInfo *e_fq = getEntryInCorkQueue (index_fq);
+		if (e_fq)
+		{
+			const char *sig = e->extensionFields.signature;
+			e_fq->extensionFields.endLine = token->lineNumber;
+			if (sig)
+				e_fq->extensionFields.signature = eStrdup (sig);
+		}
+	}
+	vStringDelete (signature);	/* NULL is acceptable */
+}
+
+static void parseNamespace (tokenInfo *const token,
+							int parent)
+{
+	tokenRead (token);
+	if (tokenIsEOF(token))
+		return;
+
+	if (tokenIsType (token, TCL_IDENTIFIER) &&
+		(strcmp(tokenString(token), "import") == 0))
+	{
+		while (1)
+		{
+			tokenRead (token);
+
+			if (!tokenIsType (token, TCL_IDENTIFIER))
+				break;
+
+			if (tokenString(token)[0] == '-')
+				continue;
+
+			notifyNamespaceImport (token);
+		}
+		skipToEndOfCmdline(token);
+		return;
+	}
+	else if (!tokenIsKeyword (token, EVAL))
+		return;
+
+	tokenRead (token);
+	if (!tokenIsType (token, TCL_IDENTIFIER))
+	{
+		skipToEndOfCmdline(token);
+		return;
+	}
+
+	int index = makeSimpleTag (token->string, K_NAMESPACE);
+	tagEntryInfo *e = getEntryInCorkQueue (index);
+	if (e && parent != CORK_NIL && !isAbsoluteIdentifier(token))
+		e->extensionFields.scopeIndex = parent;
+
+	tokenRead (token);
+	if (token->type != '{')
+	{
+		skipToEndOfCmdline(token);
+		return;
+	}
+
+	do {
+		tokenRead (token);
+		if (tokenIsKeyword (token, NAMESPACE))
+			parseNamespace (token, index);
+		else if (tokenIsKeyword (token, PROC))
+			parseProc (token, index);
+		else if (tokenIsType (token, TCL_IDENTIFIER))
+		{
+			notifyCommand (token, index);
+			skipToEndOfCmdline(token); /* ??? */
+		}
+		else if (token->type == '}')
+		{
+			if (e)
+				e->extensionFields.endLine = token->lineNumber;
+			break;
+		}
+		else
+			skipToEndOfCmdline(token);
+	} while (!tokenIsEOF(token));
+}
+
+static void parsePackage (tokenInfo *const token)
+{
+	tokenRead (token);
+	if (tokenIsType (token, TCL_IDENTIFIER)
+		&& (strcmp (tokenString (token), "require") == 0))
+	{
+	next:
+		tokenRead (token);
+		if (tokenIsType (token, TCL_IDENTIFIER)
+			&& (vStringLength (token->string) > 0))
+		{
+			if (tokenString(token)[0] == '-')
+				goto next;
+		}
+	}
+	skipToEndOfCmdline(token);
+}
+
+
+static void findTclTags (void)
+{
+	struct sTclParserState pstate = {
+		.lastTokenType = TOKEN_TCL_UNDEFINED,
+	};
+	tokenInfo *const token = newTclToken (&pstate);
+
+	do {
+		tokenRead (token);
+		if (tokenIsKeyword (token, NAMESPACE))
+			parseNamespace (token, CORK_NIL);
+		else if (tokenIsKeyword (token, PROC))
+			parseProc (token, CORK_NIL);
+		else if (tokenIsKeyword (token, PACKAGE))
+			parsePackage (token);
+		else if (tokenIsType (token, TCL_IDENTIFIER))
+		{
+			notifyCommand (token, CORK_NIL);
+			skipToEndOfCmdline(token); /* ??? */
+		}
+		else
+			skipToEndOfCmdline(token);
+	} while (!tokenIsEOF(token));
+
+	tokenDelete (token);
+	flashTokenBacklog (&tclTokenInfoClass);
+}
+
+extern parserDefinition* TclParser (void)
+{
+	static const char *const extensions [] = { "tcl", "tk", "wish", "exp", NULL };
+	static const char *const aliases [] = {"expect", "tclsh", NULL };
+
+	parserDefinition* def = parserNew ("Tcl");
+	def->kindTable      = TclKinds;
+	def->kindCount  = ARRAY_SIZE (TclKinds);
+	def->extensions = extensions;
+	def->aliases = aliases;
+	def->keywordTable = TclKeywordTable;
+	def->keywordCount = ARRAY_SIZE (TclKeywordTable);
+
+	def->parser     = findTclTags;
+	def->useCork    = CORK_QUEUE;
+	def->requestAutomaticFQTag = true;
+	def->defaultScopeSeparator = "::";
+	def->defaultRootScopeSeparator = "::";
+
+	return def;
+}


Modified: ctags/parsers/tcl.h
51 lines changed, 51 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,51 @@
+/*
+*   Copyright (c) 2017, Masatake YAMATO
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License version 2 or (at your option) any later version.
+*/
+
+#ifndef CTAGS_PARSER_TCL_H
+#define CTAGS_PARSER_TCL_H
+
+/*
+*   INCLUDE FILES
+*/
+#include "general.h"  /* must always come first */
+
+#include "subparser.h"
+#include "tokeninfo.h"
+
+typedef struct sTclSubparser tclSubparser;
+
+enum TclTokenType {
+	/* 0..255 are the byte's value */
+	TOKEN_TCL_EOF = 256,
+	TOKEN_TCL_UNDEFINED,
+	TOKEN_TCL_KEYWORD,
+	TOKEN_TCL_IDENTIFIER,
+	TOKEN_TCL_VARIABLE,
+	TOKEN_TCL_EOL,
+	TOKEN_TCL_STRING,
+};
+
+struct sTclSubparser {
+	subparser subparser;
+
+	/* `pstate' is needed to call newTclToken(). */
+	void (* namespaceImportNotify) (tclSubparser *s, char *namespace,
+									void *pstate);
+	/* Return CORK_NIL if the command line is NOT consumed.
+	   If a positive integer is returned, end: field may
+	   be attached by tcl base parser.
+	   Return CORK_NIL - 1 if the command line is consumed
+	   but not tag is made. */
+	int (* commandNotify) (tclSubparser *s, char *command,
+						   int parentIndex,
+						   void *pstate);
+};
+
+extern tokenInfo *newTclToken (void *pstate);
+extern void skipToEndOfTclCmdline (tokenInfo *const token);
+
+#endif	/* CTAGS_PARSER_TCL_H */


Modified: ctags/parsers/tcloo.c
201 lines changed, 201 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,201 @@
+/*
+*   Copyright (c) 2017, Masatake YAMATO
+*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License version 2 or (at your option) any later version.
+*
+*/
+
+#include "general.h"  /* must always come first */
+#include "tcl.h"
+#include "param.h"
+#include "parse.h"
+#include "entry.h"
+#include "tokeninfo.h"
+
+#include <string.h>
+
+
+struct tclooSubparser {
+	tclSubparser tcl;
+	bool foundTclOONamespaceImported;
+};
+
+static scopeSeparator TclOOGenericSeparators [] = {
+	{ KIND_WILDCARD_INDEX, "::" },
+};
+
+enum TclOOKind {
+	K_CLASS,
+	K_METHOD,
+};
+
+static kindDefinition TclOOKinds[] = {
+	{ true, 'c', "class", "classes" },
+	{ true, 'm', "method", "methods",
+	  ATTACH_SEPARATORS(TclOOGenericSeparators) },
+};
+
+static bool tclooForceUse;
+
+static void parseMethod (tokenInfo *token, int owner)
+{
+	tokenRead (token);
+	if (tokenIsType (token, TCL_IDENTIFIER))
+	{
+		tagEntryInfo e;
+
+		initTagEntry(&e, tokenString (token), K_METHOD);
+		e.extensionFields.scopeIndex = owner;
+		makeTagEntry (&e);
+	}
+	skipToEndOfTclCmdline (token);
+}
+
+static void parseSuperclass (tokenInfo *token, int this_class)
+{
+	tokenRead (token);
+	if (tokenIsType (token, TCL_IDENTIFIER))
+	{
+		tagEntryInfo *e = getEntryInCorkQueue(this_class);
+
+		if (e)
+		{
+			if (e->extensionFields.inheritance)
+			{   /* superclass is used twice in a class. */
+				eFree ((void *)e->extensionFields.inheritance);
+			}
+			e->extensionFields.inheritance = eStrdup(tokenString(token));
+		}
+	}
+	skipToEndOfTclCmdline (token);
+}
+
+static int parseClass (tclSubparser *s CTAGS_ATTR_UNUSED, int parentIndex,
+					   void *pstate)
+{
+	tokenInfo *token = newTclToken (pstate);
+	int r = CORK_NIL;
+
+	tokenRead (token);
+	if (tokenIsType (token, TCL_IDENTIFIER)
+		&& (strcmp(tokenString(token), "create") == 0))
+	{
+		tokenRead (token);
+		if (tokenIsType (token, TCL_IDENTIFIER))
+		{
+			tagEntryInfo e;
+
+			initTagEntry(&e, tokenString (token), K_CLASS);
+			e.extensionFields.scopeIndex = parentIndex;
+			r = makeTagEntry (&e);
+		}
+
+		if (tokenSkipToType (token, '{'))
+		{
+			do {
+				tokenRead (token);
+				if (tokenIsType (token, TCL_IDENTIFIER)
+					|| tokenIsType (token, TCL_KEYWORD))
+				{
+					if (strcmp(tokenString(token), "method") == 0)
+						parseMethod(token, r);
+					else if (strcmp(tokenString(token), "superclass") == 0)
+						parseSuperclass(token, r);
+					else
+						skipToEndOfTclCmdline (token);
+				}
+				else if (token->type == '}')
+					break;
+			} while (!tokenIsEOF(token));
+		}
+	}
+
+	skipToEndOfTclCmdline (token);
+	tokenDelete(token);
+	return r;
+}
+
+static int commandNotify (tclSubparser *s, char *command,
+						  int parentIndex, void *pstate)
+{
+	struct tclooSubparser *tcloo = (struct tclooSubparser *)s;
+	int r = CORK_NIL;
+
+	if ((tcloo->foundTclOONamespaceImported
+		 && (strcmp (command, "class") == 0))
+		|| (strcmp (command, "oo::class") == 0))
+		r = parseClass (s, parentIndex, pstate);
+
+	return r;
+}
+
+static void namespaceImportNotify (tclSubparser *s, char *namespace,
+								   void *pstate CTAGS_ATTR_UNUSED)
+{
+	struct tclooSubparser *tcloo = (struct tclooSubparser *)s;
+
+	if (strcmp(namespace, "oo::*") == 0
+		|| strcmp(namespace, "oo::class") == 0)
+		tcloo->foundTclOONamespaceImported = true;
+}
+
+static void inputStart (subparser *s)
+{
+	struct tclooSubparser *tcloo = (struct tclooSubparser *)s;
+
+	tcloo->foundTclOONamespaceImported = tclooForceUse;
+}
+
+static struct tclooSubparser tclooSubparser = {
+	.tcl = {
+		.subparser = {
+			.direction = SUBPARSER_BI_DIRECTION,
+			.inputStart = inputStart,
+		},
+		.commandNotify = commandNotify,
+		.namespaceImportNotify = namespaceImportNotify,
+	},
+};
+
+static void findTclOOTags(void)
+{
+	scheduleRunningBaseparser (RUN_DEFAULT_SUBPARSERS);
+}
+
+static void tclooForceUseParamHandler (const langType language CTAGS_ATTR_UNUSED,
+									  const char *name, const char *arg)
+{
+	tclooForceUse = paramParserBool (arg, tclooForceUse, name, "parameter");
+}
+
+static parameterHandlerTable TclOOParameterHandlerTable [] = {
+	{ .name = "forceUse",
+	  .desc = "enable the parser even when `oo' namespace is not specified in the input (true or [false])" ,
+	  .handleParameter = tclooForceUseParamHandler,
+	},
+};
+
+extern parserDefinition* TclOOParser (void)
+{
+	parserDefinition* const def = parserNew("TclOO");
+
+	static parserDependency dependencies [] = {
+		[0] = { DEPTYPE_SUBPARSER, "Tcl", &tclooSubparser },
+	};
+
+	def->dependencies = dependencies;
+	def->dependencyCount = ARRAY_SIZE (dependencies);
+
+	def->kindTable = TclOOKinds;
+	def->kindCount = ARRAY_SIZE(TclOOKinds);
+
+	def->parser = findTclOOTags;
+	def->useCork = CORK_QUEUE;
+	def->requestAutomaticFQTag = true;
+
+	def->parameterHandlerTable = TclOOParameterHandlerTable;
+	def->parameterHandlerCount = ARRAY_SIZE(TclOOParameterHandlerTable);
+
+	return def;
+}


Modified: meson.build
4 lines changed, 3 insertions(+), 1 deletions(-)
===================================================================
@@ -638,7 +638,6 @@ ctags = static_library('ctags',
 	'ctags/parsers/geany_lcpp.h',
 	'ctags/parsers/geany_markdown.c',
 	'ctags/parsers/geany_matlab.c',
-	'ctags/parsers/geany_tcl.c',
 	'ctags/parsers/geany_tex.c',
 	'ctags/parsers/geany_vhdl.c',
 	'ctags/parsers/go.c',
@@ -668,6 +667,9 @@ ctags = static_library('ctags',
 	'ctags/parsers/rust.c',
 	'ctags/parsers/sh.c',
 	'ctags/parsers/sql.c',
+	'ctags/parsers/tcl.c',
+	'ctags/parsers/tcl.h',
+	'ctags/parsers/tcloo.c',
 	'ctags/parsers/txt2tags.c',
 	'ctags/parsers/verilog.c',
 	c_args: geany_cflags + [ '-DG_LOG_DOMAIN="CTags"',


Modified: src/filetypes.c
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -144,7 +144,7 @@ static void init_builtin_filetypes(void)
 	FT_INIT( JS,         JAVASCRIPT,   "Javascript",       NULL,                      SOURCE_FILE, SCRIPT   );
 	FT_INIT( PYTHON,     PYTHON,       "Python",           NULL,                      SOURCE_FILE, SCRIPT   );
 	FT_INIT( RUBY,       RUBY,         "Ruby",             NULL,                      SOURCE_FILE, SCRIPT   );
-	FT_INIT( TCL,        TCL,          "Tcl",              NULL,                      SOURCE_FILE, SCRIPT   );
+	FT_INIT( TCL,        TCLOO,        "Tcl",              NULL,                      SOURCE_FILE, SCRIPT   );
 	FT_INIT( LUA,        LUA,          "Lua",              NULL,                      SOURCE_FILE, SCRIPT   );
 	FT_INIT( GDSCRIPT,   GDSCRIPT,     "GDScript",         NULL,                      SOURCE_FILE, SCRIPT   );
 	FT_INIT( HASKELL,    HASKELL,      "Haskell",          NULL,                      SOURCE_FILE, COMPILED );


Modified: src/tagmanager/tm_parser.c
31 lines changed, 28 insertions(+), 3 deletions(-)
===================================================================
@@ -372,10 +372,9 @@ static TMParserMapGroup group_RUBY[] = {
 };
 
 static TMParserMapEntry map_TCL[] = {
-	{'c', tm_tag_class_t},      // class
-	{'m', tm_tag_member_t},     // method
 	{'p', tm_tag_function_t},   // procedure
-	{'n', tm_tag_namespace_t},  // module
+	{'n', tm_tag_namespace_t},  // namespace
+	{'z', tm_tag_undef_t},      // parameter
 };
 static TMParserMapGroup group_TCL[] = {
 	{_("Namespaces"), TM_ICON_NAMESPACE, tm_tag_namespace_t},
@@ -384,6 +383,19 @@ static TMParserMapGroup group_TCL[] = {
 	{_("Procedures"), TM_ICON_OTHER, tm_tag_function_t},
 };
 
+static TMParserMapEntry map_TCLOO[] = {
+	{'c', tm_tag_class_t},   // class
+	{'m', tm_tag_member_t},  // method
+};
+#define group_TCLOO group_TCL
+
+static TMSubparserMapEntry subparser_TCLOO_TCL_map[] = {
+	{tm_tag_namespace_t, tm_tag_namespace_t},
+	{tm_tag_class_t, tm_tag_class_t},
+	{tm_tag_member_t, tm_tag_member_t},
+	{tm_tag_function_t, tm_tag_function_t},
+};
+
 static TMParserMapEntry map_SH[] = {
 	{'a', tm_tag_undef_t},     // alias
 	{'f', tm_tag_function_t},  // function
@@ -1008,6 +1020,7 @@ static TMParserMap parser_map[] = {
 	MAP_ENTRY(POWERSHELL),
 	MAP_ENTRY(JULIA),
 	MAP_ENTRY(CPREPROCESSOR),
+	MAP_ENTRY(TCLOO),
 };
 /* make sure the parser map is consistent and complete */
 G_STATIC_ASSERT(G_N_ELEMENTS(parser_map) == TM_PARSER_COUNT);
@@ -1110,6 +1123,7 @@ static void add_subparser(TMParserType lang, TMParserType sublang, TMSubparserMa
 static void init_subparser_map(void)
 {
 	SUBPARSER_MAP_ENTRY(HTML, JAVASCRIPT, subparser_HTML_javascript_map);
+	SUBPARSER_MAP_ENTRY(TCLOO, TCL, subparser_TCLOO_TCL_map);
 }
 
 
@@ -1302,6 +1316,13 @@ gchar *tm_parser_update_scope(TMParserType lang, gchar *scope)
 			/* PHP parser uses two different scope separators but this would
 			 * complicate things in Geany so make sure there's just one type */
 			return replace_string_if_present(scope, "\\", "::");
+		case TM_PARSER_TCL:
+		case TM_PARSER_TCLOO:
+			/* The TCL(OO) parser returns scope prefixed with :: which we don't
+			 * want. */
+			if (g_str_has_prefix(scope, "::"))
+				return g_strdup(scope + 2);
+			break;
 	}
 	return scope;
 }
@@ -1432,6 +1453,8 @@ const gchar *tm_parser_scope_separator(TMParserType lang)
 		case TM_PARSER_PHP:
 		case TM_PARSER_POWERSHELL:
 		case TM_PARSER_RUST:
+		case TM_PARSER_TCL:
+		case TM_PARSER_TCLOO:
 		case TM_PARSER_ZEPHIR:
 			return "::";
 
@@ -1492,6 +1515,8 @@ gboolean tm_parser_has_full_scope(TMParserType lang)
 		case TM_PARSER_RUBY:
 		case TM_PARSER_RUST:
 		case TM_PARSER_SQL:
+		case TM_PARSER_TCL:
+		case TM_PARSER_TCLOO:
 		case TM_PARSER_TXT2TAGS:
 		case TM_PARSER_VALA:
 		case TM_PARSER_VERILOG:


Modified: src/tagmanager/tm_parser.h
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -112,6 +112,7 @@ enum
 	TM_PARSER_JULIA,
 	TM_PARSER_BIBTEX,
 	TM_PARSER_CPREPROCESSOR,
+	TM_PARSER_TCLOO,
 	TM_PARSER_COUNT
 };
 


Modified: src/tagmanager/tm_parsers.h
3 lines changed, 2 insertions(+), 1 deletions(-)
===================================================================
@@ -67,6 +67,7 @@
 	PowerShellParser, \
 	JuliaParser, \
 	BibtexParser, \
-	CPreProParser
+	CPreProParser, \
+	TclOOParser
 
 #endif


Modified: tests/ctags/simple.tcl
59 lines changed, 52 insertions(+), 7 deletions(-)
===================================================================
@@ -1,10 +1,55 @@
-# proc comment
-proc simple1
+oo::class create X {
+    superclass Y;
 
-class tcl_class
+    method add v {
+    }; method doSomething {v} {
+    }
+}
 
-itcl::class itcl_class
+class create ShouldNotBeCaptured {
+}
 
-public method method1
-protected method method2
-private method method3
+proc simple1 {} {
+}
+
+namespace eval A::B {
+}
+
+namespace eval A {
+    proc pr1 {s} {
+	puts $s
+    }
+    pr1 "a"
+    proc B::pr2 {s} {
+	puts "$s"
+    }
+    namespace eval C {
+	proc pr3 {s} {
+	    puts $s
+	}
+    }
+    proc ::pr4 {s} {
+	puts "$s"
+    }
+
+}
+
+proc ::pr5 {s} {
+    puts "$s"
+}
+
+proc pr6 {s} {
+    puts "$s"
+}
+
+A::pr1 "b"
+
+A::B::pr2 "c"
+
+A::C::pr3 "d"
+
+pr4 "e"
+
+pr5 "f"
+
+pr6 "g"


Modified: tests/ctags/simple.tcl.tags
19 lines changed, 13 insertions(+), 6 deletions(-)
===================================================================
@@ -1,7 +1,14 @@
 # format=tagmanager
-itcl_class�1�0
-method1�64�0
-method2�64�0
-method3�64�0
-simple1�16�0
-tcl_class�1�0
+A�256�0
+A::B�256�0
+C�256�A�0
+X�1�0
+add�64�X�0
+doSomething�64�X�0
+pr1�16�{s}�A�0
+pr2�16�{s}�B�0
+pr3�16�{s}�A::C�0
+pr4�16�{s}�0
+pr5�16�{s}�0
+pr6�16�{s}�0
+simple1�16�{}�0



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list