Subject: bug#5624: 23.1; etags elisp and scheme "=" in names

From: Alex <[email-masked]>
Date: Sun, 11 Jun 2017 20:44:29 -0600

Eli Zaretskii <[email-masked]> writes:

>> From: Alex <[email-masked]>
>> Date: Sat, 10 Jun 2017 19:37:49 -0600
>> Cc: [email-masked]

>> This appears to be because in etags.c, all languages use the same
>> `notinname' procedure to determine a valid identifier.
>
> Yes.  But there are exceptions from this rule, where that is deemed
> necessary.  For example, Ruby_functions has this:
>
> 	 /* Ruby method names can end in a '='.  Also, operator overloading can
> 	    define operators whose names include '='.  */
> 	  while (!notinname (*cp) || *cp == '=')
> 	    cp++;
>
>> Shouldn't each language (optionally) use a different procedure to
>> determine the bounds of an identifier? Lisp and Scheme, for instance,
>> would not have '=' in their respective `notinname' procedures.
>
> Feel free to work on this, if you want to scratch that particular
> itch.  Alternatively, just augment notinname in language-specific
> support code, like some of the supported languages already do.
>
> TIA.

One issue is that the Lisp and Scheme functions use get_tag, which uses
notinname. What do you think about the following diff? It adds a wrapper
to notinname which get_tag uses. I had to add an additional parameter to
get_tag.

diff --git a/lib-src/etags.c b/lib-src/etags.c
index 6f280d8ab4..e354325ad5 100644
--- a/lib-src/etags.c
+++ b/lib-src/etags.c
@@ -181,6 +181,20 @@ notinname (unsigned char c)
   return table[c];
 }
 
+/* C is not in a name in language LANG. */
+static bool
+notinname_lang (unsigned char c, char const *lang)
+{
+  if (lang == NULL)
+    return notinname (c);
+
+  else if (streq ("lisp", lang) || streq ("scheme", lang))
+    if (c == '=')
+      return false;
+
+  return notinname (c);
+}
+
 /* C can start a token.  */
 static bool
 begtoken (unsigned char c)
@@ -371,7 +385,7 @@ static language *get_language_from_langname (const char *);
 static void readline (linebuffer *, FILE *);
 static long readline_internal (linebuffer *, FILE *, char const *);
 static bool nocase_tail (const char *);
-static void get_tag (char *, char **);
+static void get_tag (char *, char **, char const *);
 
 static void analyze_regex (char *);
 static void free_regexps (void);
@@ -4715,7 +4729,7 @@ Perl_functions (FILE *inf)
       if (LOOKING_AT (cp, "package"))
 	{
 	  free (package);
-	  get_tag (cp, &package);
+	  get_tag (cp, &package, NULL);
 	}
       else if (LOOKING_AT (cp, "sub"))
 	{
@@ -5346,7 +5360,7 @@ L_getit (void)
       /* Ok, then skip "(" before name in (defstruct (foo)) */
       dbp = skip_spaces (dbp);
   }
-  get_tag (dbp, NULL);
+  get_tag (dbp, NULL, "lisp");
 }
 
 static void
@@ -5426,7 +5440,7 @@ Lua_functions (FILE *inf)
 	{
 	  char *tag_name, *tp_dot, *tp_colon;
 
-	  get_tag (bp, &tag_name);
+	  get_tag (bp, &tag_name, NULL);
 	  /* If the tag ends with ".foo" or ":foo", make an additional tag for
 	     "foo".  */
 	  tp_dot = strrchr (tag_name, '.');
@@ -5436,7 +5450,7 @@ Lua_functions (FILE *inf)
 	      char *p = tp_dot > tp_colon ? tp_dot : tp_colon;
 	      int len_add = p - tag_name + 1;
 
-	      get_tag (bp + len_add, NULL);
+	      get_tag (bp + len_add, NULL, NULL);
 	    }
 	}
     }
@@ -5468,7 +5482,7 @@ PS_functions (FILE *inf)
 		    lb.buffer, ep - lb.buffer + 1, lineno, linecharno);
 	}
       else if (LOOKING_AT (bp, "defineps"))
-	get_tag (bp, NULL);
+	get_tag (bp, NULL, NULL);
     }
 }
 
@@ -5550,12 +5564,12 @@ Scheme_functions (FILE *inf)
 	  bp = skip_non_spaces (bp+4);
 	  /* Skip over open parens and white space.  Don't continue past
 	     '\0'. */
-	  while (*bp && notinname (*bp))
+	  while (*bp && notinname_lang (*bp, "scheme"))
 	    bp++;
-	  get_tag (bp, NULL);
+	  get_tag (bp, NULL, "scheme");
 	}
       if (LOOKING_AT (bp, "(SET!") || LOOKING_AT (bp, "(set!"))
-	get_tag (bp, NULL);
+	get_tag (bp, NULL, "scheme");
     }
 }
 
@@ -6573,14 +6587,14 @@ nocase_tail (const char *cp)
 }
 
 static void
-get_tag (register char *bp, char **namepp)
+get_tag (register char *bp, char **namepp, char const *lang)
 {
   register char *cp = bp;
 
   if (*bp != '\0')
     {
       /* Go till you get to white space or a syntactic break */
-      for (cp = bp + 1; !notinname (*cp); cp++)
+      for (cp = bp + 1; !notinname_lang (*cp, lang); cp++)
 	continue;
       make_tag (bp, cp - bp, true,
 		lb.buffer, cp - lb.buffer + 1, lineno, linecharno);