Hi guys,

I saw that the ruby parser don't properly generate tags declarations like:

        class Foo::Bar
        end

which should generate a tag "Bar" with the scope "Foo";  but it
generates a tag "Foo" and simply ignores "Bar".  This seems to apply to
modules, classes and methods at least -- almost everything.

So I wanted to fix that.  Unfortunately the scoping code in CTags don't
really support to easily put several scopes at the same "level", e.g. if
you want to push several scope you gotta handle the popping yourself.
And since there is one single block end, it's tricky to do.

Since I was way too lazy (and didn't even found a good implementation)
to fix that, I just did it the dirty way: reading the whole "Foo::Bar"
as a single tag name ("Foo.Bar") and tuning the code registering the tag
to split this on the last ".", putting the left part (if any) in the
scope.  Patch attached.  This is quite dirty, but works fine unless a
legitimate tag may include a "." in its name, which doesn't seem the
case currently looking at the parser.

Note that Ruby isn't the only language that allows such kind of scoping.
 For example, Vala allows to prefix stuff with a namespace -- and there
is the same problem here.

So, especially Nick, what do you guys think of this?  Is this patch too
dirty?  Do somebody have a better idea?  Or is this too dirty and "we
don't care because nobody writes ruby anyway"?  In one word: opinions?

Thanks,
Colomban
>From f0da754670cca4d4c3ddd0c8d7295881372ba3b6 Mon Sep 17 00:00:00 2001
From: Colomban Wendling <b...@herbesfolles.org>
Date: Thu, 6 Sep 2012 20:45:57 +0200
Subject: [PATCH] Correctly parse Ruby declarations with inline scoping

Generate the appropriate tags for declarations like:

	class Foo::Bar::Baz
	end

The implementation here is quite hackish but works fairly reliably.
---
 tagmanager/ctags/ruby.c |   38 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 36 insertions(+), 2 deletions(-)

diff --git a/tagmanager/ctags/ruby.c b/tagmanager/ctags/ruby.c
index a614eb1..c7695ef 100644
--- a/tagmanager/ctags/ruby.c
+++ b/tagmanager/ctags/ruby.c
@@ -132,11 +132,26 @@ static void emitRubyTag (vString* name, rubyKind kind)
 {
 	tagEntryInfo tag;
 	vString* scope;
+	const char *this_name;
 
 	vStringTerminate (name);
 	scope = stringListToScope (nesting);
 
-	initTagEntry (&tag, vStringValue (name));
+	/* extract scope and actual name from tag name in case of tags like
+	 * "class Foo::Bar::Baz" which are parsed as a single name, "Foo.Bar.Baz" */
+	this_name = strrchr (vStringValue (name), '.');
+	if (this_name)
+	{
+		if (vStringLength (scope) > 0)
+			vStringPut (scope, '.');
+		vStringNCat (scope, name, this_name - vStringValue (name));
+		vStringTerminate (scope);
+		this_name ++;
+	}
+	else
+		this_name = vStringValue (name);
+
+	initTagEntry (&tag, this_name);
 	if (vStringLength (scope) > 0) {
 	    tag.extensionFields.scope [0] = "class";
 	    tag.extensionFields.scope [1] = vStringValue (scope);
@@ -230,7 +245,26 @@ static void readAndEmitTag (const unsigned char** cp, rubyKind expected_kind)
 	if (isspace (**cp))
 	{
 		vString *name = vStringNew ();
-		rubyKind actual_kind = parseIdentifier (cp, name, expected_kind);
+		vString *chunk = vStringNew ();
+		rubyKind actual_kind;
+		unsigned int i = 0;
+
+		/* parse the identifier, allowing scoping like "class Foo::Bar::Baz" */
+		while (1)
+		{
+			actual_kind = parseIdentifier (cp, chunk, expected_kind);
+			if (i++ > 0)
+				vStringPut (name, '.');
+			vStringCat (name, chunk);
+			vStringClear (chunk);
+
+			if (actual_kind != K_UNDEFINED && (*cp)[0] == ':' && (*cp)[1] == ':')
+				*cp += 2;
+			else
+				break;
+		}
+		vStringDelete (chunk);
+		vStringTerminate (name);
 
 		if (actual_kind == K_UNDEFINED || vStringLength (name) == 0)
 		{
-- 
1.7.10.4

_______________________________________________
Geany mailing list
Geany@uvena.de
https://lists.uvena.de/cgi-bin/mailman/listinfo/geany

Reply via email to