]> code.delx.au - gnu-emacs/blobdiff - src/syntax.c
(skip_invisible): Use new retval of TEXT_PROP_MEANS_INVISIBLE.
[gnu-emacs] / src / syntax.c
index b30635f062da0a4d13110e14c3b6cd0e6b265b40..6a17e6599792495aedb90e2afbf96f172b97f931 100644 (file)
@@ -25,6 +25,7 @@ Boston, MA 02111-1307, USA.  */
 #include "commands.h"
 #include "buffer.h"
 #include "charset.h"
+#include "keymap.h"
 
 /* Make syntax table lookup grant data in gl_state.  */
 #define SYNTAX_ENTRY_VIA_PROPERTY
@@ -133,17 +134,17 @@ update_syntax_table (charpos, count, init, object)
 
   if (init)
     {
+      gl_state.old_prop = Qnil;
       gl_state.start = gl_state.b_property;
       gl_state.stop = gl_state.e_property;
-      gl_state.forward_i = interval_of (charpos, object);
-      i = gl_state.backward_i = gl_state.forward_i;
-      gl_state.left_ok = gl_state.right_ok = 1;
+      i = interval_of (charpos, object);
+      gl_state.backward_i = gl_state.forward_i = i;
       invalidate = 0;
       if (NULL_INTERVAL_P (i))
        return;
       /* interval_of updates only ->position of the return value, so
         update the parents manually to speed up update_interval.  */
-      while (!NULL_PARENT (i)) 
+      while (!NULL_PARENT (i))
        {
          if (AM_RIGHT_CHILD (i))
            INTERVAL_PARENT (i)->position = i->position
@@ -156,13 +157,13 @@ update_syntax_table (charpos, count, init, object)
          i = INTERVAL_PARENT (i);
        }
       i = gl_state.forward_i;
-      gl_state.b_property = i->position - 1 - gl_state.offset;
+      gl_state.b_property = i->position - gl_state.offset;
       gl_state.e_property = INTERVAL_LAST_POS (i) - gl_state.offset;
       goto update;
     }
   oldi = i = count > 0 ? gl_state.forward_i : gl_state.backward_i;
 
-  /* We are guarantied to be called with CHARPOS either in i,
+  /* We are guaranteed to be called with CHARPOS either in i,
      or further off.  */
   if (NULL_INTERVAL_P (i))
     error ("Error in syntax_table logic for to-the-end intervals");
@@ -172,33 +173,26 @@ update_syntax_table (charpos, count, init, object)
        error ("Error in syntax_table logic for intervals <-");
       /* Update the interval.  */
       i = update_interval (i, charpos);
-      if (oldi->position != INTERVAL_LAST_POS (i))
+      if (INTERVAL_LAST_POS (i) != gl_state.b_property)
        {
          invalidate = 0;
-         gl_state.right_ok = 1;        /* Invalidate the other end.  */
          gl_state.forward_i = i;
          gl_state.e_property = INTERVAL_LAST_POS (i) - gl_state.offset;
        }
-    } 
+    }
   else if (charpos >= INTERVAL_LAST_POS (i)) /* Move right.  */
     {
       if (count < 0)
        error ("Error in syntax_table logic for intervals ->");
       /* Update the interval.  */
       i = update_interval (i, charpos);
-      if (i->position != INTERVAL_LAST_POS (oldi))
+      if (i->position != gl_state.e_property)
        {
          invalidate = 0;
-         gl_state.left_ok = 1;         /* Invalidate the other end.  */
          gl_state.backward_i = i;
-         gl_state.b_property = i->position - 1 - gl_state.offset;
+         gl_state.b_property = i->position - gl_state.offset;
        }
     }
-  else if (count > 0 ? gl_state.right_ok : gl_state.left_ok)
-    {
-      /* We do not need to recalculate tmp_table.  */
-      tmp_table = gl_state.old_prop;
-    }
 
   update:
   tmp_table = textget (i->plist, Qsyntax_table);
@@ -206,38 +200,39 @@ update_syntax_table (charpos, count, init, object)
   if (invalidate)
     invalidate = !EQ (tmp_table, gl_state.old_prop); /* Need to invalidate? */
       
-  if (invalidate)                      /* Did not get to adjacent interval.  */
-    {                                  /* with the same table => */
-                                       /* invalidate the old range.  */
+  if (invalidate)              /* Did not get to adjacent interval.  */
+    {                          /* with the same table => */
+                               /* invalidate the old range.  */
       if (count > 0)
        {
          gl_state.backward_i = i;
-         gl_state.left_ok = 1;         /* Invalidate the other end.  */
-         gl_state.b_property = i->position - 1 - gl_state.offset;
-       } 
-      else 
+         gl_state.b_property = i->position - gl_state.offset;
+       }
+      else
        {
-         gl_state.forward_i = i;       
-         gl_state.right_ok = 1;        /* Invalidate the other end.  */
+         gl_state.forward_i = i;
          gl_state.e_property = INTERVAL_LAST_POS (i) - gl_state.offset;
        }
     }
 
-  gl_state.current_syntax_table = tmp_table;
-  gl_state.old_prop = tmp_table;
-  if (EQ (Fsyntax_table_p (tmp_table), Qt))
+  if (!EQ (tmp_table, gl_state.old_prop))
     {
-      gl_state.use_global = 0;
-    } 
-  else if (CONSP (tmp_table))
-    {
-      gl_state.use_global = 1;
-      gl_state.global_code = tmp_table;
-    }
-  else 
-    {
-      gl_state.use_global = 0;
-      gl_state.current_syntax_table = current_buffer->syntax_table;
+      gl_state.current_syntax_table = tmp_table;
+      gl_state.old_prop = tmp_table;
+      if (EQ (Fsyntax_table_p (tmp_table), Qt))
+       {
+         gl_state.use_global = 0;
+       } 
+      else if (CONSP (tmp_table))
+       {
+         gl_state.use_global = 1;
+         gl_state.global_code = tmp_table;
+       }
+      else 
+       {
+         gl_state.use_global = 0;
+         gl_state.current_syntax_table = current_buffer->syntax_table;
+       }
     }
 
   while (!NULL_INTERVAL_P (i))
