Logo Search packages:      
Sourcecode: jed version File versions

indent.c

/* -*- mode: C; mode: fold; -*- */
/* Copyright (c) 1992, 1998, 2000, 2002 John E. Davis
 * This file is part of JED editor library source.
 *
 * You may distribute this file under the terms the GNU General Public
 * License.  See the file COPYING for more information.
 */
#include "config.h"
#include "jed-feat.h"
/*{{{ Include Files */

#include <stdio.h>
#include <string.h>
#include "buffer.h"
#include "window.h"
#include "misc.h"
#include "cmds.h"
#include "display.h"
#include "ledit.h"
#include "screen.h"
#include "sysdep.h"
#include "search.h"
#include "paste.h"
#include "misc.h"
#include "indent.h"

/*}}}*/

Syntax_Table_Type *Default_Syntax_Table;

static Syntax_Table_Type *Syntax_Tables;

static unsigned char *find_comment_end (Syntax_Table_Type *table,
                              unsigned char *p, unsigned char *pmax)
{
   unsigned int len;
   char *str;
   unsigned char ch;
   /* unsigned char quote; */

   str = table->comment_stop;
   len = table->comment_stop_len;

   if ((str == NULL) || (len == 0))
     return NULL;

   pmax -= (len - 1);

   ch = *str;
   /* quote = table->quote_char; */

   while (p < pmax)
     {
      /* if (*p == quote) */
      /*   { */
      /*      p += 2; */
      /*      continue; */
      /*   } */
      if ((*p == ch)
          && (0 == strncmp (str, (char *)p, len)))
        return p + len;

      p++;
     }

   return NULL;
}

static unsigned char *find_string_end (Syntax_Table_Type *table,
                               unsigned char *p, unsigned char *pmax,
                               unsigned char ch)
{
   unsigned char quote;

   quote = table->quote_char;

   while (p < pmax)
     {
      if (*p == quote)
        {
           p += 2;
           continue;
        }

      if (*p++ == ch)
        return p;
     }
   
   return NULL;
}

static int is_fortran_comment (Line *l, Syntax_Table_Type *table)
{
   return l->len && table->fortran_comment_chars[l->data[0]];
}

int _jed_is_eol_comment_start (Syntax_Table_Type *table, Line *l,
                         unsigned char *p, unsigned char *pmax, unsigned int *com)
{
   unsigned int i;
   int wsc;

   if (p >= pmax)
     return 0;

   /* Most of the time, num_eol_comments is going to be <= 1, so just 
    * use table->whatever.
    */
   wsc = table->flags & EOL_COMMENT_NEEDS_WHITESPACE;

   for (i = 0; i < table->num_eol_comments; i++)
     {
      if (*p != table->eol_comment_starts[i][0])
        continue;
      
      if (p + table->eol_comment_lens[i] > pmax)
        continue;

      if (0 == (table->flags & SYNTAX_NOT_CASE_SENSITIVE))
        {
           if (0 != strncmp ((char *) p, table->eol_comment_starts[i],
                         table->eol_comment_lens[i]))
             continue;
        }
      else
        {
           if (0 != jed_case_strncmp ((char *) p, table->eol_comment_starts[i],
                              table->eol_comment_lens[i]))
             continue;
        }
      
      if ((table->char_syntax[*p] & WORD_SYNTAX) || wsc)
        {
           unsigned char *p1;
           unsigned char ch1;

           p1 = p + table->eol_comment_lens[i];
           if (p1 < pmax)
             {
              ch1 = *p1;
              if (wsc)
                {
                   if ((ch1 != ' ') && (ch1 != '\n') && (ch1 != '\t'))
                   {
                      /* This piece of code permits: #### */
                      if ((ch1 != *p)
                        || (table->char_syntax[ch1] & WORD_SYNTAX)
                        || (p1 != p + 1))
                        continue;
                   }
                }
              else if (table->char_syntax[ch1] & WORD_SYNTAX)
                continue;
             }
           
           if (p > l->data)
             {
              ch1 = *(p-1);

              if (wsc)
                {
                   if ((ch1 != ' ') && (ch1 != '\t'))
                   {
                      /* This piece of code permits: #### */
                      if ((ch1 != *p)
                        || (table->char_syntax[ch1] & WORD_SYNTAX)
                        || (p1 != p + 1))
                        continue;
                   }
                }
              else if (table->char_syntax[ch1] & WORD_SYNTAX)
                continue;
             }
        }

      if (com != NULL) 
        *com = i;

      return 1;
     }
   
   return 0;
}

