]> code.delx.au - gnu-emacs/blobdiff - lib-src/etags.c
Ibuffer change marks
[gnu-emacs] / lib-src / etags.c
index adc08a2367817967532dc3244d1716ff3bb228c8..1c85a79289694f46ceb18f607b2ea67dd8879026 100644 (file)
@@ -35,8 +35,8 @@ This file is not considered part of GNU Emacs.
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -112,7 +112,6 @@ char pot_etags_version[] = "@(#) pot revision number is 17.38.1.4";
 
 #ifdef WINDOWSNT
 # include <direct.h>
-# define MAXPATHLEN _MAX_PATH
 # undef HAVE_NTGUI
 # undef  DOS_NT
 # define DOS_NT
@@ -354,6 +353,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 *);
@@ -641,6 +641,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 [] =
@@ -675,8 +679,8 @@ static const char *Objc_suffixes [] =
 static const char Objc_help [] =
 "In Objective C code, tags include Objective C definitions for classes,\n\
 class categories, methods and protocols.  Tags for variables and\n\
-functions in classes are named 'CLASS::VARIABLE' and 'CLASS::FUNCTION'.\n\
-(Use --help --lang=c --lang=objc --lang=java for full help.)";
+functions in classes are named 'CLASS::VARIABLE' and 'CLASS::FUNCTION'.\
+\n(Use --help --lang=c --lang=objc --lang=java for full help.)";
 
 static const char *Pascal_suffixes [] =
   { "p", "pas", NULL };
@@ -724,10 +728,12 @@ static const char Python_help [] =
 generate a tag.";
 
 static const char *Ruby_suffixes [] =
-  { "rb", "ruby", NULL };
+  { "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.";
+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 [] =
@@ -794,6 +800,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      },
@@ -807,7 +814,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",      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   },
@@ -963,11 +970,12 @@ Relative ones are stored relative to the output file's directory.\n");
        in some languages.");
 
   puts ("-Q, --class-qualify\n\
-        Qualify tag names with their class name in C++, ObjC, and Java.\n\
+        Qualify tag names with their class name in C++, ObjC, Java, and Perl.\n\
         This produces tag names of the form \"class::member\" for C++,\n\
         \"class(category)\" for Objective C, and \"class.member\" for Java.\n\
         For Objective C, this also produces class methods qualified with\n\
-        their arguments, as in \"foo:bar:baz:more\".");
+        their arguments, as in \"foo:bar:baz:more\".\n\
+        For Perl, this produces \"package::member\".");
   puts ("-r REGEXP, --regex=REGEXP or --regex=@regexfile\n\
         Make a tag for each line matching a regular expression pattern\n\
        in the following files.  {LANGUAGE}REGEXP uses REGEXP for LANGUAGE\n\
@@ -1334,7 +1342,7 @@ main (int argc, char **argv)
     {
       char *cmd =
        xmalloc (strlen (tagfile) + whatlen_max +
-                sizeof "mv..OTAGS;fgrep -v '\t\t' OTAGS >;rm OTAGS");
+                sizeof "mv..OTAGS;grep -Fv '\t\t' OTAGS >;rm OTAGS");
       for (i = 0; i < current_arg; ++i)
        {
          switch (argbuffer[i].arg_type)
@@ -1347,7 +1355,7 @@ main (int argc, char **argv)
            }
          char *z = stpcpy (cmd, "mv ");
          z = stpcpy (z, tagfile);
-         z = stpcpy (z, " OTAGS;fgrep -v '\t");
+         z = stpcpy (z, " OTAGS;grep -Fv '\t");
          z = stpcpy (z, argbuffer[i].what);
          z = stpcpy (z, "\t' OTAGS >");
          z = stpcpy (z, tagfile);
@@ -1477,8 +1485,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++)
@@ -4053,13 +4069,13 @@ Yacc_entries (FILE *inf)
   ((assert ("" kw), true)   /* syntax error if not a literal string */ \
    && strneq ((cp), kw, sizeof (kw)-1)         /* cp points at kw */   \
    && notinname ((cp)[sizeof (kw)-1])          /* end of kw */         \
-   && ((cp) = skip_spaces ((cp)+sizeof (kw)-1))) /* skip spaces */
+   && ((cp) = skip_spaces ((cp) + sizeof (kw) - 1), true)) /* skip spaces */
 
 /* Similar to LOOKING_AT but does not use notinname, does not skip */
 #define LOOKING_AT_NOCASE(cp, kw) /* the keyword is a literal string */        \
   ((assert ("" kw), true) /* syntax error if not a literal string */   \
    && strncaseeq ((cp), kw, sizeof (kw)-1)     /* cp points at kw */   \
-   && ((cp) += sizeof (kw)-1))                 /* skip spaces */
+   && ((cp) += sizeof (kw) - 1, true))         /* skip spaces */
 
 /*
  * Read a file, but do no processing.  This is used to do regexp
@@ -4207,6 +4223,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
@@ -4451,10 +4534,21 @@ Perl_functions (FILE *inf)
            continue;           /* nothing found */
          pos = strchr (sp, ':');
          if (pos && pos < cp && pos[1] == ':')
-           /* The name is already qualified. */
-           make_tag (sp, cp - sp, true,
-                     lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
-         else
+           {
+             /* The name is already qualified. */
+             if (!class_qualify)
+               {
+                 char *q = pos + 2, *qpos;
+                 while ((qpos = strchr (q, ':')) != NULL
+                        && qpos < cp
+                        && qpos[1] == ':')
+                   q = qpos + 2;
+                 sp = q;
+               }
+             make_tag (sp, cp - sp, true,
+                       lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
+           }
+         else if (class_qualify)
            /* Qualify it. */
            {
              char savechar, *name;
@@ -4467,6 +4561,9 @@ Perl_functions (FILE *inf)
                        lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
              free (name);
            }
+         else
+           make_tag (sp, cp - sp, true,
+                     lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
        }
       else if (LOOKING_AT (cp, "use constant")
               || LOOKING_AT (cp, "use constant::defer"))
@@ -4547,6 +4644,7 @@ 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)
     {
@@ -4555,7 +4653,9 @@ Ruby_functions (FILE *inf)
       char *name;
 
       cp = skip_spaces (cp);
-      if (c_isalpha (*cp) && c_isupper (*cp)) /* constants */
+      if (!continuation
+         /* Constants.  */
+         && c_isalpha (*cp) && c_isupper (*cp))
        {
          char *bp, *colon = NULL;
 
@@ -4569,7 +4669,7 @@ Ruby_functions (FILE *inf)
          if (cp > name + 1)
            {
              bp = skip_spaces (cp);
-             if (*bp == '=' && c_isspace (bp[1]))
+             if (*bp == '=' && !(bp[1] == '=' || bp[1] == '>'))
                {
                  if (colon && !c_isspace (colon[1]))
                    name = colon + 1;
@@ -4578,12 +4678,14 @@ Ruby_functions (FILE *inf)
                }
            }
        }
-      else if ((is_method = LOOKING_AT (cp, "def")) /* module/class/method */
-              || (is_class = LOOKING_AT (cp, "class"))
-              || LOOKING_AT (cp, "module"))
+      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.") - 1;
+         const size_t self_size1 = sizeof (self_name) - 1;
 
          name = cp;
 
@@ -4615,6 +4717,91 @@ Ruby_functions (FILE *inf)
          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++;
+           }
+       }
     }
 }