@@ -245,42 +240,39 @@ update_syntax_table (charpos, count, init, object)
       if (cnt && !EQ (tmp_table, textget (i->plist, Qsyntax_table)))
        {
          if (count > 0)
-           gl_state.right_ok = 0;
-         else 
-           gl_state.left_ok = 0;
-         break;          
+           {
+             gl_state.e_property = i->position - gl_state.offset;
+             gl_state.forward_i = i;
+           }
+         else
+           {
+             gl_state.b_property = i->position + LENGTH (i) - gl_state.offset;
+             gl_state.backward_i = i;
+           }
+         return;
        }
       else if (cnt == INTERVALS_AT_ONCE) 
        {
          if (count > 0)
-           gl_state.right_ok = 1;
-         else 
-           gl_state.left_ok = 1;
-         break;
+           {
+             gl_state.e_property = i->position + LENGTH (i) - gl_state.offset;
+             gl_state.forward_i = i;
+           }
+         else
+           {
+             gl_state.b_property = i->position - gl_state.offset;
+             gl_state.backward_i = i;
+           }
+         return;
        }
       cnt++;
       i = count > 0 ? next_interval (i) : previous_interval (i);
     }
-  if (NULL_INTERVAL_P (i)) 
-    {                                  /* This property goes to the end.  */
-      if (count > 0)
-       gl_state.e_property = gl_state.stop;
-      else
-       gl_state.b_property = gl_state.start;
-    } 
-  else 
-    {
-      if (count > 0) 
-       {
-         gl_state.e_property = i->position - gl_state.offset;
-         gl_state.forward_i = i;
-       }
-      else 
-       {
-         gl_state.b_property = i->position + LENGTH (i) - 1 - gl_state.offset;
-         gl_state.backward_i = i;
-       }
-    }    
+  eassert (NULL_INTERVAL_P (i)); /* This property goes to the end.  */
+  if (count > 0)
+    gl_state.e_property = gl_state.stop;
+  else
+    gl_state.b_property = gl_state.start;
 }
 \f
 /* Returns TRUE if char at CHARPOS is quoted.
@@ -341,9 +333,15 @@ dec_bytepos (bytepos)
   return bytepos;
 }
 \f
-/* Find a defun-start that is the last one before POS (or nearly the last).
-   We record what we find, so that another call in the same area
-   can return the same value right away.  
+/* Return a defun-start position before before POS and not too far before.
+   It should be the last one before POS, or nearly the last.
+
+   When open_paren_in_column_0_is_defun_start is nonzero,
+   the beginning of every line is treated as a defun-start.
+
+   We record the information about where the scan started
+   and what its result was, so that another call in the same area
+   can return the same value very quickly.
 
    There is no promise at which position the global syntax data is
    valid on return from the subroutine, so the caller should explicitly
@@ -425,19 +423,19 @@ prev_char_comend_first (pos, pos_byte)
 
 /* Return the SYNTAX_COMSTART_FIRST of the character before POS, POS_BYTE.  */
 
-static int
-prev_char_comstart_first (pos, pos_byte)
-     int pos, pos_byte;
-{
-  int c, val;
-
-  DEC_BOTH (pos, pos_byte);
-  UPDATE_SYNTAX_TABLE_BACKWARD (pos);
-  c = FETCH_CHAR (pos_byte);
-  val = SYNTAX_COMSTART_FIRST (c);
-  UPDATE_SYNTAX_TABLE_FORWARD (pos + 1);
-  return val;
-}
+/* static int
+ * prev_char_comstart_first (pos, pos_byte)
*      int pos, pos_byte;
+ * {
*   int c, val;
+ * 
*   DEC_BOTH (pos, pos_byte);
*   UPDATE_SYNTAX_TABLE_BACKWARD (pos);
*   c = FETCH_CHAR (pos_byte);
*   val = SYNTAX_COMSTART_FIRST (c);
*   UPDATE_SYNTAX_TABLE_FORWARD (pos + 1);
*   return val;
+ * } */
 
 /* Checks whether charpos FROM is at the end of a comment.
    FROM_BYTE is the bytepos corresponding to FROM.
@@ -469,13 +467,16 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
      PARITY is current parity of quotes from the comment end.  */
   int string_style = -1;       /* Presumed outside of any string. */
   int string_lossage = 0;
+  /* Not a real lossage: indicates that we have passed a matching comment
+     starter plus an non-matching comment-ender, meaning that any matching
+     comment-starter we might see later could be a false positive (hidden
+     inside another comment).
+     Test case:  { a (* b } c (* d *) */
+  int comment_lossage = 0;
   int comment_end = from;
   int comment_end_byte = from_byte;
   int comstart_pos = 0;
   int comstart_byte;
-  /* Value that PARITY had, when we reached the position
-     in COMSTART_POS.  */
-  int scanstart = from - 1;
   /* Place where the containing defun starts,
      or 0 if we didn't come across it yet.  */
   int defun_start = 0;
@@ -483,44 +484,82 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
   register enum syntaxcode code;
   int nesting = 1;             /* current comment nesting */
   int c;
