]> code.delx.au - gnu-emacs/blobdiff - lib-src/etags.c
; Merge from origin/emacs-25
[gnu-emacs] / lib-src / etags.c
index 791722d4b661878975fed9e9888dacf5c9c2e888..796adaa5d057bab7086d5c0522e0ca7b62cc9a44 100644 (file)
@@ -28,7 +28,7 @@ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
-Copyright (C) 1984, 1987-1989, 1993-1995, 1998-2015 Free Software
+Copyright (C) 1984, 1987-1989, 1993-1995, 1998-2016 Free Software
 Foundation, Inc.
 
 This file is not considered part of GNU Emacs.
@@ -150,10 +150,29 @@ char pot_etags_version[] = "@(#) pot revision number is 17.38.1.4";
 # define CTAGS false
 #endif
 
-#define streq(s,t)     (assert ((s)!=NULL || (t)!=NULL), !strcmp (s, t))
-#define strcaseeq(s,t) (assert ((s)!=NULL && (t)!=NULL), !c_strcasecmp (s, t))
-#define strneq(s,t,n)  (assert ((s)!=NULL || (t)!=NULL), !strncmp (s, t, n))
-#define strncaseeq(s,t,n) (assert ((s)!=NULL && (t)!=NULL), !c_strncasecmp (s, t, n))
+static bool
+streq (char const *s, char const *t)
+{
+  return strcmp (s, t) == 0;
+}
+
+static bool
+strcaseeq (char const *s, char const *t)
+{
+  return c_strcasecmp (s, t) == 0;
+}
+
+static bool
+strneq (char const *s, char const *t, size_t n)
+{
+  return strncmp (s, t, n) == 0;
+}
+
+static bool
+strncaseeq (char const *s, char const *t, size_t n)
+{
+  return c_strncasecmp (s, t, n) == 0;
+}
 
 /* C is not in a name.  */
 static bool
@@ -335,6 +354,7 @@ static void Cstar_entries (FILE *);
 static void Erlang_functions (FILE *);
 static void Forth_words (FILE *);
 static void Fortran_functions (FILE *);
+static void Go_functions (FILE *);
 static void HTML_labels (FILE *);
 static void Lisp_functions (FILE *);
 static void Lua_functions (FILE *);
@@ -345,6 +365,7 @@ static void PHP_functions (FILE *);
 static void PS_functions (FILE *);
 static void Prolog_functions (FILE *);
 static void Python_functions (FILE *);
+static void Ruby_functions (FILE *);
 static void Scheme_functions (FILE *);
 static void TeX_commands (FILE *);
 static void Texinfo_nodes (FILE *);
@@ -621,6 +642,10 @@ static const char *Fortran_suffixes [] =
 static const char Fortran_help [] =
 "In Fortran code, functions, subroutines and block data are tags.";
 
+static const char *Go_suffixes [] = {"go", NULL};
+static const char Go_help [] =
+  "In Go code, functions, interfaces and packages are tags.";
+
 static const char *HTML_suffixes [] =
   { "htm", "html", "shtml", NULL };
 static const char HTML_help [] =
@@ -703,6 +728,14 @@ static const char Python_help [] =
 "In Python code, 'def' or 'class' at the beginning of a line\n\
 generate a tag.";
 
+static const char *Ruby_suffixes [] =
+  { "rb", "ru", "rbw", NULL };
+static const char *Ruby_filenames [] =
+  { "Rakefile", "Thorfile", NULL };
+static const char Ruby_help [] =
+  "In Ruby code, 'def' or 'class' or 'module' at the beginning of\n\
+a line generate a tag.  Constants also generate a tag.";
+
 /* Can't do the `SCM' or `scm' prefix with a version number. */
 static const char *Scheme_suffixes [] =
   { "oak", "sch", "scheme", "SCM", "scm", "SM", "sm", "ss", "t", NULL };
@@ -768,6 +801,7 @@ static language lang_names [] =
   { "erlang",    Erlang_help,    Erlang_functions,  Erlang_suffixes    },
   { "forth",     Forth_help,     Forth_words,       Forth_suffixes     },
   { "fortran",   Fortran_help,   Fortran_functions, Fortran_suffixes   },
+  { "go",        Go_help,        Go_functions,      Go_suffixes        },
   { "html",      HTML_help,      HTML_labels,       HTML_suffixes      },
   { "java",      Cjava_help,     Cjava_entries,     Cjava_suffixes     },
   { "lisp",      Lisp_help,      Lisp_functions,    Lisp_suffixes      },