static int parse_to_point1 (Syntax_Table_Type *table,
                      Line *l,
                      unsigned char *pmax)
{
   unsigned char ch;
   int quote,  cc,  flags;
   unsigned char *p;
   unsigned int lflags;
   unsigned char com_start_char;
   char *comment_start;
   unsigned short *syntax;
   unsigned char *sc;

   flags = table->flags;
   p = l->data;

   if (flags & FORTRAN_TYPE)
     {
      if (is_fortran_comment (l, table))
        return JED_LINE_HAS_EOL_COMMENT;
     }

   sc = table->string_chars;
   cc = table->char_char;
   syntax = table->char_syntax;

   lflags = l->flags & JED_LINE_SYNTAX_BITS;

   if (lflags)
     {
      if (lflags & JED_LINE_IN_COMMENT)
        {
           if (NULL == (p = find_comment_end (table, p, pmax)))
             return JED_LINE_IN_COMMENT;
        }
      else if (lflags & JED_LINE_IN_STRING0)
        {
           if (NULL == (p = find_string_end (table, p, pmax, sc[0])))
             return JED_LINE_IN_STRING0;
        }
      else if (lflags & JED_LINE_IN_STRING1)
        {
           if (NULL == (p = find_string_end (table, p, pmax, sc[1])))
             return JED_LINE_IN_STRING1;
        }
     }

   /* Now we should be out of comments, strings, etc... */
   quote = table->quote_char;

   comment_start = table->comment_start;

   com_start_char = (comment_start == NULL) ? 0 : *comment_start;

   while (p < pmax)
     {
      ch = *p;
      if (ch == quote)
        {
           p += 2;
           continue;
        }

      /* Search for a string delimiter, char delimiter, or comment start. */
      if (syntax[ch] & (STRING_SYNTAX))
        {
           p = find_string_end (table, p + 1, pmax, ch);
           if (p == NULL)
             {
              if (sc[0] == ch)
                return JED_LINE_IN_STRING0;
              if (sc[1] == ch)
                return JED_LINE_IN_STRING1;
              return JED_LINE_IN_STRING0;
             }
           continue;
        }

      if (0 == (syntax[ch] & COMMENT_SYNTAX))
        {
           p++;
           continue;
        }

      if ((ch == com_start_char)
          && (comment_start != NULL)
          && (p + table->comment_start_len <= pmax)
          && (0 == strncmp ((char *) p, comment_start,
                        table->comment_start_len)))
        {
           p = find_comment_end (table, p + table->comment_start_len, pmax);
           if (p == NULL)
             return JED_LINE_IN_COMMENT;

           continue;
        }

      if (_jed_is_eol_comment_start (table, l, p, pmax, NULL))
        return JED_LINE_HAS_EOL_COMMENT;

      p++;
     }

   return 0;
}

static void goto_effective_eol (Syntax_Table_Type *table) /*{{{*/
{
   unsigned int flags;
   unsigned char *p, *pmax;
   unsigned short *syntax;

   flags = CLine->flags;
   if (0 == (flags & JED_LINE_HAS_EOL_COMMENT))
     {
      eol ();
      return;
     }

   if (table->flags & FORTRAN_TYPE)
     {
      Point = 0;

      if (is_fortran_comment (CLine, table))
        return;
     }

   if (0 == table->num_eol_comments)
     {
      /* ?? */
      CLine->flags &= ~JED_LINE_HAS_EOL_COMMENT;
      eol ();
      return;
     }

   /* We know that there is an EOL style comment somewhere on this line.  So,
    * just search for it.
    */
   p = CLine->data;
   pmax = CLine->data + CLine->len;
   
   syntax = table->char_syntax;

   while (p < pmax)
     {
      unsigned int i;

      if ((syntax[*p] & COMMENT_SYNTAX)
          && (_jed_is_eol_comment_start (table, CLine, p, pmax, &i)))
        {
           /* Is this really a comment?  Perhaps is occurs in a string
            * or in part of another word. Only one way to find out.
            */
           Point = (int) (p - CLine->data) + table->eol_comment_lens[i];
           if (JED_LINE_HAS_EOL_COMMENT == parse_to_point1 (table, CLine, 
                                                p + table->eol_comment_lens[i]))
             {
              Point = (int) (p - CLine->data);
              return;                /* we are there */
             }
        }
      p++;
     }

   /* ?? */
   CLine->flags &= ~JED_LINE_HAS_EOL_COMMENT;
   eol ();
}

/*}}}*/

static Syntax_Table_Type *setup_for_match (unsigned char **pp,
                                 unsigned char *chp,
                                 unsigned char *want_chp)
{
   register unsigned char ch, want_ch;
   Syntax_Table_Type *table;

#if JED_HAS_LINE_ATTRIBUTES
   /* Make sure that the line flags are ok. */
   if (CBuf->min_unparsed_line_num)
     jed_syntax_parse_buffer (0);
#endif

   *pp = CLine->data + Point;

   ch = *chp;
   if (ch == 0) ch = **pp;

   table = CBuf->syntax_table;

   want_ch = 0;
   if (table != NULL)
     want_ch = table->matching_delim [ch];

   if (want_ch == 0)
     {
      table = Default_Syntax_Table;
      if (table != NULL)
        want_ch = table->matching_delim [ch];
     }

   if (want_ch == 0)
     return NULL;

   *chp = ch;
   *want_chp = want_ch;

   return table;
}

static int is_quoted (unsigned char *pmin, unsigned char **pp, unsigned char q)
{
   unsigned char *p;
   int iq;

   iq = 0;
   p = *pp;

   while ((p >= pmin) && (*p == q))
     {
      p--;
      iq = !iq;
     }

   if (iq)
     *pp = p;

   return iq;
}