+  int syntax = 0;
+
+  /* FIXME: A }} comment-ender style leads to incorrect behavior
+     in the case of {{ c }}} because we ignore the last two chars which are
+     assumed to be comment-enders although they aren't.  */
 
   /* At beginning of range to scan, we're outside of strings;
      that determines quote parity to the comment-end.  */
   while (from != stop)
     {
-      int temp_byte;
+      int temp_byte, prev_syntax;
+      int com2start, com2end;
 
       /* Move back and examine a character.  */
       DEC_BOTH (from, from_byte);
       UPDATE_SYNTAX_TABLE_BACKWARD (from);
 
+      prev_syntax = syntax;
       c = FETCH_CHAR (from_byte);
+      syntax = SYNTAX_WITH_FLAGS (c);
       code = SYNTAX (c);
 
-      /* If this char is the second of a 2-char comment end sequence,
-        back up and give the pair the appropriate syntax.  */
-      if (from > stop && SYNTAX_COMEND_SECOND (c)
-         && prev_char_comend_first (from, from_byte))
+      /* Check for 2-char comment markers.  */
+      com2start = (SYNTAX_FLAGS_COMSTART_FIRST (syntax)
+                  && SYNTAX_FLAGS_COMSTART_SECOND (prev_syntax)
+                  && comstyle == SYNTAX_FLAGS_COMMENT_STYLE (prev_syntax)
+                  && (SYNTAX_FLAGS_COMMENT_NESTED (prev_syntax)
+                      || SYNTAX_FLAGS_COMMENT_NESTED (syntax)) == comnested);
+      com2end = (SYNTAX_FLAGS_COMEND_FIRST (syntax)
+                && SYNTAX_FLAGS_COMEND_SECOND (prev_syntax));
+
+      /* Nasty cases with overlapping 2-char comment markers:
+        - snmp-mode: -- c -- foo -- c --
+                     --- c --
+                     ------ c --
+        - c-mode:    *||*
+                     |* *|* *|
+                     |*| |* |*|
+                     ///   */
+
+      /* If a 2-char comment sequence partly overlaps with another,
+        we don't try to be clever.  */
+      if (from > stop && (com2end || com2start))
        {
-         code = Sendcomment;
-         DEC_BOTH (from, from_byte);
-         UPDATE_SYNTAX_TABLE_BACKWARD (from);
-         c = FETCH_CHAR (from_byte);
-       }
-                       
-      /* If this char starts a 2-char comment start sequence,
-        treat it like a 1-char comment starter.  */
-      if (from < scanstart && SYNTAX_COMSTART_FIRST (c))
-       {
-         temp_byte = inc_bytepos (from_byte);
-         UPDATE_SYNTAX_TABLE_FORWARD (from + 1);
-         if (SYNTAX_COMSTART_SECOND (FETCH_CHAR (temp_byte))
-             && comstyle == SYNTAX_COMMENT_STYLE (FETCH_CHAR (temp_byte)))
-           code = Scomment;
-         UPDATE_SYNTAX_TABLE_BACKWARD (from);
+         int next = from, next_byte = from_byte, next_c, next_syntax;
+         DEC_BOTH (next, next_byte);
+         UPDATE_SYNTAX_TABLE_BACKWARD (next);
+         next_c = FETCH_CHAR (next_byte);
+         next_syntax = SYNTAX_WITH_FLAGS (next_c);
+         if (((com2start || comnested)
+              && SYNTAX_FLAGS_COMEND_SECOND (syntax)
+              && SYNTAX_FLAGS_COMEND_FIRST (next_syntax))
+             || ((com2end || comnested)
+                 && SYNTAX_FLAGS_COMSTART_SECOND (syntax)
+                 && comstyle == SYNTAX_FLAGS_COMMENT_STYLE (syntax)
+                 && SYNTAX_FLAGS_COMSTART_FIRST (next_syntax)))
+           goto lossage;
+         /* UPDATE_SYNTAX_TABLE_FORWARD (next + 1); */
        }
-      else if (code == Scomment && comstyle != SYNTAX_COMMENT_STYLE (c))
-       /* Ignore comment starters of a different style.  */
+
+      if (com2start && comstart_pos == 0)
+       /* We're looking at a comment starter.  But it might be a comment
+          ender as well (see snmp-mode).  The first time we see one, we
+          need to consider it as a comment starter,
+          and the subsequent times as a comment ender.  */
+       com2end = 0;
+
+      /* Turn a 2-char comment sequences into the appropriate syntax.  */
+      if (com2end)
+       code = Sendcomment;
+      else if (com2start)
+       code = Scomment;
+      /* Ignore comment starters of a different style.  */
+      else if (code == Scomment
+              && (comstyle != SYNTAX_FLAGS_COMMENT_STYLE (syntax)
+                  || SYNTAX_FLAGS_COMMENT_NESTED (syntax) != comnested))
        continue;
 
       /* Ignore escaped characters, except comment-enders.  */
@@ -548,27 +587,30 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
          
        case Scomment:
          /* We've already checked that it is the relevant comstyle.  */
-         if (string_style != -1 || string_lossage)
+         if (string_style != -1 || comment_lossage || string_lossage)
            /* There are odd string quotes involved, so let's be careful.
               Test case in Pascal: " { " a { " } */
            goto lossage;
 
-         if (comnested && --nesting <= 0)
+         if (!comnested)
+           {
+             /* Record best comment-starter so far.  */
+             comstart_pos = from;
+             comstart_byte = from_byte;
+           }
+         else if (--nesting <= 0)
            /* nested comments have to be balanced, so we don't need to
               keep looking for earlier ones.  We use here the same (slightly
               incorrect) reasoning as below:  since it is followed by uniform
               paired string quotes, this comment-start has to be outside of
               strings, else the comment-end itself would be inside a string. */
            goto done;
-
-         /* Record comment-starters according to that
-            quote-parity to the comment-end.  */
-         comstart_pos = from;
-         comstart_byte = from_byte;
          break;
 
        case Sendcomment:
-         if (SYNTAX_COMMENT_STYLE (FETCH_CHAR (from_byte)) == comstyle)
+         if (SYNTAX_FLAGS_COMMENT_STYLE (syntax) == comstyle
+             && (SYNTAX_FLAGS_COMMENT_NESTED (prev_syntax)
+                 || SYNTAX_FLAGS_COMMENT_NESTED (syntax)) == comnested)
            /* This is the same style of comment ender as ours. */
            {
              if (comnested)
@@ -578,6 +620,15 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
                   this comment-ender rather than ours.  */
                from = stop;    /* Break out of the loop.  */
            }
+         else if (comstart_pos != 0 || c != '\n')
+           /* We're mixing comment styles here, so we'd better be careful.
+              The (comstart_pos != 0 || c != '\n') check is not quite correct
+              (we should just always set comment_lossage), but removing it
+              would imply that any multiline comment in C would go through
+              lossage, which seems overkill.
+              The failure should only happen in the rare cases such as
+                { (* } *)   */
+           comment_lossage = 1;
          break;
 
        case Sopen:
@@ -594,7 +645,7 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
          break;
 
        default:
-         continue;
+         break;
        }
     }
 
