[geany/geany] a5d5ae: Add Clojure ctags parser

Jiří Techet git-noreply at geany.org
Thu May 12 23:28:16 UTC 2022


Branch:      refs/heads/master
Author:      Jiří Techet <techet at gmail.com>
Committer:   Jiří Techet <techet at gmail.com>
Date:        Thu, 12 May 2022 23:28:16 UTC
Commit:      a5d5ae3d87f7760ac83d17991faad63644a5aa9d
             https://github.com/geany/geany/commit/a5d5ae3d87f7760ac83d17991faad63644a5aa9d

Log Message:
-----------
Add Clojure ctags parser


Modified Paths:
--------------
    ctags/Makefile.am
    ctags/parsers/clojure.c
    data/filedefs/filetypes.Clojure.conf
    meson.build
    src/tagmanager/tm_parser.c
    src/tagmanager/tm_parser.h
    src/tagmanager/tm_parsers.h
    tests/ctags/Makefile.am
    tests/ctags/simple.clj
    tests/ctags/simple.clj.tags
    tests/meson.build

Modified: ctags/Makefile.am
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -50,6 +50,7 @@ parsers = \
 	parsers/basic.c \
 	parsers/bibtex.c \
 	parsers/geany_c.c \
+	parsers/clojure.c \
 	parsers/cobol.c \
 	parsers/iniconf.c \
 	parsers/iniconf.h \


Modified: ctags/parsers/clojure.c
195 lines changed, 195 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,195 @@
+/**
+ *   Copyright (c) 2015, Miloslav Nenadál <nenadalm at gmail.com>
+ *
+ *   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 code for generating tags for the Clojure language.
+ */
+
+#include "general.h"
+
+#include <string.h>
+
+#include "parse.h"
+#include "read.h"
+#include "routines.h"
+#include "vstring.h"
+#include "entry.h"
+
+typedef enum {
+	K_FUNCTION,
+	K_NAMESPACE
+} clojureKind;
+
+static kindDefinition ClojureKinds[] = {
+	{true, 'f', "function", "functions"},
+	{true, 'n', "namespace", "namespaces"}
+};
+
+static int isNamespace (const char *strp)
+{
+	return strncmp (++strp, "ns", 2) == 0 && isspace (strp[2]);
+}
+
+static int isCoreNamespace (const char *strp)
+{
+	return strncmp (++strp, "clojure.core/ns", 15) == 0 && isspace (strp[15]);
+}
+
+static int isFunction (const char *strp)
+{
+	return (strncmp (++strp, "defn", 4) == 0 && isspace (strp[4]));
+}
+
+static int isCoreFunction (const char *strp)
+{
+	return (strncmp (++strp, "clojure.core/defn", 17) == 0 && isspace (strp[17]));
+}
+
+static int isQuote (const char *strp)
+{
+	return strncmp (++strp, "quote", 5) == 0 && isspace (strp[5]);
+}
+
+static void functionName (vString * const name, const char *dbp)
+{
+	const char *p;
+
+	if (*dbp == '\'')
+		dbp++;
+	else if (*dbp == '(' && isQuote (dbp))
+	{
+		dbp += 7;
+		while (isspace (*dbp))
+			dbp++;
+	}
+
+	for (p = dbp; *p != '\0' && *p != '(' && !isspace ((int) *p) && *p != ')';
+		p++)
+		vStringPut (name, *p);
+}
+
+const char* skipMetadata (const char *dbp)
+{
+	while (1)
+	{
+		if (*dbp == '^')
+		{
+			dbp++;
+			if (*dbp == '{')
+			{
+				/* skipping an arraymap */
+				for (; *dbp != '\0' && *dbp != '}'; dbp++)
+					;
+			}
+			else
+			{
+				/* skip a keyword or a symbol */
+				for (; *dbp != '\0' && !isspace((unsigned char)*dbp); dbp++)
+					;
+			}
+
+			if (*dbp == '\0')
+				break;
+
+			dbp++;
+			while (isspace ((unsigned char)*dbp))
+				dbp++;
+		}
+		else
+			break;
+	}
+
+	return dbp;
+}
+
+static int makeNamespaceTag (vString * const name, const char *dbp)
+{
+	dbp = skipMetadata (dbp);
+	functionName (name, dbp);
+	if (vStringLength (name) > 0 && ClojureKinds[K_NAMESPACE].enabled)
+	{
+		tagEntryInfo e;
+		initTagEntry (&e, vStringValue (name), K_NAMESPACE);
+		e.lineNumber = getInputLineNumber ();
+		e.filePosition = getInputFilePosition ();
+
+		return makeTagEntry (&e);
+	}
+	else
+		return CORK_NIL;
+}
+
+static void makeFunctionTag (vString * const name, const char *dbp, int scope_index)
+{
+	dbp = skipMetadata (dbp);
+	functionName (name, dbp);
+	if (vStringLength (name) > 0 && ClojureKinds[K_FUNCTION].enabled)
+	{
+		tagEntryInfo e;
+		initTagEntry (&e, vStringValue (name), K_FUNCTION);
+		e.lineNumber = getInputLineNumber ();
+		e.filePosition = getInputFilePosition ();
+
+		e.extensionFields.scopeIndex =  scope_index;
+		makeTagEntry (&e);
+	}
+}
+
+static void skipToSymbol (const char **p)
+{
+	while (**p != '\0' && !isspace ((int) **p))
+		*p = *p + 1;
+	while (isspace ((int) **p))
+		*p = *p + 1;
+}
+
+static void findClojureTags (void)
+{
+	vString *name = vStringNew ();
+	const char *p;
+	int scope_index = CORK_NIL;
+
+	while ((p = (char *)readLineFromInputFile ()) != NULL)
+	{
+		vStringClear (name);
+
+		while (isspace (*p))
+			p++;
+
+		if (*p == '(')
+		{
+			if (isNamespace (p) || isCoreNamespace (p))
+			{
+				skipToSymbol (&p);
+				scope_index = makeNamespaceTag (name, p);
+			}
+			else if (isFunction (p) || isCoreFunction (p))
+			{
+				skipToSymbol (&p);
+				makeFunctionTag (name, p, scope_index);
+			}
+		}
+	}
+	vStringDelete (name);
+}
+
+extern parserDefinition *ClojureParser (void)
+{
+	static const char *const extensions[] = {
+		"clj", "cljs", "cljc", NULL
+	};
+	static const char *const aliases[] = {
+		NULL
+	};
+
+	parserDefinition *def = parserNew ("Clojure");
+	def->kindTable = ClojureKinds;
+	def->kindCount = ARRAY_SIZE (ClojureKinds);
+	def->extensions = extensions;
+	def->aliases = aliases;
+	def->parser = findClojureTags;
+	def->useCork = CORK_QUEUE;
+	return def;
+}