static int goto_comment_begin (Syntax_Table_Type *table)
{
   unsigned char *p, *pmin, *pmax;
   unsigned char ch;
   char *s;
   unsigned int len;

   if (NULL == (s = table->comment_start))
     return -1;

   len = table->comment_start_len;
   ch = *s;

   while (1)
     {
      pmin = CLine->data;
      p = pmin + Point;
      pmax = pmin + CLine->len;

      while (p >= pmin)
        {
           if ((*p == ch)
             && (p + len <= pmax)
             && (0 == strncmp (s, (char *)p, len))
             && (0 == parse_to_point1 (table, CLine, p)))
             {
              Point = (int) (p - pmin);
              return 0;
             }

           p--;
        }

      if (CLine->prev == NULL)
        {
           Point = 0;
           return -1;
        }
      CLine = CLine->prev; LineNum--; Point = CLine->len - 1;
     }
}

static int goto_comment_end (Syntax_Table_Type *table)
{
   unsigned char *p, *pmax;
   unsigned char ch;
   char *s;
   unsigned int len;

   if (NULL == (s = table->comment_stop))
     return -1;

   len = table->comment_stop_len;
   ch = *s;

   while (1)
     {
      p = CLine->data + Point;
      pmax = CLine->data + CLine->len;

      while (p < pmax)
        {
           if ((*p == ch)
             && (p + len <= pmax)
             && (0 == strncmp (s, (char *)p, len)))
             {
              p += len;
              Point = (int) (p - CLine->data);
              return 0;
             }
           p++;
        }

      if (CLine->next == NULL)
        {
           eol ();
           return -1;
        }
      CLine = CLine->next; LineNum++; Point = 0;
     }
}

/* Note that this routine may be called with p corresponding to Point = -1.
 * This will happen when the end quote character is at the beginning of a 
 * line.
 */
static int goto_string_begin (Syntax_Table_Type *table, 
                        unsigned char ch, unsigned char *p)
{
   unsigned char *pmin;
   unsigned char quote;

   quote = table->quote_char;
   pmin = CLine->data;

   while (1)
     {
      while (p >= pmin)
        {
           if (*p-- == ch)
             {
              if ((p >= pmin)
                  && (*p == quote)
                  && (is_quoted (pmin, &p, quote)))
                continue;

              p++;
              Point = (int) (p - pmin);
              return 0;
             }
        }
      
      if ((table->flags & SINGLE_LINE_STRINGS)
          || (CLine->prev == NULL))
        {
           Point = 0;
           return -1;
        }
      
      CLine = CLine->prev; LineNum--; Point = CLine->len - 1;
      pmin = CLine->data;
      p = pmin + Point;
     }
}

static int goto_string_end (Syntax_Table_Type *table, unsigned char ch)
{
   unsigned char *p, *pmax;
   unsigned char quote;

   quote = table->quote_char;
   while (1)
     {
      p = CLine->data + Point;
      pmax = CLine->data + CLine->len;
      while (p < pmax)
        {
           if (*p == ch)
             {
              p++;                   /* go around it */
              Point = (int) (p - CLine->data);
              return 0;
             }
           if (*p == quote) p++;
           p++;
        }

      if ((table->flags & SINGLE_LINE_STRINGS)
          || (CLine->next == NULL))
        {
           eol ();
           return -1;
        }

      CLine = CLine->next; LineNum++; Point = 0;
     }
}

/* Go backward looking for the matching ch--- not the char that matches ch.
 * Rather, ch is the matching character.
 * This routine returns:
 *   1 if found and leaves the point on the match
 *  -2 if not found but we appear to be in a comment.  In this case, the point
 *     if left at the beginning of the comment
 *  -1 Not found but we appear to be in a string.  This leaves the point at the
 *     beginning of the string.
 *   0 if not found.  The point is left where we gave up
 *   2 if went back too far
 * count is the number of lines to go back
 */

/* FIXME!!! Both the forward and backward got_match routines need to be
 * rewritten to take advantage of the parse_to_point function.
 */