@@ -604,12 +655,9 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
       from_byte = comment_end_byte;
       UPDATE_SYNTAX_TABLE_FORWARD (comment_end - 1);
     }
-  /* If the earliest comment starter
-     is followed by uniform paired string quotes or none,
-     we know it can't be inside a string
-     since if it were then the comment ender would be inside one.
-     So it does start a comment.  Skip back to it.  */
-  else if (!comnested)
+  /* If comstart_pos is set and we get here (ie. didn't jump to `lossage'
+     or `done'), then we've found the beginning of the non-nested comment.  */
+  else if (1)  /* !comnested */
     {
       from = comstart_pos;
       from_byte = comstart_byte;
@@ -844,86 +892,35 @@ DEFUN ("matching-paren", Fmatching_paren, Smatching_paren, 1, 1, 0,
   return Qnil;
 }
 
-/* This comment supplies the doc string for modify-syntax-entry,
-   for make-docfile to see.  We cannot put this in the real DEFUN
-   due to limits in the Unix cpp.
-
-DEFUN ("modify-syntax-entry", foo, bar, 2, 3, 0,
-  "Set syntax for character CHAR according to string S.\n\
-The syntax is changed only for table TABLE, which defaults to\n\
- the current buffer's syntax table.\n\
-The first character of S should be one of the following:\n\
-  Space or -  whitespace syntax.    w   word constituent.\n\
-  _           symbol constituent.   .   punctuation.\n\
-  (           open-parenthesis.     )   close-parenthesis.\n\
-  \"           string quote.         \\   escape.\n\
-  $           paired delimiter.     '   expression quote or prefix operator.\n\
-  <           comment starter.      >   comment ender.\n\
-  /           character-quote.      @   inherit from `standard-syntax-table'.\n\
-\n\
-Only single-character comment start and end sequences are represented thus.\n\
-Two-character sequences are represented as described below.\n\
-The second character of S is the matching parenthesis,\n\
- used only if the first character is `(' or `)'.\n\
-Any additional characters are flags.\n\
-Defined flags are the characters 1, 2, 3, 4, b, p, and n.\n\
- 1 means CHAR is the start of a two-char comment start sequence.\n\
- 2 means CHAR is the second character of such a sequence.\n\
- 3 means CHAR is the start of a two-char comment end sequence.\n\
- 4 means CHAR is the second character of such a sequence.\n\
-\n\
-There can be up to two orthogonal comment sequences.  This is to support\n\
-language modes such as C++.  By default, all comment sequences are of style\n\
-a, but you can set the comment sequence style to b (on the second character\n\
-of a comment-start, or the first character of a comment-end sequence) using\n\
-this flag:\n\
- b means CHAR is part of comment sequence b.\n\
- n means CHAR is part of a nestable comment sequence.\n\
-\n\
- p means CHAR is a prefix character for `backward-prefix-chars';\n\
-   such characters are treated as whitespace when they occur\n\
-   between expressions.")
-  (char, s, table)
-*/
-
-DEFUN ("modify-syntax-entry", Fmodify_syntax_entry, Smodify_syntax_entry, 2, 3, 
-  /* I really don't know why this is interactive
-     help-form should at least be made useful whilst reading the second arg
-   */
-  "cSet syntax for character: \nsSet syntax for %s to: ",
-  0 /* See immediately above */)
-  (c, newentry, syntax_table)
-     Lisp_Object c, newentry, syntax_table;
+DEFUN ("string-to-syntax", Fstring_to_syntax, Sstring_to_syntax, 1, 1, 0,
+  "Convert a syntax specification STRING into syntax cell form.\n\
+STRING should be a string as it is allowed as argument of\n\
+`modify-syntax-entry'.  Value is the equivalent cons cell\n\
+\(CODE . MATCHING-CHAR) that can be used as value of a `syntax-table'\n\
+text property.")
+  (string)
+     Lisp_Object string;
 {
   register unsigned char *p;
   register enum syntaxcode code;
   int val;
   Lisp_Object match;
 
-  CHECK_NUMBER (c, 0);
-  CHECK_STRING (newentry, 1);
-
-  if (NILP (syntax_table))
-    syntax_table = current_buffer->syntax_table;
-  else
-    check_syntax_table (syntax_table);
+  CHECK_STRING (string, 0);
 
-  p = XSTRING (newentry)->data;
+  p = XSTRING (string)->data;
   code = (enum syntaxcode) syntax_spec_code[*p++];
   if (((int) code & 0377) == 0377)
     error ("invalid syntax description letter: %c", p[-1]);
 
   if (code == Sinherit)
-    {
-      SET_RAW_SYNTAX_ENTRY (syntax_table, XINT (c), Qnil);
-      return Qnil;
-    }
+    return Qnil;
 
   if (*p)
     {
       int len;
       int character = (STRING_CHAR_AND_LENGTH
-                      (p, STRING_BYTES (XSTRING (newentry)) - 1, len));
+                      (p, STRING_BYTES (XSTRING (string)) - 1, len));
       XSETINT (match, character);
       if (XFASTINT (match) == ' ')
        match = Qnil;
@@ -966,13 +963,72 @@ DEFUN ("modify-syntax-entry", Fmodify_syntax_entry, Smodify_syntax_entry, 2, 3,
       }
        
   if (val < XVECTOR (Vsyntax_code_object)->size && NILP (match))
-    newentry = XVECTOR (Vsyntax_code_object)->contents[val];
+    return XVECTOR (Vsyntax_code_object)->contents[val];
   else
     /* Since we can't use a shared object, let's make a new one.  */
-    newentry = Fcons (make_number (val), match);
-    
-  SET_RAW_SYNTAX_ENTRY (syntax_table, XINT (c), newentry);
+    return Fcons (make_number (val), match);
+}
+
+/* This comment supplies the doc string for modify-syntax-entry,
+   for make-docfile to see.  We cannot put this in the real DEFUN
+   due to limits in the Unix cpp.
+
+DEFUN ("modify-syntax-entry", foo, bar, 2, 3, 0,
+  "Set syntax for character CHAR according to string S.\n\
+The syntax is changed only for table TABLE, which defaults to\n\
+ the current buffer's syntax table.\n\
+The first character of S should be one of the following:\n\
+  Space or -  whitespace syntax.    w   word constituent.\n\
+  _           symbol constituent.   .   punctuation.\n\
+  (           open-parenthesis.     )   close-parenthesis.\n\
+  \"           string quote.         \\   escape.\n\
+  $           paired delimiter.     '   expression quote or prefix operator.\n\
+  <           comment starter.      >   comment ender.\n\
+  /           character-quote.      @   inherit from `standard-syntax-table'.\n\
+  |           generic string fence. !   generic comment fence.\n\
+\n\
+Only single-character comment start and end sequences are represented thus.\n\
+Two-character sequences are represented as described below.\n\
+The second character of S is the matching parenthesis,\n\
+ used only if the first character is `(' or `)'.\n\
+Any additional characters are flags.\n\
+Defined flags are the characters 1, 2, 3, 4, b, p, and n.\n\
+ 1 means CHAR is the start of a two-char comment start sequence.\n\
+ 2 means CHAR is the second character of such a sequence.\n\
+ 3 means CHAR is the start of a two-char comment end sequence.\n\
+ 4 means CHAR is the second character of such a sequence.\n\
+\n\
+There can be up to two orthogonal comment sequences.  This is to support\n\
+language modes such as C++.  By default, all comment sequences are of style\n\
+a, but you can set the comment sequence style to b (on the second character\n\
+of a comment-start, or the first character of a comment-end sequence) using\n\
+this flag:\n\
+ b means CHAR is part of comment sequence b.\n\
+ n means CHAR is part of a nestable comment sequence.\n\
+\n\
+ p means CHAR is a prefix character for `backward-prefix-chars';\n\
+   such characters are treated as whitespace when they occur\n\
+   between expressions.")
+  (char, s, table)
+*/
 
+DEFUN ("modify-syntax-entry", Fmodify_syntax_entry, Smodify_syntax_entry, 2, 3, 
+  /* I really don't know why this is interactive
+     help-form should at least be made useful whilst reading the second arg
+   */
+  "cSet syntax for character: \nsSet syntax for %s to: ",
+  0 /* See immediately above */)
+  (c, newentry, syntax_table)
+     Lisp_Object c, newentry, syntax_table;
+{
+  CHECK_NUMBER (c, 0);
+
+  if (NILP (syntax_table))
+    syntax_table = current_buffer->syntax_table;
+  else
+    check_syntax_table (syntax_table);
+
+  SET_RAW_SYNTAX_ENTRY (syntax_table, XINT (c), Fstring_to_syntax (newentry));
   return Qnil;
 }
 \f
@@ -1074,7 +1130,7 @@ describe_syntax (value)
     case Sclose:
       insert_string ("close"); break;
     case Squote:
-      insert_string ("quote"); break;
+      insert_string ("prefix"); break;
     case Sstring:
       insert_string ("string"); break;
     case Smath:
@@ -1087,6 +1143,12 @@ describe_syntax (value)
       insert_string ("comment"); break;
     case Sendcomment:
       insert_string ("endcomment"); break;
+    case Sinherit:
+      insert_string ("inherit"); break;
+    case Scomment_fence:
+      insert_string ("comment fence"); break;
+    case Sstring_fence:
+      insert_string ("string fence"); break;
     default:
       insert_string ("invalid");
       return;
@@ -1270,7 +1332,7 @@ and the function returns nil.  Field boundaries are not noticed if\n\
 
   /* Avoid jumping out of an input field.  */
   val = XFASTINT (Fconstrain_to_field (make_number (val), make_number (PT),
-                                      Qt, Qnil));
+                                      Qt, Qnil, Qnil));
   
   SET_PT (val);
   return val == orig_val ? Qt : Qnil;
