XEmacs 21.2.28 "Hermes".
[chise/xemacs-chise.git.1] / src / font-lock.c
1 /* Routines to compute the current syntactic context, for font-lock mode.
2    Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
3    Copyright (C) 1995 Sun Microsystems, Inc.
4
5 This file is part of XEmacs.
6
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING.  If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 /* Synched up with: Not in FSF. */
23
24 /* This code computes the syntactic context of the current point, that is,
25    whether point is within a comment, a string, what have you.  It does
26    this by picking a point "known" to be outside of any syntactic constructs
27    and moving forward, examining the syntax of each character.
28
29    Two caches are used: one caches the last point computed, and the other
30    caches the last point at the beginning of a line.  This makes there
31    be little penalty for moving left-to-right on a line a character at a
32    time; makes starting over on a line be cheap; and makes random-accessing
33    within a line relatively cheap.
34
35    When we move to a different line farther down in the file (but within the
36    current top-level form) we simply continue computing forward.  If we move
37    backward more than a line, or move beyond the end of the current tlf, or
38    switch buffers, then we call `beginning-of-defun' and start over from
39    there.
40
41    #### We should really rewrite this to keep extents over the buffer
42    that hold the current syntactic information.  This would be a big win.
43    This way there would be no guessing or incorrect results.
44  */
45
46 #include <config.h>
47 #include "lisp.h"
48
49 #include "buffer.h"
50 #include "insdel.h"
51 #include "syntax.h"
52
53 Lisp_Object Qcomment;
54 Lisp_Object Qblock_comment;
55 Lisp_Object Qbeginning_of_defun;
56
57 enum syntactic_context
58 {
59   context_none,
60   context_string,
61   context_comment,
62   context_block_comment
63 };
64
65 enum block_comment_context
66 {
67   ccontext_none,
68   ccontext_start1,
69   ccontext_start2,
70   ccontext_end1
71 };
72
73 enum comment_style
74 {
75   comment_style_none,
76   comment_style_a,
77   comment_style_b
78 };
79
80 struct context_cache
81 {
82   Bufpos start_point;                   /* beginning of defun */
83   Bufpos cur_point;                     /* cache location */
84   Bufpos end_point;                     /* end of defun */
85   struct buffer *buffer;                /* does this need to be staticpro'd? */
86   enum syntactic_context context;       /* single-char-syntax state */
87   enum block_comment_context ccontext;  /* block-comment state */
88   enum comment_style style;             /* which comment group */
89   Emchar scontext;                      /* active string delimiter */
90   int depth;                            /* depth in parens */
91   int backslash_p;                      /* just read a backslash */
92   int needs_its_head_reexamined;        /* we're apparently outside of
93                                            a top level form, and far away
94                                            from it.  This is a bad situation
95                                            because it will lead to constant
96                                            slowness as we keep going way
97                                            back to that form and moving
98                                            forward again.  In this case,
99                                            we try to compute a "pseudo-
100                                            top-level-form" where the
101                                            depth is 0 and the context
102                                            is none at both ends. */
103 };
104
105 /* We have two caches; one for the current point and one for
106    the beginning of line.  We used to rely on the caller to
107    tell us when to invalidate them, but now we do it ourselves;
108    it lets us be smarter. */
109
110 static struct context_cache context_cache;
111
112 static struct context_cache bol_context_cache;
113
114 int font_lock_debug;
115
116 #define reset_context_cache(cc) memset (cc, 0, sizeof (struct context_cache))
117
118 /* This function is called from signal_after_change() to tell us when
119    textual changes are made so we can flush our caches when necessary.
120
121    We make the following somewhat heuristic assumptions:
122
123      (remember that current_point is always >= start_point, but may be
124      less than or greater than end_point (we might not be inside any
125      top-level form)).
126
127    1) Textual changes before the beginning of the current top-level form
128       don't affect anything; all we need to do is offset the caches
129       appropriately.
130    2) Textual changes right at the beginning of the current
131       top-level form messes things up and requires that we flush
132       the caches.
133    3) Textual changes after the beginning of the current top-level form
134       and before one or both or the caches invalidates the corresponding
135       cache(s).
136    4) Textual changes after the caches and before the end of the
137       current top-level form don't affect anything; all we need to do is
138       offset the caches appropriately.
139    5) Textual changes right at the end of the current top-level form
140       necessitate recomputing that end value.
141    6) Textual changes after the end of the current top-level form
142       are ignored. */
143
144
145 void
146 font_lock_maybe_update_syntactic_caches (struct buffer *buf, Bufpos start,
147                                          Bufpos orig_end, Bufpos new_end)
148 {
149   /* Note: either both context_cache and bol_context_cache are valid and
150      point to the same buffer, or both are invalid.  If we have to
151      invalidate just context_cache, we recopy it from bol_context_cache.
152    */
153   if (context_cache.buffer != buf)
154     /* caches don't apply */
155     return;
156   /* NOTE: The order of the if statements below is important.  If you
157      change them around unthinkingly, you will probably break something. */
158   if (orig_end <= context_cache.start_point - 1)
159     {
160       /* case 1: before the beginning of the current top-level form */
161       Charcount diff = new_end - orig_end;
162       if (font_lock_debug)
163         stderr_out ("font-lock; Case 1\n");
164       context_cache.start_point += diff;
165       context_cache.cur_point += diff;
166       context_cache.end_point += diff;
167       bol_context_cache.start_point += diff;
168         bol_context_cache.cur_point += diff;
169       bol_context_cache.end_point += diff;
170     }
171   else if (start <= context_cache.start_point)
172     {
173       if (font_lock_debug)
174         stderr_out ("font-lock; Case 2\n");
175       /* case 2: right at the current top-level form (paren that starts
176          top level form got deleted or moved away from the newline it
177          was touching) */
178       reset_context_cache (&context_cache);
179       reset_context_cache (&bol_context_cache);
180     }
181   /* OK, now we know that the start is after the beginning of the
182      current top-level form. */
183   else if (start < bol_context_cache.cur_point)
184     {
185       if (font_lock_debug)
186         stderr_out ("font-lock; Case 3 (1)\n");
187       /* case 3: after the beginning of the current top-level form
188          and before both of the caches */
189       reset_context_cache (&context_cache);
190       reset_context_cache (&bol_context_cache);
191     }
192   else if (start < context_cache.cur_point)
193     {
194       if (font_lock_debug)
195         stderr_out ("font-lock; Case 3 (2)\n");
196       /* case 3: but only need to invalidate one cache */
197       context_cache = bol_context_cache;
198     }
199   /* OK, now we know that the start is after the caches. */
200   else if (start >= context_cache.end_point)
201     {
202       if (font_lock_debug)
203         stderr_out ("font-lock; Case 6\n");
204       /* case 6: after the end of the current top-level form
205          and after the caches. */
206     }
207   else if (orig_end <= context_cache.end_point - 2)
208     {
209       /* case 4: after the caches and before the end of the
210          current top-level form */
211       Charcount diff = new_end - orig_end;
212       if (font_lock_debug)
213         stderr_out ("font-lock; Case 4\n");
214       context_cache.end_point += diff;
215       bol_context_cache.end_point += diff;
216     }
217   else
218     {
219       if (font_lock_debug)
220         stderr_out ("font-lock; Case 5\n");
221       /* case 5: right at the end of the current top-level form */
222       context_cache.end_point = context_cache.start_point - 1;
223       bol_context_cache.end_point = context_cache.start_point - 1;
224     }
225 }
226
227 /* This function is called from Fkill_buffer(). */
228
229 void
230 font_lock_buffer_was_killed (struct buffer *buf)
231 {
232   if (context_cache.buffer == buf)
233     {
234       reset_context_cache (&context_cache);
235       reset_context_cache (&bol_context_cache);
236     }
237 }
238
239 static Bufpos
240 beginning_of_defun (struct buffer *buf, Bufpos pt)
241 {
242   /* This function can GC */
243   Bufpos opt = BUF_PT (buf);
244   if (pt == BUF_BEGV (buf))
245     return pt;
246   BUF_SET_PT (buf, pt);
247   /* There used to be some kludginess to call c++-beginning-of-defun
248      if we're in C++ mode.  There's no point in this any more;
249      we're using cc-mode.  If you really want to get the old c++
250      mode working, fix it rather than the C code. */
251   call0_in_buffer (buf, Qbeginning_of_defun);
252   pt = BUF_PT (buf);
253   BUF_SET_PT (buf, opt);
254   return pt;
255 }
256
257 static Bufpos
258 end_of_defun (struct buffer *buf, Bufpos pt)
259 {
260   Lisp_Object retval = scan_lists (buf, pt, 1, 0, 0, 1);
261   if (NILP (retval))
262     return BUF_ZV (buf);
263   else
264     return XINT (retval);
265 }
266
267 /* Set up context_cache for attempting to determine the syntactic context
268    in buffer BUF at point PT. */
269
270 static void
271 setup_context_cache (struct buffer *buf, Bufpos pt)
272 {
273   int recomputed_start_point = 0;
274   /* This function can GC */
275   if (context_cache.buffer != buf || pt < context_cache.start_point)
276     {
277     start_over:
278       if (font_lock_debug)
279         stderr_out ("reset context cache\n");
280       /* OK, completely invalid. */
281       reset_context_cache (&context_cache);
282       reset_context_cache (&bol_context_cache);
283     }
284   if (!context_cache.buffer)
285     {
286       /* Need to recompute the start point. */
287       if (font_lock_debug)
288         stderr_out ("recompute start\n");
289       context_cache.start_point = beginning_of_defun (buf, pt);
290       recomputed_start_point = 1;
291       bol_context_cache.start_point = context_cache.start_point;
292       bol_context_cache.buffer = context_cache.buffer = buf;
293     }
294   if (context_cache.end_point < context_cache.start_point)
295     {
296       /* Need to recompute the end point. */
297       if (font_lock_debug)
298         stderr_out ("recompute end\n");
299       context_cache.end_point = end_of_defun (buf, context_cache.start_point);
300       bol_context_cache.end_point = context_cache.end_point;
301     }
302   if (bol_context_cache.cur_point == 0 ||
303       pt < bol_context_cache.cur_point)
304     {
305       if (font_lock_debug)
306         stderr_out ("reset to start\n");
307       if (pt > context_cache.end_point
308           /* 3000 is some arbitrary delta but seems reasonable;
309              about the size of a reasonable function */
310           && pt - context_cache.end_point > 3000)
311         /* If we're far past the end of the top level form,
312            don't trust it; recompute it. */
313         {
314           /* But don't get in an infinite loop doing this.
315              If we're really far past the end of the top level
316              form, try to compute a pseudo-top-level form. */
317           if (recomputed_start_point)
318             context_cache.needs_its_head_reexamined = 1;
319           else
320             /* force recomputation */
321             goto start_over;
322         }
323       /* Go to the nearest end of the top-level form that's before
324          us. */
325       if (pt > context_cache.end_point)
326         pt = context_cache.end_point;
327       else
328         pt = context_cache.start_point;
329       /* Reset current point to start of buffer. */
330       context_cache.cur_point = pt;
331       context_cache.context = context_none;
332       context_cache.ccontext = ccontext_none;
333       context_cache.style = comment_style_none;
334       context_cache.scontext = '\000';
335       context_cache.depth = 0;
336       context_cache.backslash_p = ((pt > 1) &&
337                                    (BUF_FETCH_CHAR (buf, pt - 1) == '\\'));
338       /* Note that the BOL context cache may not be at the beginning
339          of the line, but that should be OK, nobody's checking. */
340       bol_context_cache = context_cache;
341       return;
342     }
343   else if (pt < context_cache.cur_point)
344     {
345       if (font_lock_debug)
346         stderr_out ("reset to bol\n");
347       /* bol cache is OK but current_cache is not. */
348       context_cache = bol_context_cache;
349       return;
350     }
351   else if (pt <= context_cache.end_point)
352     {
353       if (font_lock_debug)
354         stderr_out ("everything is OK\n");
355       /* in same top-level form. */
356       return;
357     }
358   {
359     /* OK, we're past the end of the top-level form. */
360     Bufpos maxpt = max (context_cache.end_point, context_cache.cur_point);
361 #if 0
362     int shortage;
363 #endif
364
365     if (font_lock_debug)
366       stderr_out ("past end\n");
367     if (pt <= maxpt)
368       /* OK, fine. */
369       return;
370 #if 0
371     /* This appears to cause huge slowdowns in files like
372        emacsfns.h, which have no top-level forms.
373
374        In any case, it's not really necessary that we know for
375        sure the top-level form we're in; if we're in a form
376        but the form we have recorded is the previous one,
377        it will be OK. */
378
379     scan_buffer (buf, '\n', maxpt, pt, 1, &shortage, 1);
380     if (!shortage)
381       /* If there was a newline in the region past the known universe,
382          we might be inside another top-level form, so start over.
383          Otherwise, we're outside of any top-level forms and we know
384          the one directly before us, so it's OK. */
385       goto start_over;
386 #endif
387   }
388 }
389
390 #define SYNTAX_START_STYLE(table, c1, c2)                                \
391   (SYNTAX_STYLES_MATCH_START_P (table, c1, c2, SYNTAX_COMMENT_STYLE_A) ? \
392    comment_style_a :                                                     \
393    SYNTAX_STYLES_MATCH_START_P (table, c1, c2, SYNTAX_COMMENT_STYLE_B) ? \
394    comment_style_b :                                                     \
395    comment_style_none)
396
397 #define SYNTAX_END_STYLE(table, c1, c2)                                 \
398   (SYNTAX_STYLES_MATCH_END_P (table, c1, c2, SYNTAX_COMMENT_STYLE_A) ?  \
399    comment_style_a :                                                    \
400    SYNTAX_STYLES_MATCH_END_P (table, c1, c2, SYNTAX_COMMENT_STYLE_B) ?  \
401    comment_style_b :                                                    \
402    comment_style_none)
403
404 #define SINGLE_SYNTAX_STYLE(table, c)                                   \
405       (SYNTAX_STYLES_MATCH_1CHAR_P (table, c, SYNTAX_COMMENT_STYLE_A) ? \
406        comment_style_a :                                                \
407        SYNTAX_STYLES_MATCH_1CHAR_P (table, c, SYNTAX_COMMENT_STYLE_B) ? \
408        comment_style_b :                                                \
409        comment_style_none)
410
411 /* Set up context_cache for position PT in BUF. */
412
413 static void
414 find_context (struct buffer *buf, Bufpos pt)
415 {
416   /* This function can GC */
417   Lisp_Char_Table *mirrortab = XCHAR_TABLE (buf->mirror_syntax_table);
418   Lisp_Object syntaxtab = buf->syntax_table;
419   Emchar prev_c, c;
420   Bufpos target = pt;
421   setup_context_cache (buf, pt);
422   pt = context_cache.cur_point;
423
424   if (pt > BUF_BEGV (buf))
425     c = BUF_FETCH_CHAR (buf, pt - 1);
426   else
427     c = '\n'; /* to get bol_context_cache at point-min */
428
429   for (; pt < target; pt++, context_cache.cur_point = pt)
430     {
431       if (context_cache.needs_its_head_reexamined)
432         {
433           if (context_cache.depth == 0
434               && context_cache.context == context_none)
435             {
436               /* We've found an anchor spot.
437                  Try to put the start of defun within 6000 chars of
438                  the target, and the end of defun as close as possible.
439                  6000 is also arbitrary but tries to strike a balance
440                  between two conflicting pulls when dealing with a
441                  file that has lots of stuff sitting outside of a top-
442                  level form:
443
444                  a) If you move past the start of defun, you will
445                     have to recompute defun, which in this case
446                     means that start of defun goes all the way back
447                     to the beginning of the file; so you want
448                     to set start of defun a ways back from the
449                     current point.
450                  b) If you move a line backwards but within start of
451                     defun, you have to move back to start of defun;
452                     so you don't want start of defun too far from
453                     the current point.
454                  */
455               if (target - context_cache.start_point > 6000)
456                 context_cache.start_point = pt;
457               context_cache.end_point = pt;
458               bol_context_cache = context_cache;
459             }
460         }
461
462       prev_c = c;
463       c = BUF_FETCH_CHAR (buf, pt);
464
465       if (prev_c == '\n')
466         bol_context_cache = context_cache;
467
468       if (context_cache.backslash_p)
469         {
470           context_cache.backslash_p = 0;
471           continue;
472         }
473
474       switch (SYNTAX (mirrortab, c))
475         {
476         case Sescape:
477           context_cache.backslash_p = 1;
478           break;
479
480         case Sopen:
481           if (context_cache.context == context_none)
482             context_cache.depth++;
483           break;
484
485         case Sclose:
486           if (context_cache.context == context_none)
487             context_cache.depth--;
488           break;
489
490         case Scomment:
491           if (context_cache.context == context_none)
492             {
493               context_cache.context = context_comment;
494               context_cache.ccontext = ccontext_none;
495               context_cache.style = SINGLE_SYNTAX_STYLE (mirrortab, c);
496               if (context_cache.style == comment_style_none) abort ();
497             }
498           break;
499
500         case Sendcomment:
501           if (context_cache.style != SINGLE_SYNTAX_STYLE (mirrortab, c))
502             ;
503           else if (context_cache.context == context_comment)
504             {
505               context_cache.context = context_none;
506               context_cache.style = comment_style_none;
507             }
508           else if (context_cache.context == context_block_comment &&
509                    (context_cache.ccontext == ccontext_start2 ||
510                     context_cache.ccontext == ccontext_end1))
511             {
512               context_cache.context = context_none;
513               context_cache.ccontext = ccontext_none;
514               context_cache.style = comment_style_none;
515             }
516           break;
517
518         case Sstring:
519           {
520             if (context_cache.context == context_string &&
521                 context_cache.scontext == c)
522               {
523                 context_cache.context = context_none;
524                 context_cache.scontext = '\000';
525               }
526             else if (context_cache.context == context_none)
527               {
528                 Lisp_Object stringtermobj = syntax_match (syntaxtab, c);
529                 Emchar stringterm;
530
531                 if (CHARP (stringtermobj))
532                   stringterm = XCHAR (stringtermobj);
533                 else
534                   stringterm = c;
535                 context_cache.context = context_string;
536                 context_cache.scontext = stringterm;
537                 context_cache.ccontext = ccontext_none;
538               }
539             break;
540           }
541         default:
542           ;
543         }
544
545       /* That takes care of the characters with manifest syntax.
546          Now we've got to hack multi-char sequences that start
547          and end block comments.
548        */
549       if ((SYNTAX_COMMENT_BITS (mirrortab, c) &
550            SYNTAX_SECOND_CHAR_START) &&
551           context_cache.context == context_none &&
552           context_cache.ccontext == ccontext_start1 &&
553           SYNTAX_START_P (mirrortab, prev_c, c) /* the two chars match */
554           )
555         {
556           context_cache.ccontext = ccontext_start2;
557           context_cache.style = SYNTAX_START_STYLE (mirrortab, prev_c, c);
558           if (context_cache.style == comment_style_none) abort ();
559         }
560       else if ((SYNTAX_COMMENT_BITS (mirrortab, c) &
561                 SYNTAX_FIRST_CHAR_START) &&
562                context_cache.context == context_none &&
563                (context_cache.ccontext == ccontext_none ||
564                 context_cache.ccontext == ccontext_start1))
565         {
566           context_cache.ccontext = ccontext_start1;
567           context_cache.style = comment_style_none; /* should be this already*/
568         }
569       else if ((SYNTAX_COMMENT_BITS (mirrortab, c) &
570                 SYNTAX_SECOND_CHAR_END) &&
571                context_cache.context == context_block_comment &&
572                context_cache.ccontext == ccontext_end1 &&
573                SYNTAX_END_P (mirrortab, prev_c, c) &&
574                /* the two chars match */
575                context_cache.style ==
576                SYNTAX_END_STYLE (mirrortab, prev_c, c)
577                )
578         {
579           context_cache.context = context_none;
580           context_cache.ccontext = ccontext_none;
581           context_cache.style = comment_style_none;
582         }
583       else if ((SYNTAX_COMMENT_BITS (mirrortab, c) &
584                 SYNTAX_FIRST_CHAR_END) &&
585                context_cache.context == context_block_comment &&
586                (context_cache.style ==
587                 SYNTAX_END_STYLE (mirrortab, c,
588                                   BUF_FETCH_CHAR (buf, pt+1))) &&
589                (context_cache.ccontext == ccontext_start2 ||
590                 context_cache.ccontext == ccontext_end1))
591         /* #### is it right to check for end1 here?? */
592         {
593           if (context_cache.style == comment_style_none) abort ();
594           context_cache.ccontext = ccontext_end1;
595         }
596
597       else if (context_cache.ccontext == ccontext_start1)
598         {
599           if (context_cache.context != context_none) abort ();
600           context_cache.ccontext = ccontext_none;
601         }
602       else if (context_cache.ccontext == ccontext_end1)
603         {
604           if (context_cache.context != context_block_comment) abort ();
605           context_cache.context = context_none;
606           context_cache.ccontext = ccontext_start2;
607         }
608
609       if (context_cache.ccontext == ccontext_start2 &&
610           context_cache.context == context_none)
611         {
612           context_cache.context = context_block_comment;
613           if (context_cache.style == comment_style_none) abort ();
614         }
615       else if (context_cache.ccontext == ccontext_none &&
616                context_cache.context == context_block_comment)
617         {
618           context_cache.context = context_none;
619         }
620     }
621
622   context_cache.needs_its_head_reexamined = 0;
623 }
624
625 static Lisp_Object
626 context_to_symbol (enum syntactic_context context)
627 {
628   switch (context)
629     {
630     case context_none:          return Qnil;
631     case context_string:        return Qstring;
632     case context_comment:       return Qcomment;
633     case context_block_comment: return Qblock_comment;
634     default: abort (); return Qnil; /* suppress compiler warning */
635     }
636 }
637
638 DEFUN ("buffer-syntactic-context", Fbuffer_syntactic_context, 0, 1, 0, /*
639 Return the syntactic context of BUFFER at point.
640 If BUFFER is nil or omitted, the current buffer is assumed.
641 The returned value is one of the following symbols:
642
643         nil             ; meaning no special interpretation
644         string          ; meaning point is within a string
645         comment         ; meaning point is within a line comment
646         block-comment   ; meaning point is within a block comment
647
648 See also the function `buffer-syntactic-context-depth', which returns
649 the current nesting-depth within all parenthesis-syntax delimiters
650 and the function `syntactically-sectionize', which will map a function
651 over each syntactic context in a region.
652
653 WARNING: this may alter match-data.
654 */
655        (buffer))
656 {
657   /* This function can GC */
658   struct buffer *buf = decode_buffer (buffer, 0);
659   find_context (buf, BUF_PT (buf));
660   return context_to_symbol (context_cache.context);
661 }
662
663 DEFUN ("buffer-syntactic-context-depth", Fbuffer_syntactic_context_depth,
664        0, 1, 0, /*
665 Return the depth within all parenthesis-syntax delimiters at point.
666 If BUFFER is nil or omitted, the current buffer is assumed.
667 WARNING: this may alter match-data.
668 */
669        (buffer))
670 {
671   /* This function can GC */
672   struct buffer *buf = decode_buffer (buffer, 0);
673   find_context (buf, BUF_PT (buf));
674   return make_int (context_cache.depth);
675 }
676
677
678 DEFUN ("syntactically-sectionize", Fsyntactically_sectionize, 3, 4, 0, /*
679 Call FUNCTION for each contiguous syntactic context in the region.
680 Call the given function with four arguments: the start and end of the
681 region, a symbol representing the syntactic context, and the current
682 depth (as returned by the functions `buffer-syntactic-context' and
683 `buffer-syntactic-context-depth').  When this function is called, the
684 current buffer will be set to BUFFER.
685
686 WARNING: this may alter match-data.
687 */
688        (function, start, end, buffer))
689 {
690   /* This function can GC */
691   Bufpos s, pt, e;
692   int edepth;
693   enum syntactic_context this_context;
694   Lisp_Object extent = Qnil;
695   struct gcpro gcpro1;
696   struct buffer *buf = decode_buffer (buffer, 0);
697
698   get_buffer_range_char (buf, start, end, &s, &e, 0);
699
700   pt = s;
701   find_context (buf, pt);
702
703   GCPRO1 (extent);
704   while (pt < e)
705     {
706       Bufpos estart, eend;
707       /* skip over "blank" areas, and bug out at end-of-buffer. */
708       while (context_cache.context == context_none)
709         {
710           pt++;
711           if (pt >= e) goto DONE_LABEL;
712           find_context (buf, pt);
713         }
714       /* We've found a non-blank area; keep going until we reach its end */
715       this_context = context_cache.context;
716       estart = pt;
717
718       /* Minor kludge: consider the comment-start character(s) a part of
719          the comment.
720        */
721       if (this_context == context_block_comment &&
722           context_cache.ccontext == ccontext_start2)
723         estart -= 2;
724       else if (this_context == context_comment)
725         estart -= 1;
726
727       edepth = context_cache.depth;
728       while (context_cache.context == this_context && pt < e)
729         {
730           pt++;
731           find_context (buf, pt);
732         }
733
734       eend = pt;
735
736       /* Minor kludge: consider the character which terminated the comment
737          a part of the comment.
738        */
739       if ((this_context == context_block_comment ||
740            this_context == context_comment)
741           && pt < e)
742         eend++;
743
744       if (estart == eend)
745         continue;
746       /* Make sure not to pass in values that are outside the
747          actual bounds of this function. */
748       call4_in_buffer (buf, function, make_int (max (s, estart)),
749                        make_int (eend == e ? e : eend - 1),
750                        context_to_symbol (this_context),
751                        make_int (edepth));
752     }
753  DONE_LABEL:
754   UNGCPRO;
755   return Qnil;
756 }
757
758 void
759 syms_of_font_lock (void)
760 {
761   defsymbol (&Qcomment, "comment");
762   defsymbol (&Qblock_comment, "block-comment");
763   defsymbol (&Qbeginning_of_defun, "beginning-of-defun");
764
765   DEFSUBR (Fbuffer_syntactic_context);
766   DEFSUBR (Fbuffer_syntactic_context_depth);
767   DEFSUBR (Fsyntactically_sectionize);
768 }
769
770 void
771 reinit_vars_of_font_lock (void)
772 {
773   xzero (context_cache);
774   xzero (bol_context_cache);
775 }
776
777 void
778 vars_of_font_lock (void)
779 {
780   reinit_vars_of_font_lock ();
781 }