static int backward_goto_match (int count, unsigned char ch) /*{{{*/
{
   unsigned char *p, *pmin, want_ch;
   unsigned short *syntax;
   int in_string, in_comment, level;
   int quote;
   Syntax_Table_Type *table;
   unsigned int this_syntax;
   unsigned char *pmax;
   unsigned char com_start_char, com_stop_char;

   if (NULL == (table = setup_for_match (&p, &ch, &want_ch)))
     return 0;

   syntax = table->char_syntax;
   quote = table->quote_char;

   level = 1;

   /* Get some context */
   in_string = 0; in_comment = 0;
   switch (parse_to_point1 (table, CLine, CLine->data + Point))
     {
      case JED_LINE_HAS_EOL_COMMENT:
      case JED_LINE_IN_COMMENT:
      in_comment = 1;
      break;

      case JED_LINE_IN_STRING0:
      in_string = (int) table->string_chars[0];
      break;
      case JED_LINE_IN_STRING1:
      in_string = (int) table->string_chars[1];
      break;
     }
   Point--;

   if (table->comment_start != NULL)
     com_start_char = (unsigned char) table->comment_start[0];
   else
     com_start_char = 0;

   if (table->comment_stop != NULL)
     com_stop_char = (unsigned char) table->comment_stop[0];
   else com_stop_char = 0;

   pmax = CLine->data + CLine->len;
   while (count)
     {
      pmin = CLine->data;
      p = pmin + Point;

      /* This loop here is where it all happens. In this loop, we are
       * either in a string, a comment, or in code.  If we are in a string
       * or comment, then that is where we started.  So, upon hitting the
       * left boundary of the string or comment, quit parsing and return.
       * If in code, then skip all comments and strings because the match
       * must be in code.
       */
      while (p >= pmin)
        {
           ch = *p--;

           if ((syntax[ch] & SYNTAX_MASK) == 0) continue;

           /* Check to see if it is quoted. */
           if ((p >= pmin) && (*p == quote))
             {
              if (is_quoted (pmin, &p, quote))
                continue;
             }

           this_syntax = syntax[ch];

           /* Check for strings and characters since those occur quite
            * frequently.
            */
           if (this_syntax & STRING_SYNTAX)
             {
              /* a string is meaningless in a comment */
              if (in_comment) continue;
              if (in_string)
                {
                   if (in_string == (int) ch)
                   {
                      /* Match not found.  Leave point at beginning
                       * of string and return -1.
                       */
                      Point = 1 + (int) (p - pmin);
                      return -1;
                   }
                   continue;
                }

              Point = (int) (p - pmin);
              if (Point < 0)
                Point = 0;

              if (-1 == goto_string_begin (table, ch, p))
                {
                   /* Can't find it.  This should not happen except in the
                  * case of a run-away character.
                  */
                   return 0;
                }

              pmin = CLine->data;
              pmax = pmin + CLine->len;
              p = pmin + (Point - 1);
              continue;
             }

           /* Before checking for comments, check for matching
            * delimiters to give those a priority.
            */
           if (this_syntax & OPEN_DELIM_SYNTAX)
             {
              if (level == 1)
                {
                   Point = (int) (p - pmin) + 1;
                   if (ch == want_ch) return 1;
                   return 0;
                }
              level--;
              /* Drop down through because the open delimiter may also
               * be a comment character as in HTML and PASCAL.
               */
             }

           if (this_syntax & CLOSE_DELIM_SYNTAX)
             {
              level++;
              /* Drop */
             }

           /* The last thing to check is a comment. */
           if ((0 == (this_syntax & COMMENT_SYNTAX))
             || (in_string))
             continue;

           if (in_comment)
             {
              /* Just look for the character that denotes the start
               * of the comment.  In C++ this is complicated by something
               * like "*(no space)//".  If you use such a construct then
               * you lose.
               */
              p++;
              if (_jed_is_eol_comment_start (table, CLine, p, pmax, NULL))
                {
                   Point = (int) (p - pmin);
                   return -2;
                }

              if ((ch == com_start_char)
                  && (table->comment_start != NULL)
                  && (p + table->comment_start_len <= pmax)
                  && (0 == strncmp ((char *) p, table->comment_start, table->comment_start_len)))
                {
                   Point = (int) (p - pmin);
                   return -2;
                }
              p--;
              continue;
             }

           /* We are not in a comment but we may be moving into the tail
            * end of one.  I am not going to consider eol type comments
            * here because if this is coded correctly and the algorithm
            * works, then that was already handled.
            */
           if (ch != com_stop_char)
             continue;

           p++;
           if ((table->comment_stop == NULL)
             || (p + table->comment_stop_len > pmax)
             || (0 != strncmp ((char *) p, table->comment_stop, table->comment_stop_len)))
             {
              p--;
              continue;
             }

           Point = (int) (p - pmin);
           if (-1 == goto_comment_begin (table))
             return 0;

           pmin = CLine->data;
           pmax = pmin + CLine->len;
           p = pmin + (Point - 1);
        }

      /* Move to the previous line. */
      if (CLine->prev == NULL)
        {
           Point = 0;
           break;
        }
      CLine = CLine->prev; LineNum--; Point = CLine->len - 1;
      count--;
      if (table->flags & SINGLE_LINE_STRINGS)
        in_string = 0;
      goto_effective_eol (table);
      pmax = CLine->data + Point;
     }

   /* What have we learned? */

   if (Point < 0) Point = 0;
   if (count == 0)
     {
      /* In this case, we went back as far as permitted.  Nothing much can be
       * said.
       */
      Point = 0;
      return 2;
     }

   if (in_string) return -1;
   if (in_comment) return -2;

   /* If we are here, then we have a mismatch */
   return 0;
}

/*}}}*/