@@ -1332,15 +1394,10 @@ skip_chars (forwardp, syntaxp, string, lim)
      Lisp_Object string, lim;
 {
   register unsigned int c;
-  register int ch;
   unsigned char fastmap[0400];
   /* If SYNTAXP is 0, STRING may contain multi-byte form of characters
-     of which codes don't fit in FASTMAP.  In that case, we set the
-     first byte of multibyte form (i.e. base leading-code) in FASTMAP
-     and set the actual ranges of characters in CHAR_RANGES.  In the
-     form "X-Y" of STRING, both X and Y must belong to the same
-     character set because a range striding across character sets is
-     meaningless.  */
+     of which codes don't fit in FASTMAP.  In that case, set the
+     ranges of characters in CHAR_RANGES.  */
   int *char_ranges;
   int n_char_ranges = 0;
   int negate = 0;
@@ -1348,12 +1405,34 @@ skip_chars (forwardp, syntaxp, string, lim)
   int multibyte = !NILP (current_buffer->enable_multibyte_characters);
   int string_multibyte;
   int size_byte;
+  unsigned char *str;
+  int len;
 
   CHECK_STRING (string, 0);
   char_ranges = (int *) alloca (XSTRING (string)->size * (sizeof (int)) * 2);
   string_multibyte = STRING_MULTIBYTE (string);
+  str = XSTRING (string)->data;
   size_byte = STRING_BYTES (XSTRING (string));
 
+  /* Adjust the multibyteness of the string to that of the buffer.  */
+  if (multibyte != string_multibyte)
+    {
+      int nbytes;
+
+      if (multibyte)
+       nbytes = count_size_as_multibyte (XSTRING (string)->data,
+                                         XSTRING (string)->size);
+      else
+       nbytes = XSTRING (string)->size;
+      if (nbytes != size_byte)
+       {
+         str = (unsigned char *) alloca (nbytes);
+         copy_text (XSTRING (string)->data, str, size_byte,
+                    string_multibyte, multibyte);
+         size_byte = nbytes;
+       }
+    }
+
   if (NILP (lim))
     XSETINT (lim, forwardp ? ZV : BEGV);
   else
@@ -1367,12 +1446,12 @@ skip_chars (forwardp, syntaxp, string, lim)
 
   bzero (fastmap, sizeof fastmap);
 
-  i = 0, i_byte = 0;
+  i_byte = 0;
 
   if (i_byte < size_byte
       && XSTRING (string)->data[0] == '^')
     {
-      negate = 1; i++, i_byte++;
+      negate = 1; i_byte++;
     }
 
   /* Find the characters specified and set their elements of fastmap.
@@ -1381,16 +1460,8 @@ skip_chars (forwardp, syntaxp, string, lim)
 
   while (i_byte < size_byte)
     {
-      int c_leading_code = XSTRING (string)->data[i_byte];
-
-      FETCH_STRING_CHAR_ADVANCE (c, string, i, i_byte);
-
-      /* Convert multibyteness between what the string has
-        and what the buffer has.  */
-      if (multibyte)
-       c = unibyte_char_to_multibyte (c);
-      else
-       c &= 0377;
+      c = STRING_CHAR_AND_LENGTH (str + i_byte, size_byte - i_byte, len);
+      i_byte += len;
 
       if (syntaxp)
        fastmap[syntax_spec_code[c & 0377]] = 1;
@@ -1401,52 +1472,58 @@ skip_chars (forwardp, syntaxp, string, lim)
              if (i_byte == size_byte)
                break;
 
-             c_leading_code = XSTRING (string)->data[i_byte];
-             FETCH_STRING_CHAR_ADVANCE (c, string, i, i_byte);
+             c = STRING_CHAR_AND_LENGTH (str+i_byte, size_byte-i_byte, len);
+             i_byte += len;
            }
          if (i_byte < size_byte
-             && XSTRING (string)->data[i_byte] == '-')
+             && str[i_byte] == '-')
            {
-             unsigned int c2, c2_leading_code;
+             unsigned int c2;
 
              /* Skip over the dash.  */
-             i++, i_byte++;
+             i_byte++;
 
              if (i_byte == size_byte)
                break;
 
              /* Get the end of the range.  */
-             c2_leading_code = XSTRING (string)->data[i_byte];
-             FETCH_STRING_CHAR_ADVANCE (c2, string, i, i_byte);
+             c2 =STRING_CHAR_AND_LENGTH (str+i_byte, size_byte-i_byte, len);
+             i_byte += len;
 
              if (SINGLE_BYTE_CHAR_P (c))
                {
                  if (! SINGLE_BYTE_CHAR_P (c2))
-                   error ("Invalid charcter range: %s",
-                          XSTRING (string)->data);
+                   {
+                     /* Handle a range starting with a character of
+                        less than 256, and ending with a character of
+                        not less than 256.  Split that into two
+                        ranges, the low one ending at 0377, and the
+                        high one starting at the smallest character
+                        in the charset of C2 and ending at C2.  */
+                     int charset = CHAR_CHARSET (c2);
+                     int c1 = MAKE_CHAR (charset, 0, 0);
+
+                     char_ranges[n_char_ranges++] = c1;
+                     char_ranges[n_char_ranges++] = c2;
+                     c2 = 0377;
+                   }
                  while (c <= c2)
                    {
                      fastmap[c] = 1;
                      c++;
                    }
                }
-             else
+             else if (c <= c2) /* Both C and C2 are multibyte char.  */
                {
-                 if (c_leading_code != c2_leading_code)
-                   error ("Invalid charcter range: %s",
-                          XSTRING (string)->data);
-                 fastmap[c_leading_code] = 1;
-                 if (c <= c2)
-                   {
-                     char_ranges[n_char_ranges++] = c;
-                     char_ranges[n_char_ranges++] = c2;
-                   }
+                 char_ranges[n_char_ranges++] = c;
+                 char_ranges[n_char_ranges++] = c2;
                }
            }
          else
            {
-             fastmap[c_leading_code] = 1;
-             if (!SINGLE_BYTE_CHAR_P (c))
+             if (SINGLE_BYTE_CHAR_P (c))
+               fastmap[c] = 1;
+             else
                {
                  char_ranges[n_char_ranges++] = c;
                  char_ranges[n_char_ranges++] = c;
@@ -1455,19 +1532,10 @@ skip_chars (forwardp, syntaxp, string, lim)
        }
     }
 