Modified: data/filedefs/filetypes.Clojure.conf
2 lines changed, 2 insertions(+), 0 deletions(-)
===================================================================
@@ -9,6 +9,8 @@ keywords=* *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *
 # default extension used when saving files
 extension=clj
 lexer_filetype=Lisp
+tag_parser=Clojure
+
 # the following characters are these which a "word" can contains, see documentation
 #wordchars=_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
 


Modified: meson.build
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -593,6 +593,7 @@ ctags = static_library('ctags',
 	'ctags/parsers/asm.c',
 	'ctags/parsers/basic.c',
 	'ctags/parsers/bibtex.c',
+	'ctags/parsers/clojure.c',
 	'ctags/parsers/cobol.c',
 	'ctags/parsers/cpreprocessor.c',
 	'ctags/parsers/cpreprocessor.h',


Modified: src/tagmanager/tm_parser.c
11 lines changed, 11 insertions(+), 0 deletions(-)
===================================================================
@@ -978,6 +978,15 @@ static TMParserMapGroup group_GDSCRIPT[] = {
 	{_("Other"), TM_ICON_OTHER, tm_tag_other_t},
 };
 
+static TMParserMapEntry map_CLOJURE[] = {
+	{'f', tm_tag_function_t},   // function
+	{'n', tm_tag_namespace_t},  // namespace
+};
+static TMParserMapGroup group_CLOJURE[] = {
+	{_("Namespaces"), TM_ICON_NAMESPACE, tm_tag_namespace_t},
+	{_("Functions"), TM_ICON_METHOD, tm_tag_function_t},
+};
+
 typedef struct
 {
     TMParserMapEntry *entries;
@@ -1045,6 +1054,7 @@ static TMParserMap parser_map[] = {
 	MAP_ENTRY(JULIA),
 	MAP_ENTRY(CPREPROCESSOR),
 	MAP_ENTRY(TCLOO),
+	MAP_ENTRY(CLOJURE),
 };
 /* make sure the parser map is consistent and complete */
 G_STATIC_ASSERT(G_N_ELEMENTS(parser_map) == TM_PARSER_COUNT);
@@ -1557,6 +1567,7 @@ gboolean tm_parser_has_full_scope(TMParserType lang)
 		/* These make use of the scope, but don't include nested hierarchy
 		 * (either as a parser limitation or a language semantic) */
 		case TM_PARSER_ASCIIDOC:
+		case TM_PARSER_CLOJURE:
 		case TM_PARSER_CONF:
 		case TM_PARSER_ERLANG:
 		case TM_PARSER_F77:


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


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


Modified: tests/ctags/Makefile.am
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -288,6 +288,7 @@ test_sources = \
 	simple.abc						\
 	simple.asciidoc					\
 	simple.bas						\
+	simple.clj						\
 	simple.conf						\
 	simple.d						\
 	simple.diff						\


Modified: tests/ctags/simple.clj
15 lines changed, 15 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,15 @@
+(ns app.controller)
+
+ (defn empty-fn [])
+
+(defn function-with-body []
+    (println "body"))
+
+(clojure.core/defn core-function-with-body []
+    (println "core"))
+
+'(defn quoted-function [])
+(quote quoted-function2 [])
+
+(clojure.core/ns another.name)
+(defn x [])


Modified: tests/ctags/simple.clj.tags
7 lines changed, 7 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,7 @@
+# format=tagmanager
+another.name�256�0
+app.controller�256�0
+core-function-with-body�16�app.controller�0
+empty-fn�16�app.controller�0
+function-with-body�16�app.controller�0
+x�16�another.name�0


Modified: tests/meson.build
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -284,6 +284,7 @@ ctags_tests = files([
 	'ctags/simple.abc.tags',
 	'ctags/simple.asciidoc.tags',
 	'ctags/simple.bas.tags',
+	'ctags/simple.clj.tags',
 	'ctags/simple.conf.tags',
 	'ctags/simple.d.tags',
 	'ctags/simple.diff.tags',



--------------
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