static int forward_goto_match (unsigned char ch) /*{{{*/
{
   unsigned char *p, *pmax, want_ch;
   unsigned short *syntax;
   int in_string, in_comment, level;
   unsigned int this_syntax;
   Syntax_Table_Type *table;
   unsigned char com_start_char;

   if (NULL == (table = setup_for_match (&p, &ch, &want_ch)))
     return 0;

   syntax = table->char_syntax;

   /* Here we go */

   in_string = 0; in_comment = 0;
   switch (parse_to_point1 (table, CLine, CLine->data + Point))
     {
      case JED_LINE_HAS_EOL_COMMENT:
      in_comment = JED_LINE_HAS_EOL_COMMENT;
      break;

      case JED_LINE_IN_COMMENT:
      in_comment = JED_LINE_IN_COMMENT;
      break;

      case JED_LINE_IN_STRING0:
      in_string = (int) table->string_chars[0];
      break;
      case JED_LINE_IN_STRING1:
      in_string = (int) table->string_chars[1];
      break;
     }

   com_start_char = 0;

   if (table->comment_start != NULL)
     com_start_char = (unsigned char) table->comment_start[0];

   level = 1;

   Point++;
   while (1)
     {
      p = CLine->data + Point;
      pmax = CLine->data + CLine->len;

      while (p < pmax)
        {
           ch = *p++;

           if ((syntax[ch] & SYNTAX_MASK) == 0) continue;

           this_syntax = syntax[ch];

           if (this_syntax & COMMENT_SYNTAX)
             {
              if (in_string) continue;

              if (in_comment == JED_LINE_IN_COMMENT)
                {
                   p--;
                   if ((table->comment_stop != NULL)
                     && (p + table->comment_stop_len <= pmax)
                     && (0 == strncmp ((char *) p, table->comment_stop, table->comment_start_len)))
                   {
                      p += table->comment_stop_len;
                      in_comment = 0;
                      continue;
                   }
                   p++;
                   continue;
                }

              /* We may have run into a comment.  If so, go around */
              if (in_comment == 0)
                {
                   p--;
                   if ((ch == com_start_char)
                     && (table->comment_start != NULL)
                     && (p + table->comment_start_len <= pmax)
                     && (0 == strncmp ((char *) p, table->comment_start, table->comment_start_len)))
                   {
                      Point = (int) (p - CLine->data);
                      if (-1 == goto_comment_end (table))
                        return 0;

                      p = CLine->data + Point;
                      pmax = CLine->data + CLine->len;
                      continue;
                   }
                   
                   if (_jed_is_eol_comment_start (table, CLine, p, pmax, NULL))
                   {
                      p = pmax;
                      continue;
                   }
                   p++;
                }
              /* drop */
             }

           if (this_syntax & STRING_SYNTAX)
             {
              /* string/char */
              if (in_comment) continue;
              if (in_string)
                {
                   if ((int) ch == in_string)
                   in_string = 0;
                }
              else
                {
                   Point = (int) (p - CLine->data);
                   if (-1 == goto_string_end (table, ch))
                   return -1;

                   p = CLine->data + Point;
                   pmax = CLine->data + CLine->len;
                }
              continue;
             }

           if (this_syntax & OPEN_DELIM_SYNTAX)
             {
              level++;
              continue;
             }

           if (this_syntax & CLOSE_DELIM_SYNTAX)
             {
              if (level == 1)
                {
                   Point = (int) (p - CLine->data) - 1;
                   if (ch == want_ch) return 1;
                   return 0;
                }
              level--;
              continue;
             }

           if (this_syntax & QUOTE_SYNTAX) p++; /* skip next char */
        }
      /* END OF MAIN LOOP: while (p < pmax) */

      if (in_comment == JED_LINE_HAS_EOL_COMMENT)
        return -2;

      /* Move to the next line. */
      while (1)
        {
           if (CLine->next == NULL)
             {
              eol ();
              if (in_string) return -1;
              if (in_comment) return -2;

              /* If we are here, then we have a mismatch */
              return 0;
             }

           CLine = CLine->next; LineNum++; Point = 0;
           if (table->flags & SINGLE_LINE_STRINGS)
             in_string = 0;

           if ((0 == (table->flags & FORTRAN_TYPE))
             || (0 == is_fortran_comment (CLine, table)))
             break;
        }
     }
}

/*}}}*/

static int find_matching_delimiter_1 (unsigned char ch, int nlines)
{
   unsigned char want_ch;
   Syntax_Table_Type *table;
   unsigned char *p;

   table = setup_for_match (&p, &ch, &want_ch);
   if (table == NULL)
     return 0;

   if (table->char_syntax[ch] & OPEN_DELIM_SYNTAX)
     return forward_goto_match (ch);
   else
     return backward_goto_match (nlines, ch);
}

static int find_matching_delimiter (int *ch)
{
   return find_matching_delimiter_1 ((unsigned char) *ch, 5000);
}

int goto_match (void) /*{{{*/
{
   if (1 != find_matching_delimiter_1 (0, LineNum))
     {
      if (!IS_MINIBUFFER) msg_error("Mismatch!!");
      return 0;
     }
   return 1;
}

/*}}}*/

static int parse_to_point (void) /*{{{*/
{
   int state;

   Syntax_Table_Type *table = CBuf->syntax_table;
   if (table == NULL) return 0;
#if JED_HAS_LINE_ATTRIBUTES
   jed_syntax_parse_buffer (0);
#endif
   state = parse_to_point1 (table, CLine, CLine->data + Point);

   if (state == JED_LINE_IN_COMMENT) return -2;
   if ((state == JED_LINE_IN_STRING0)
       || (state == JED_LINE_IN_STRING1))
     return -1;
   if (state == JED_LINE_HAS_EOL_COMMENT) return -2;
   return 0;
}

/*}}}*/