-  /* If ^ was the first character, complement the fastmap.  In
-     addition, as all multibyte characters have possibility of
-     matching, set all entries for base leading codes, which is
-     harmless even if SYNTAXP is 1.  */
-
+  /* If ^ was the first character, complement the fastmap.  */
   if (negate)
     for (i = 0; i < sizeof fastmap; i++)
-      {
-       if (!multibyte || !BASE_LEADING_CODE_P (i))
-         fastmap[i] ^= 1;
-       else
-         fastmap[i] = 1;
-      }
+      fastmap[i] ^= 1;
 
   {
     int start_point = PT;
@@ -1542,28 +1610,32 @@ skip_chars (forwardp, syntaxp, string, lim)
        if (forwardp)
          {
            if (multibyte)
-             while (pos < XINT (lim) && fastmap[(c = FETCH_BYTE (pos_byte))])
+             while (pos < XINT (lim))
                {
-                 if (!BASE_LEADING_CODE_P (c))
-                   INC_BOTH (pos, pos_byte);
-                 else if (n_char_ranges)
+                 c = FETCH_MULTIBYTE_CHAR (pos_byte);
+                 if (SINGLE_BYTE_CHAR_P (c))
                    {
-                     /* We much check CHAR_RANGES for a multibyte
-                        character.  */
-                     ch = FETCH_MULTIBYTE_CHAR (pos_byte);
-                     for (i = 0; i < n_char_ranges; i += 2)
-                       if ((ch >= char_ranges[i] && ch <= char_ranges[i + 1]))
-                         break;
-                     if (!(negate ^ (i < n_char_ranges)))
+                     if (!fastmap[c])
                        break;
-
-                     INC_BOTH (pos, pos_byte);
                    }
                  else
                    {
-                     if (!negate) break;
-                     INC_BOTH (pos, pos_byte);
+                     /* If we are looking at a multibyte character,
+                        we must look up the character in the table
+                        CHAR_RANGES.  If there's no data in the
+                        table, that character is not what we want to
+                        skip.  */
+
+                     /* The following code do the right thing even if
+                        n_char_ranges is zero (i.e. no data in
+                        CHAR_RANGES).  */
+                     for (i = 0; i < n_char_ranges; i += 2)
+                       if (c >= char_ranges[i] && c <= char_ranges[i + 1])
+                         break;
+                     if (!(negate ^ (i < n_char_ranges)))
+                       break;
                    }
+                 INC_BOTH (pos, pos_byte);
                }
            else
              while (pos < XINT (lim) && fastmap[FETCH_BYTE (pos)])
@@ -1574,41 +1646,26 @@ skip_chars (forwardp, syntaxp, string, lim)
            if (multibyte)
              while (pos > XINT (lim))
                {
-                 int savepos = pos_byte;
-                 DEC_BOTH (pos, pos_byte);
-                 if (fastmap[(c = FETCH_BYTE (pos_byte))])
+                 int prev_pos_byte = pos_byte;
+
+                 DEC_POS (prev_pos_byte);
+                 c = FETCH_MULTIBYTE_CHAR (prev_pos_byte);
+                 if (SINGLE_BYTE_CHAR_P (c))
                    {
-                     if (!BASE_LEADING_CODE_P (c))
-                       ;
-                     else if (n_char_ranges)
-                       {
-                         /* We much check CHAR_RANGES for a multibyte
-                            character.  */
-                         ch = FETCH_MULTIBYTE_CHAR (pos_byte);
-                         for (i = 0; i < n_char_ranges; i += 2)
-                           if (ch >= char_ranges[i] && ch <= char_ranges[i + 1])
-                             break;
-                         if (!(negate ^ (i < n_char_ranges)))
-                           {
-                             pos++;
-                             pos_byte = savepos;
-                             break;
-                           }
-                       }
-                     else
-                       if (!negate)
-                         {
-                           pos++;
-                           pos_byte = savepos;
-                           break;
-                         }
+                     if (!fastmap[c])
+                       break;
                    }
                  else
                    {
-                     pos++;
-                     pos_byte = savepos;
-                     break;
+                     /* See the comment in the previous similar code.  */
+                     for (i = 0; i < n_char_ranges; i += 2)
+                       if (c >= char_ranges[i] && c <= char_ranges[i + 1])
+                         break;
+                     if (!(negate ^ (i < n_char_ranges)))
+                       break;
                    }
+                 pos--;
+                 pos_byte = prev_pos_byte;
                }
            else
              while (pos > XINT (lim) && fastmap[FETCH_BYTE (pos - 1)])
@@ -1690,7 +1747,8 @@ forw_comment (from, from_byte, stop, nesting, style, prev_syntax,
       code = syntax & 0xff;
       if (code == Sendcomment
          && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style
-         && --nesting <= 0)
+         && (SYNTAX_FLAGS_COMMENT_NESTED (syntax) ?
+             (nesting > 0 && --nesting == 0) : nesting < 0))
        /* we have encountered a comment end of the same style
           as the comment sequence which began this comment
           section */
@@ -1703,6 +1761,7 @@ forw_comment (from, from_byte, stop, nesting, style, prev_syntax,
        break;
       if (nesting > 0
          && code == Scomment
+         && SYNTAX_FLAGS_COMMENT_NESTED (syntax)
          && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style)
        /* we have encountered a nested comment of the same style
           as the comment sequence which began this comment section */
@@ -1714,7 +1773,9 @@ forw_comment (from, from_byte, stop, nesting, style, prev_syntax,
       if (from < stop && SYNTAX_FLAGS_COMEND_FIRST (syntax)
          && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style
          && (c1 = FETCH_CHAR (from_byte),
-             SYNTAX_COMEND_SECOND (c1)))
+             SYNTAX_COMEND_SECOND (c1))
+         && ((SYNTAX_FLAGS_COMMENT_NESTED (syntax) ||
+              SYNTAX_COMMENT_NESTED (c1)) ? nesting > 0 : nesting < 0))
        {
          if (--nesting <= 0)
            /* we have encountered a comment end of the same style
@@ -1732,7 +1793,9 @@ forw_comment (from, from_byte, stop, nesting, style, prev_syntax,
          && SYNTAX_FLAGS_COMSTART_FIRST (syntax)
          && (c1 = FETCH_CHAR (from_byte),
              SYNTAX_COMMENT_STYLE (c1) == style
-             && SYNTAX_COMSTART_SECOND (c1)))
+             && SYNTAX_COMSTART_SECOND (c1))
+         && (SYNTAX_FLAGS_COMMENT_NESTED (syntax) ||
+             SYNTAX_COMMENT_NESTED (c1)))
        /* we have encountered a nested comment of the same style
           as the comment sequence which began this comment
           section */
@@ -1813,10 +1876,8 @@ between them, return t; otherwise return nil.")
              INC_BOTH (from, from_byte);
              UPDATE_SYNTAX_TABLE_FORWARD (from);
            }
-         /* FIXME: here we ignore 2-char endcomments while we don't
-            when going backwards.  */
        }
-      while (code == Swhitespace || code == Sendcomment);
+      while (code == Swhitespace || (code == Sendcomment && c == '\n'));
 
       if (code == Scomment_fence)
        comstyle = ST_COMMENT_STYLE;
@@ -1847,7 +1908,7 @@ between them, return t; otherwise return nil.")
     {
       while (1)
        {
-         int quoted, comstart_second;
+         int quoted;
 
          if (from <= stop)
            {
@@ -1859,18 +1920,12 @@ between them, return t; otherwise return nil.")
          DEC_BOTH (from, from_byte);
          /* char_quoted does UPDATE_SYNTAX_TABLE_BACKWARD (from).  */
          quoted = char_quoted (from, from_byte);
-         if (quoted)
-           {
-             DEC_BOTH (from, from_byte);
-             goto leave;
-           }
          c = FETCH_CHAR (from_byte);
          code = SYNTAX (c);
          comstyle = 0;
          comnested = SYNTAX_COMMENT_NESTED (c);
          if (code == Sendcomment)
            comstyle = SYNTAX_COMMENT_STYLE (c);
-         comstart_second = SYNTAX_COMSTART_SECOND (c);
          if (from > stop && SYNTAX_COMEND_SECOND (c)
              && prev_char_comend_first (from, from_byte)
              && !char_quoted (from - 1, dec_bytepos (from_byte)))
@@ -1886,13 +1941,6 @@ between them, return t; otherwise return nil.")
              comstyle = SYNTAX_COMMENT_STYLE (c1);
              comnested = comnested || SYNTAX_COMMENT_NESTED (c1);
            }
-         if (from > stop && comstart_second
-             && prev_char_comstart_first (from, from_byte)
-             && !char_quoted (from - 1, dec_bytepos (from_byte)))
-           {
-             code = Scomment;
-             DEC_BOTH (from, from_byte);
-           }
 
          if (code == Scomment_fence)
            {
@@ -1926,21 +1974,29 @@ between them, return t; otherwise return nil.")
                                    &out_charpos, &out_bytepos);
              if (found == -1)
                {
-#if 0  /* cc-mode (and maybe others) relies on the bogus behavior.  */
-                 /* Failure: we should go back to the end of this
-                    not-quite-endcomment.  */
-                 if (SYNTAX(c) != code)
-                   /* It was a two-char Sendcomment.  */
-                   INC_BOTH (from, from_byte);
-                 goto leave;
-#endif
+                 if (c == '\n')
+                   /* This end-of-line is not an end-of-comment.
+                      Treat it like a whitespace.
+                      CC-mode (and maybe others) relies on this behavior.  */
+                   ;
+                 else
+                   {
+                     /* Failure: we should go back to the end of this
+                        not-quite-endcomment.  */
+                     if (SYNTAX(c) != code)
+                       /* It was a two-char Sendcomment.  */
+                       INC_BOTH (from, from_byte);
+                     goto leave;
+                   }
                }
              else
-               /* We have skipped one comment.  */
-               from = out_charpos, from_byte = out_bytepos;
-             break;
+               {
+                 /* We have skipped one comment.  */
+                 from = out_charpos, from_byte = out_bytepos;
+                 break;
+               }
            }
-         else if (code != Swhitespace && code != Scomment)
+         else if (code != Swhitespace || quoted)
            {
            leave:
              immediate_quit = 0;
@@ -1959,7 +2015,7 @@ between them, return t; otherwise return nil.")
 }
 \f
 /* Return syntax code of character C if C is a single byte character
-   or `multibyte_symbol_p' is zero.  Otherwise, retrun Ssymbol.  */
+   or `multibyte_symbol_p' is zero.  Otherwise, return Ssymbol.  */
 
 #define SYNTAX_WITH_MULTIBYTE_CHECK(c)                 \
   ((SINGLE_BYTE_CHAR_P (c) || !multibyte_symbol_p)     \
@@ -2128,8 +2184,9 @@ scan_lists (from, count, depth, sexpflag)
                  if (from >= stop) goto lose;
                  UPDATE_SYNTAX_TABLE_FORWARD (from);
                  c = FETCH_CHAR (from_byte);
-                 if (code == Sstring 
-                     ? c == stringterm
+                 if (code == Sstring
+                     ? (c == stringterm
+                        && SYNTAX_WITH_MULTIBYTE_CHECK (c) == Sstring)
                      : SYNTAX_WITH_MULTIBYTE_CHECK (c) == Sstring_fence)
                    break;
 
@@ -2306,7 +2363,8 @@ scan_lists (from, count, depth, sexpflag)
                    temp_pos--;
                  UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
                  if (!char_quoted (from - 1, temp_pos)
-                     && stringterm == FETCH_CHAR (temp_pos))
+                     && stringterm == (c = FETCH_CHAR (temp_pos))
+                     && SYNTAX_WITH_MULTIBYTE_CHECK (c) == Sstring)
                    break;
                  DEC_BOTH (from, from_byte);
                }
@@ -2879,7 +2937,7 @@ init_syntax_once ()
   Qchar_table_extra_slots = intern ("char-table-extra-slots");
 
   /* Create objects which can be shared among syntax tables.  */
-  Vsyntax_code_object = Fmake_vector (make_number (13), Qnil);
+  Vsyntax_code_object = Fmake_vector (make_number (Smax), Qnil);
   for (i = 0; i < XVECTOR (Vsyntax_code_object)->size; i++)
     XVECTOR (Vsyntax_code_object)->contents[i]
       = Fcons (make_number (i), Qnil);
@@ -2959,11 +3017,10 @@ syms_of_syntax ()
     "Non-nil means `forward-sexp', etc., should treat comments as whitespace.");
 
   DEFVAR_BOOL ("parse-sexp-lookup-properties", &parse_sexp_lookup_properties,
-    "Non-nil means `forward-sexp', etc., grant `syntax-table' property.\n\
-The value of this property should be either a syntax table, or a cons\n\
-of the form (SYNTAXCODE . MATCHCHAR), SYNTAXCODE being the numeric\n\
-syntax code, MATCHCHAR being nil or the character to match (which is\n\
-relevant only for open/close type.");
+    "Non-nil means `forward-sexp', etc., obey `syntax-table' property.\n\
+Otherwise, that text property is simply ignored.\n\
+See the info node `(elisp)Syntax Properties' for a description of the\n\
+`syntax-table' property.");
 
   words_include_escapes = 0;
   DEFVAR_BOOL ("words-include-escapes", &words_include_escapes,
@@ -2985,6 +3042,7 @@ relevant only for open/close type.");
   defsubr (&Sset_syntax_table);
   defsubr (&Schar_syntax);
   defsubr (&Smatching_paren);
+  defsubr (&Sstring_to_syntax);
   defsubr (&Smodify_syntax_entry);
   defsubr (&Sdescribe_syntax);