@@ -781,6 +815,7 @@ static language lang_names [] =
   { "proc",      no_lang_help,   plain_C_entries,   plain_C_suffixes   },
   { "prolog",    Prolog_help,    Prolog_functions,  Prolog_suffixes    },
   { "python",    Python_help,    Python_functions,  Python_suffixes    },
+  { "ruby",      Ruby_help,Ruby_functions,Ruby_suffixes,Ruby_filenames },
   { "scheme",    Scheme_help,    Scheme_functions,  Scheme_suffixes    },
   { "tex",       TeX_help,       TeX_commands,      TeX_suffixes       },
   { "texinfo",   Texinfo_help,   Texinfo_nodes,     Texinfo_suffixes   },
@@ -1451,8 +1486,16 @@ get_language_from_filename (char *file, int case_sensitive)
 {
   language *lang;
   const char **name, **ext, *suffix;
+  char *slash;
 
   /* Try whole file name first. */
+  slash = strrchr (file, '/');
+  if (slash != NULL)
+    file = slash + 1;
+#ifdef DOS_NT
+  else if (file[0] && file[1] == ':')
+    file += 2;
+#endif
   for (lang = lang_names; lang->name != NULL; lang++)
     if (lang->filenames != NULL)
       for (name = lang->filenames; *name != NULL; name++)
@@ -4181,6 +4224,73 @@ Fortran_functions (FILE *inf)
     }
 }
 