/* blink the matching fence.  This assumes that the window is ok */
void blink_match (void) /*{{{*/
{
   Line *save;
   int pnt, code, matchp;
   unsigned int l;
   char buf[600], strbuf[256];

   if (!Blink_Flag || (Repeat_Factor != NULL) || Batch) return;
   if (JWindow->trashed) update((Line *) NULL, 0, 0, 0);
   if (JWindow->trashed) return;
   pnt = Point;
   save = CLine;
   l = LineNum;

   if (Point) Point--;
   code = backward_goto_match (1000, 0);

   if (code == 0)
     {
      if ((! (CBuf->modes == WRAP_MODE)) && (!IS_MINIBUFFER))
        message("Mismatch??");
     }
   else if ((code == 1) && is_line_visible (LineNum))
     {
      point_cursor(0);
      input_pending(&Number_Ten);
      Point = pnt;
      CLine = save;
      LineNum = l;
      point_cursor(0);
      return;
     }
   else if (code == 1)
     {
      unsigned int len;

      matchp = Point;
      Point = 0;
      strcpy(buf, "Matches ");
      skip_whitespace();
      if ((matchp == Point) && prevline(&Number_One))
        {
           Point = 0;
           safe_strcat (buf, 
                    make_line_string(strbuf, sizeof(strbuf)), 
                    sizeof (buf));
           nextline(&Number_One);
           Point = 0;
        }

      safe_strcat(buf, 
                make_line_string(strbuf, sizeof (strbuf)), 
                sizeof(buf));
      
      /* Apparantly there are some who think that it is a bug to see
       * ^J in the mini-buffer.  Sigh.
       */
      len = strlen (buf);
      if (len && (buf[len - 1] == '\n'))
        buf[len - 1] = 0;

      message(buf);
     }
   Point = pnt;
   CLine = save;
   LineNum = l;
}

/*}}}*/

Syntax_Table_Type *jed_find_syntax_table (char *name, int err) /*{{{*/
{
   Syntax_Table_Type *table = Syntax_Tables;
   while (table != NULL)
     {
      if (!strcmp (table->name, name)) return table;
      table = table->next;
     }
   if (err) msg_error ("Syntax table undefined.");
   return table;
}

/*}}}*/

static void set_syntax_flags (char *name, int *flags) /*{{{*/
{
   Syntax_Table_Type *table;

   table = jed_find_syntax_table (name, 1);
   if (table == NULL) return;

   table->flags |= *flags & 0xFF;
}

/*}}}*/

static void define_syntax (int *what, char *name) /*{{{*/
{
   Syntax_Table_Type *table;
   int c2;
   unsigned int i;
   char *s1 = NULL, *s2 = NULL;
   unsigned char lut[256], *s;

   table = jed_find_syntax_table (name, 1);
   if (table == NULL) return;

   switch (*what)
     {
      case '%':
      if (SLang_pop_slstring (&s2)) break;
      if (SLang_pop_slstring (&s1)) break;
      
      table->char_syntax[(unsigned char) *s1] |= COMMENT_SYNTAX;

      if ((*s2 == 0) || (*s2 == '\n'))
        {
           SLang_free_slstring (s2);

           i = table->num_eol_comments;
           if (i == MAX_EOL_COMMENTS)
             {
              SLang_free_slstring (s1);
              return;
             }

           table->eol_comment_starts[i] = s1;
           table->eol_comment_lens[i] = strlen (s1);
           table->num_eol_comments = i + 1;
           return;
        }

      table->char_syntax[(unsigned char) *s2] |= COMMENT_SYNTAX;

      SLang_free_slstring (table->comment_start);
      SLang_free_slstring (table->comment_stop);
      table->comment_start = s1;
      table->comment_stop = s2;
      table->comment_start_len = strlen (s1);
      table->comment_stop_len = strlen (s2);
      return;

      case '\\':
      if (SLang_pop_integer (&c2)) break;
      table->char_syntax[(unsigned char) c2] |= QUOTE_SYNTAX;
      table->quote_char = (unsigned char) c2;
      break;

      case '#':
      if (SLang_pop_integer (&c2)) break;
      table->preprocess = (unsigned char) c2;
      break;

      case '\'':
      if (SLang_pop_integer (&c2)) break;
      table->char_syntax[(unsigned char) c2] |= STRING_SYNTAX;
      table->char_char = (unsigned char) c2;
      break;
      case '"':
      if (SLang_pop_integer (&c2)) break;
      if (table->num_string_chars == MAX_STRING_CHARS)
        break;
      table->char_syntax[(unsigned char) c2] |= STRING_SYNTAX;
      table->string_chars[table->num_string_chars++] = (unsigned char) c2;
      break;

      case '<':
      case '>':
      if (SLang_pop_slstring (&s1)) break;
      s2 = s1;
      while (*s2 != 0)
        {
           if (*(s2 + 1) == 0) break;
           table->char_syntax[(unsigned char) *s2] |= HTML_START_SYNTAX;
           table->char_syntax[(unsigned char) *(s2 + 1)] |= HTML_END_SYNTAX;
           s2 += 2;
        }
      s2 = NULL;
      break;

      case '(':
      case ')':
      if (SLang_pop_slstring (&s2)) break;
      if (SLang_pop_slstring (&s1)) break;

      i = strlen (s1);
      if (i != strlen (s2))
        {
           msg_error ("Delimiter set does not match.");
        }
      while (i > 0)
        {
           unsigned char ch1, ch2;
           i--;
           ch1 = (unsigned char) s1[i]; ch2 = (unsigned char) s2[i];
           table->char_syntax[ch1] |= OPEN_DELIM_SYNTAX;
           table->char_syntax[ch2] |= CLOSE_DELIM_SYNTAX;
           table->matching_delim[ch2] = ch1;
           table->matching_delim[ch1] = ch2;
        }
      break;

      case '+':
      if (SLang_pop_slstring (&s1)) break;
      for (i = 0; i < 256; i++) table->char_syntax[i] &= ~OP_SYNTAX;

      s = (unsigned char *) s1;
      while (*s)
        {
           table->char_syntax[*s] |= OP_SYNTAX;
           s++;
        }
      break;

      case '0':
      if (SLang_pop_slstring (&s1)) break;
      SLmake_lut (lut, (unsigned char *) s1, 0);

      for (i = 0; i < 256; i++)
        {
           if (lut[i]) table->char_syntax[i] |= NUMBER_SYNTAX;
           else table->char_syntax[i] &= ~NUMBER_SYNTAX;
        }
      break;

      case ',':
      if (SLang_pop_slstring (&s1)) break;
      s = (unsigned char *) s1;
      for (i = 0; i < 256; i++) table->char_syntax[i] &= ~DELIM_SYNTAX;
      while (*s)
        {
           table->char_syntax[*s] |= DELIM_SYNTAX;
           s++;
        }
      break;

      case 'w':
      if (SLang_pop_slstring (&s1)) break;
      SLmake_lut (lut, (unsigned char *) s1, 0);

      for (i = 0; i < 256; i++)
        {
           if (lut[i]) table->char_syntax[i] |= WORD_SYNTAX;
           else table->char_syntax[i] &= ~WORD_SYNTAX;
        }
      break;

      default:
      msg_error ("Bad parameter to define_syntax");
     }

   if (s1 != NULL) SLang_free_slstring (s1);
   if (s2 != NULL) SLang_free_slstring (s2);
}

