+ return syntax_table;
+}
+\f
+/* The current syntax state */
+struct syntax_cache syntax_cache;
+
+
+/*
+ Update syntax_cache to an appropriate setting for position POS
+
+ The sign of COUNT gives the relative position of POS wrt the
+ previously valid interval. (not currently used)
+
+ `syntax_cache.*_change' are the next and previous positions at
+ which syntax_code and c_s_t will need to be recalculated.
+
+ #### Currently this code uses 'get-char-property', which will
+ return the "last smallest" extent at a given position. In cases
+ where overlapping extents are defined, this code will simply use
+ whatever is returned by get-char-property.
+
+ It might be worth it at some point to merge provided syntax tables
+ outward to the current buffer.
+
+ sjt sez:
+ This implementation has to rather inefficient, since it looks at
+ next-extent-change, and a heavily font-locked buffer will be rife
+ with irrelevant extents. We could do a sledgehammer check on this
+ by looking at the distribution of extent lengths. Also count up
+ cache hits and misses.
+
+ If we assume that syntax-table is a _text_ property (which also
+ deals with the issue of overlapping syntax-table properties), then
+ the following strategy recommends itself
+ o give the syntax cache a `valid' flag, to be reset whenever a
+ syntax-table property is added, changed, or removed; this could
+ be done by setting syntax_cache's prev_change > next_change
+ (but not compatible with using extents/markers here); if it's a
+ Lisp variable, doing it in Lisp shouldn't be too inefficient
+ o lazily initialize the cache whenever the object being examined
+ differs from the object the cache currently refers to
+ o by using {previous,next-single-property-change} we should be
+ able to get much bigger cache intervals (in most cases, the
+ whole buffer)
+ o cache markers instead of positions so the mere insertion or
+ deletion of text doesn't invalidate the cache, only if it
+ involves a syntax-table property (we could also cache the
+ extents carrying the syntax-table text-property; that gives us
+ another check for invalid cache).
+
+ If I understand this correctly, we need to invalidate the cache in the
+ following cases:
+ o If the referenced object changes (it's a global cache)
+ o If there are insertions or deletions of text (the positions are
+ absolute; fix: use markers or an extent instead?)
+ o If the syntax-table property is altered == added and different or
+ removed and the same (fix: probably computable from range overlap,
+ but is it worth it? would interact with ins/del); this includes
+ detachment of extents with the same value (but only the boundary
+ extents, as otherwise the range coalesces across the deletion point)
+ and attachment of extents with a different value
+ Note: the above looks a lot like what Ben has implemented in 21.5, but
+ he goes one better by making the cache buffer-local.
+
+ Note: cperl mode uses the text property API, not extents/overlays.
+*/
+
+#ifdef SYNTAX_CACHE_STATISTICS
+struct syntax_cache_statistics scs_statistics =
+ { 0, 0, 0, 0, -1, -1, 0.0, 0.0, scs_no_function};
+
+char* syntax_cache_statistics_function_names[scs_number_of_functions] = {
+ "find_context",
+ "find_defun_start",
+ "scan_words",
+ "Fforward_comment",
+ "scan_lists",
+ "Fbackward_prefix_characters",
+ "scan_sexps_forward"
+};
+#endif /* SYNTAX_CACHE_STATISTICS */
+
+void
+update_syntax_cache (int pos, int count)
+{
+ Lisp_Object tmp_table;
+
+#ifdef SYNTAX_CACHE_STATISTICS
+ if (scs_statistics.total_updates == 0)
+ {
+ int i;
+ for (i = 0; i < scs_number_of_functions; ++i)
+ scs_statistics.functions[i] = 0;
+ }
+ if (syntax_cache.prev_change > syntax_cache.next_change)
+ scs_statistics.inits++;
+ else if (pos < syntax_cache.prev_change)
+ scs_statistics.misses_lo++;
+ else if (pos >= syntax_cache.next_change)
+ scs_statistics.misses_hi++;
+#endif /* SYNTAX_CACHE_STATISTICS */
+
+ /* #### Since font-lock undoes any narrowing, maybe the BUF_ZV and
+ BUF_BEGV below should be BUF_Z and BUF_BEG respectively? */
+ if (BUFFERP (syntax_cache.object))
+ {
+ int get_change_before = pos + 1;
+
+ tmp_table = Fget_char_property (make_int(pos), Qsyntax_table,
+ syntax_cache.object, Qnil);
+#if NEXT_SINGLE_PROPERTY_CHANGE
+ /* #### shouldn't we be using BUF_BEGV here? */
+ syntax_cache.next_change =
+ XINT (Fnext_single_property_change
+ (make_int (pos > 0 ? pos : 1), Qsyntax_table,
+ syntax_cache.object, make_int (BUF_ZV (syntax_cache.buffer))));
+#else
+ syntax_cache.next_change =
+ XINT (Fnext_extent_change (make_int (pos > 0 ? pos : 1),
+ syntax_cache.object));
+#endif
+
+ /* #### shouldn't we be using BUF_BEGV here? */
+ if (get_change_before < 1)
+ get_change_before = 1;
+ else if (get_change_before > BUF_ZV (syntax_cache.buffer))
+ get_change_before = BUF_ZV (syntax_cache.buffer);
+
+#if PREVIOUS_SINGLE_PROPERTY_CHANGE
+ /* #### shouldn't we be using BUF_BEGV here? */
+ syntax_cache.prev_change =
+ XINT (Fprevious_single_property_change
+ (make_int (get_change_before), Qsyntax_table,
+ syntax_cache.object, make_int(1)));
+#else
+ syntax_cache.prev_change =
+ XINT (Fprevious_extent_change (make_int (get_change_before),
+ syntax_cache.object));
+#endif
+ }
+ else if (STRINGP (syntax_cache.object))
+ {
+ int get_change_before = pos + 1;
+
+ tmp_table = Fget_char_property (make_int(pos), Qsyntax_table,
+ syntax_cache.object, Qnil);
+#if NEXT_SINGLE_PROPERTY_CHANGE
+ /* #### shouldn't we be using BUF_BEGV here? */
+ syntax_cache.next_change =
+ XINT (Fnext_single_property_change
+ (make_int (pos >= 0 ? pos : 0), Qsyntax_table,
+ syntax_cache.object,
+ make_int(XSTRING_LENGTH(syntax_cache.object))));
+#else
+ syntax_cache.next_change =
+ XINT (Fnext_extent_change (make_int (pos >= 0 ? pos : 0),
+ syntax_cache.object));
+#endif
+
+ if (get_change_before < 0)
+ get_change_before = 0;
+ else if (get_change_before > XSTRING_LENGTH(syntax_cache.object))
+ get_change_before = XSTRING_LENGTH(syntax_cache.object);
+
+#if PREVIOUS_SINGLE_PROPERTY_CHANGE
+ syntax_cache.prev_change =
+ XINT (Fprevious_single_property_change
+ (make_int (get_change_before), Qsyntax_table,
+ syntax_cache.object, make_int(0)));
+#else
+ syntax_cache.prev_change =
+ XINT (Fprevious_extent_change (make_int (get_change_before),
+ syntax_cache.object));
+#endif
+ }
+ else
+ {
+ tmp_table = Qnil; /* silence compiler */
+ /* Always ABORTs. #### Is there another sensible thing to do here? */
+ assert (BUFFERP (syntax_cache.object) || STRINGP (syntax_cache.object));
+ }
+
+ if (EQ (Fsyntax_table_p (tmp_table), Qt))
+ {
+ syntax_cache.use_code = 0;
+#ifdef UTF2000
+ syntax_cache.current_syntax_table = tmp_table;
+#else
+ syntax_cache.current_syntax_table =
+ XCHAR_TABLE (tmp_table)->mirror_table;
+#endif
+ }
+ else if (CONSP (tmp_table) && INTP (XCAR (tmp_table)))
+ {
+ syntax_cache.use_code = 1;
+ syntax_cache.syntax_code = (enum syntaxcode) XINT (XCAR (tmp_table));
+ }
+ else
+ {
+ syntax_cache.use_code = 0;
+#ifdef UTF2000
+ syntax_cache.current_syntax_table =
+ syntax_cache.buffer->syntax_table;
+#else
+ syntax_cache.current_syntax_table =
+ syntax_cache.buffer->mirror_syntax_table;
+#endif
+ }
+
+#ifdef SYNTAX_CACHE_STATISTICS
+ {
+ int length = syntax_cache.next_change - syntax_cache.prev_change;
+ int misses = scs_statistics.misses_lo +
+ scs_statistics.misses_hi + scs_statistics.inits;
+
+ if (scs_statistics.min_length == -1 || scs_statistics.min_length > length)
+ scs_statistics.min_length = length;
+ if (scs_statistics.max_length == -1 || scs_statistics.max_length < length)
+ scs_statistics.max_length = length;
+ scs_statistics.mean_length_on_miss =
+ ((misses - 1) * scs_statistics.mean_length_on_miss + length) / misses;
+ }
+
+ scs_statistics.mean_length
+ = scs_statistics.total_updates*scs_statistics.mean_length
+ + syntax_cache.next_change - syntax_cache.prev_change;
+ scs_statistics.total_updates++;
+ scs_statistics.mean_length /= scs_statistics.total_updates;
+
+ if (scs_statistics.this_function != scs_no_function)
+ {
+ scs_statistics.functions[scs_statistics.this_function]++;
+ scs_statistics.this_function = scs_no_function;
+ }
+
+ if (!(scs_statistics.total_updates % SYNTAX_CACHE_STATISTICS_REPORT_INTERVAL))
+ {
+ fprintf (stderr, "Syntax cache stats:\n ");
+ fprintf (stderr, "updates %d, inits %d, misses low %d, misses high %d,",
+ scs_statistics.total_updates, scs_statistics.inits,
+ scs_statistics.misses_lo, scs_statistics.misses_hi);
+ fprintf (stderr, "\n ");
+
+#define REPORT_FUNCTION(i) \
+ fprintf (stderr, " %s %d,", \
+ syntax_cache_statistics_function_names[i], \
+ scs_statistics.functions[i]);
+
+ REPORT_FUNCTION(scs_find_context);
+ REPORT_FUNCTION(scs_find_defun_start);
+ REPORT_FUNCTION(scs_scan_words);
+ REPORT_FUNCTION(scs_Fforward_comment);
+ fprintf (stderr, "\n ");
+ REPORT_FUNCTION(scs_scan_lists);
+ REPORT_FUNCTION(scs_Fbackward_prefix_characters);
+ REPORT_FUNCTION(scs_scan_sexps_forward);
+#undef REPORT_FUNCTION
+
+ fprintf (stderr, "\n min length %d, max length %d,",
+ scs_statistics.min_length, scs_statistics.max_length);
+ fprintf (stderr, "\n mean length %.1f, mean length on miss %.1f\n",
+ scs_statistics.mean_length,
+ scs_statistics.mean_length_on_miss);
+ }
+#endif /* SYNTAX_CACHE_STATISTICS */