+\f
+/*
+ * Go language support
+ * Original code by Xi Lu <lx@shellcodes.org> (2016)
+ */
+static void
+Go_functions(FILE *inf)
+{
+  char *cp, *name;
+
+  LOOP_ON_INPUT_LINES(inf, lb, cp)
+    {
+      cp = skip_spaces (cp);
+
+      if (LOOKING_AT (cp, "package"))
+       {
+         name = cp;
+         while (!notinname (*cp) && *cp != '\0')
+           cp++;
+         make_tag (name, cp - name, false, lb.buffer,
+                   cp - lb.buffer + 1, lineno, linecharno);
+       }
+      else if (LOOKING_AT (cp, "func"))
+       {
+         /* Go implementation of interface, such as:
+            func (n *Integer) Add(m Integer) ...
+            skip `(n *Integer)` part.
+         */
+         if (*cp == '(')
+           {
+             while (*cp != ')')
+               cp++;
+             cp = skip_spaces (cp+1);
+           }
+
+         if (*cp)
+           {
+             name = cp;
+
+             while (!notinname (*cp))
+               cp++;
+
+             make_tag (name, cp - name, true, lb.buffer,
+                       cp - lb.buffer + 1, lineno, linecharno);
+           }
+       }
+      else if (members && LOOKING_AT (cp, "type"))
+       {
+         name = cp;
+
+         /* Ignore the likes of the following:
+            type (
+                   A
+            )
+          */
+         if (*cp == '(')
+           return;
+
+         while (!notinname (*cp) && *cp != '\0')
+           cp++;
+
+         make_tag (name, cp - name, false, lb.buffer,
+                   cp - lb.buffer + 1, lineno, linecharno);
+       }
+    }
+}
+
 \f
 /*
  * Ada parsing
@@ -4513,6 +4623,175 @@ Python_functions (FILE *inf)
     }
 }
 
+/*
+ * Ruby support
+ * Original code by Xi Lu <lx@shellcodes.org> (2015)
+ */
+static void
+Ruby_functions (FILE *inf)
+{
+  char *cp = NULL;
+  bool reader = false, writer = false, alias = false, continuation = false;
+
+  LOOP_ON_INPUT_LINES (inf, lb, cp)
+    {
+      bool is_class = false;
+      bool is_method = false;
+      char *name;
+
+      cp = skip_spaces (cp);
+      if (!continuation
+         /* Constants.  */
+         && c_isalpha (*cp) && c_isupper (*cp))
+       {
+         char *bp, *colon = NULL;
+
+         name = cp;
+
+         for (cp++; c_isalnum (*cp) || *cp == '_' || *cp == ':'; cp++)
+           {
+             if (*cp == ':')
+               colon = cp;
+           }
+         if (cp > name + 1)
+           {
+             bp = skip_spaces (cp);
+             if (*bp == '=' && !(bp[1] == '=' || bp[1] == '>'))
+               {
+                 if (colon && !c_isspace (colon[1]))
+                   name = colon + 1;
+                 make_tag (name, cp - name, false,
+                           lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
+               }
+           }
+       }
+      else if (!continuation
+              /* Modules, classes, methods.  */
+              && ((is_method = LOOKING_AT (cp, "def"))
+                  || (is_class = LOOKING_AT (cp, "class"))
+                  || LOOKING_AT (cp, "module")))
+       {
+         const char self_name[] = "self.";
+         const size_t self_size1 = sizeof (self_name) - 1;
+
+         name = cp;
+
+        /* Ruby method names can end in a '='.  Also, operator overloading can
+           define operators whose names include '='.  */
+         while (!notinname (*cp) || *cp == '=')
+           cp++;
+
+         /* Remove "self." from the method name.  */
+         if (cp - name > self_size1
+             && strneq (name, self_name, self_size1))
+           name += self_size1;
+
+         /* Remove the class/module qualifiers from method names.  */
+         if (is_method)
+           {
+             char *q;
+
+             for (q = name; q < cp && *q != '.'; q++)
+               ;
+             if (q < cp - 1)   /* punt if we see just "FOO." */
+               name = q + 1;
+           }
+
+         /* Don't tag singleton classes.  */
+         if (is_class && strneq (name, "<<", 2) && cp == name + 2)
+           continue;
+
+         make_tag (name, cp - name, true,
+                   lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
+       }
+      else
+       {
+         /* Tag accessors and aliases.  */
+
+         if (!continuation)
+           reader = writer = alias = false;
+
+         while (*cp && *cp != '#')
+           {
+             if (!continuation)
+               {
+                 reader = writer = alias = false;
+                 if (LOOKING_AT (cp, "attr_reader"))
+                   reader = true;
+                 else if (LOOKING_AT (cp, "attr_writer"))
+                   writer = true;
+                 else if (LOOKING_AT (cp, "attr_accessor"))
+                   {
+                     reader = true;
+                     writer = true;
+                   }
+                 else if (LOOKING_AT (cp, "alias_method"))
+                   alias = true;
+               }
+             if (reader || writer || alias)
+               {
+                 do {
+                   char *np;
+
+                   cp = skip_spaces (cp);
+                   if (*cp == '(')
+                     cp = skip_spaces (cp + 1);
+                   np = cp;
+                   cp = skip_name (cp);
+                   if (*np != ':')
+                     continue;
+                   np++;
+                   if (reader)
+                     {
+                       make_tag (np, cp - np, true,
+                                 lb.buffer, cp - lb.buffer + 1,
+                                 lineno, linecharno);
+                       continuation = false;
+                     }
+                   if (writer)
+                     {
+                       size_t name_len = cp - np + 1;
+                       char *wr_name = xnew (name_len + 1, char);
+
+                       memcpy (wr_name, np, name_len - 1);
+                       memcpy (wr_name + name_len - 1, "=", 2);
+                       pfnote (wr_name, true, lb.buffer, cp - lb.buffer + 1,
+                               lineno, linecharno);
+                       continuation = false;
+                     }
+                   if (alias)
+                     {
+                       if (!continuation)
+                         make_tag (np, cp - np, true,
+                                   lb.buffer, cp - lb.buffer + 1,
+                                   lineno, linecharno);
+                       continuation = false;
+                       while (*cp && *cp != '#' && *cp != ';')
+                         {
+                           if (*cp == ',')
+                             continuation = true;
+                           else if (!c_isspace (*cp))
+                             continuation = false;
+                           cp++;
+                         }
+                       if (*cp == ';')
+                         continuation = false;
+                     }
+                   cp = skip_spaces (cp);
+                 } while ((alias
+                           ? (*cp == ',')
+                           : (continuation = (*cp == ',')))
+                          && (cp = skip_spaces (cp + 1), *cp && *cp != '#'));
+               }
+             if (*cp != '#')
+               cp = skip_name (cp);
+             while (*cp && *cp != '#' && notinname (*cp))
+               cp++;
+           }
+       }
+    }
+}
+
 \f
 /*
  * PHP support
@@ -4929,13 +5208,29 @@ Lua_functions (FILE *inf)
 
   LOOP_ON_INPUT_LINES (inf, lb, bp)
     {
+      bp = skip_spaces (bp);
       if (bp[0] != 'f' && bp[0] != 'l')
        continue;
 
       (void)LOOKING_AT (bp, "local"); /* skip possible "local" */
 
       if (LOOKING_AT (bp, "function"))
-       get_tag (bp, NULL);
+       {
+         char *tag_name, *tp_dot, *tp_colon;
+
+         get_tag (bp, &tag_name);
+         /* If the tag ends with ".foo" or ":foo", make an additional tag for
+            "foo".  */
+         tp_dot = strrchr (tag_name, '.');
+         tp_colon = strrchr (tag_name, ':');
+         if (tp_dot || tp_colon)
+           {
+             char *p = tp_dot > tp_colon ? tp_dot : tp_colon;
+             int len_add = p - tag_name + 1;
+
+             get_tag (bp + len_add, NULL);
+           }
+       }
     }
 }