/*}}}*/

static void set_fortran_comment_style (char *table_name, char *str)
{
   Syntax_Table_Type *table;
   int reverse;

   if (NULL == (table = jed_find_syntax_table (table_name, 1)))
     return;
   
   reverse = 0;
   if ((*str == '^') && (str[1] != 0))
     {
      str++;
      reverse = 1;
     }
   SLmake_lut(table->fortran_comment_chars, (unsigned char *) str, reverse);
}

static void use_syntax_table (char *s) /*{{{*/
{
   Syntax_Table_Type *table;

   if ((s == NULL) || (*s == 0))
     {
      s = "";
      table = Default_Syntax_Table;
     }
   else
     {
      table = jed_find_syntax_table (s, 1);
      if (table == NULL) return;
     }
   CBuf->syntax_table = table;

   (void) SLang_run_hooks ("use_syntax_table_hook", 1, s);
}

/*}}}*/

static void clear_syntax_table (Syntax_Table_Type *t)
{
   unsigned int i;
   
   for (i = 0; i < t->num_eol_comments; i++)
     {
      SLang_free_slstring (t->eol_comment_starts[i]);
      t->eol_comment_starts[i] = NULL;
      t->eol_comment_lens[i] = 0;
     }
   t->num_eol_comments = 0;

   SLang_free_slstring (t->comment_start); t->comment_start = NULL;
   SLang_free_slstring (t->comment_stop); t->comment_stop = NULL;

   /* I could do more here .... */
}

static Syntax_Table_Type *allocate_syntax_table (char *name)
{
   Syntax_Table_Type *table;

   if (NULL == (table = (Syntax_Table_Type *) jed_malloc0 (sizeof (Syntax_Table_Type))))
     return NULL;

   if (NULL == (name = SLang_create_slstring (name)))
     {
      SLfree ((char *) table);
      return NULL;
     }
   table->name = name;
   return table;
}


static void create_syntax_table (char *name) /*{{{*/
{
   Syntax_Table_Type *table;

   if (NULL != (table = jed_find_syntax_table (name, 0)))
     {
      clear_syntax_table (table);
      return;
     }

   if (NULL == (table = allocate_syntax_table (name)))
     return;

   table->next = Syntax_Tables;
   Syntax_Tables = table;
}


void init_syntax_tables (void) /*{{{*/
{
   unsigned short *a;
   unsigned char *m;

   Default_Syntax_Table = allocate_syntax_table ("DEFAULT");
   if (Default_Syntax_Table == NULL) return;

   a = Default_Syntax_Table->char_syntax;
   m = Default_Syntax_Table->matching_delim;

   a [(unsigned char) '['] = OPEN_DELIM_SYNTAX; m[(unsigned char) '['] = ']';
   a [(unsigned char) ']'] = CLOSE_DELIM_SYNTAX; m[(unsigned char) ']'] = '[';
   a [(unsigned char) '('] = OPEN_DELIM_SYNTAX; m[(unsigned char) '('] = ')';
   a [(unsigned char) ')'] = CLOSE_DELIM_SYNTAX; m[(unsigned char) ')'] = '(';
   a [(unsigned char) '{'] = OPEN_DELIM_SYNTAX; m[(unsigned char) '{'] = '}';
   a [(unsigned char) '}'] = CLOSE_DELIM_SYNTAX; m[(unsigned char) '}'] = '{';
}

/*}}}*/

static void define_keywords (char *name, char *kwords, int *lenp, int *tbl_nump) /*{{{*/
{
   char *kw;
   int len;
   int kwlen;
   unsigned int table_number = (unsigned int) *tbl_nump;
   Syntax_Table_Type *table = jed_find_syntax_table (name, 1);

   if (table == NULL) return;

   if (table_number >= MAX_KEYWORD_TABLES)
     {
      msg_error ("Table number too high.");
      return;
     }

   len = *lenp;

   if ((len < 1) || (len > MAX_KEYWORD_LEN))
     {
      msg_error ("Keyword length not supported.");
      return;
     }

   kwlen = strlen (kwords);
   if (kwlen % len)
     {
      msg_error ("Keyword list is improperly formed.");
      return;
     }

   len--;
   kw = table->keywords[table_number][len];
   if (kw == NULL) SLang_push_string ("");
   else
     {
      SLang_push_string (kw);
      SLang_free_slstring (kw);
     }

   table->keywords[table_number][len] = SLang_create_slstring (kwords);
}

/*}}}*/

#if JED_HAS_LINE_ATTRIBUTES
static void syntax_parse_lines (Syntax_Table_Type *table, Line *l, unsigned int num)
{
   int state;

   if (l == NULL)
     return;

   if (l->prev != NULL)
     {
      l = l->prev;
      num++;
     }

   while (1)
     {
      state = parse_to_point1 (table, l, l->data + l->len);
      if (state == JED_LINE_HAS_EOL_COMMENT)
        {
           l->flags |= state;
           state = 0;
        }
      if (((state == JED_LINE_IN_STRING0) || (state == JED_LINE_IN_STRING1))
          && (table->flags & SINGLE_LINE_STRINGS))
        state = 0;

      l = l->next;

      if (l == NULL)
        break;

      if (num == 0)
        {
           if (state == (int) (l->flags & JED_LINE_SYNTAX_BITS))
             return;
        }
      else num--;

      l->flags &= ~JED_LINE_SYNTAX_BITS;
      l->flags |= state;
     }
}

void jed_syntax_parse_buffer (int do_all)
{
   unsigned int min_line_num;
   unsigned int max_line_num;
   int n;
   int is_narrow;
   Line *l;
   Syntax_Table_Type *table;
   unsigned int undo_bit = 0;


   table = CBuf->syntax_table;
   if (table == NULL)
     {
      CBuf->min_unparsed_line_num = 0;
      CBuf->max_unparsed_line_num = 0;
      return;
     }

   min_line_num = CBuf->min_unparsed_line_num;
   max_line_num = CBuf->max_unparsed_line_num;

   if (min_line_num == 0)
     {
      if (do_all)
        return;
     }

   is_narrow = (CBuf->narrow != NULL);
   if (is_narrow)
     {
      /* yuk--- a major hack: turn off undo temporally */
      undo_bit = CBuf->flags & UNDO_ENABLED;
      CBuf->flags &= ~UNDO_ENABLED;

      jed_push_narrow ();
      jed_widen_whole_buffer (CBuf);
     }

   if (do_all)
     {
      min_line_num = 1;
      max_line_num = Max_LineNum;
     }
   n = (int) min_line_num;

   push_spot ();
   goto_line (&n);
   l = CLine;
   pop_spot ();

   syntax_parse_lines (table, l, 1 + (unsigned int) (max_line_num - min_line_num));

   if (is_narrow)
     {
      jed_pop_narrow ();
      CBuf->flags |= undo_bit;       /* hack */
     }

   CBuf->min_unparsed_line_num = CBuf->max_unparsed_line_num = 0;
}
#endif

static char *what_syntax_table (void)
{
   Syntax_Table_Type *s;

   if ((NULL == (s = CBuf->syntax_table))
       || (NULL == (s = Default_Syntax_Table)))
     return NULL;
   
   return s->name;                   /* not thread safe */
}

static SLang_Intrin_Fun_Type Intrinsics [] = 
{
   MAKE_INTRINSIC("parse_to_point", parse_to_point, INT_TYPE, 0),
   MAKE_INTRINSIC_SI("set_syntax_flags", set_syntax_flags, VOID_TYPE),
   MAKE_INTRINSIC_IS("define_syntax", define_syntax, VOID_TYPE),
   MAKE_INTRINSIC_S("use_syntax_table", use_syntax_table, VOID_TYPE),
   MAKE_INTRINSIC_0("what_syntax_table", what_syntax_table, STRING_TYPE),
   MAKE_INTRINSIC_S("create_syntax_table", create_syntax_table, VOID_TYPE),
   MAKE_INTRINSIC_4("define_keywords_n", define_keywords, VOID_TYPE, STRING_TYPE, STRING_TYPE, INT_TYPE, INT_TYPE),
   MAKE_INTRINSIC_SS("set_fortran_comment_chars", set_fortran_comment_style, SLANG_VOID_TYPE),
   MAKE_INTRINSIC_I("find_matching_delimiter", find_matching_delimiter, INT_TYPE),
   SLANG_END_INTRIN_FUN_TABLE
};

int jed_init_syntax (void)
{
#if JED_HAS_DFA_SYNTAX
   if (-1 == jed_init_dfa_syntax ())
     return -1;
#endif

   return SLadd_intrin_fun_table (Intrinsics, NULL);
}

Generated by  Doxygen 1.6.0   Back to index