From xemacs-m  Thu Jun 26 23:14:55 1997
Received: from jagor.srce.hr (hniksic@jagor.srce.hr [161.53.2.130])
	by xemacs.org (8.8.5/8.8.5) with ESMTP id XAA23916
	for <xemacs-beta@xemacs.org>; Thu, 26 Jun 1997 23:14:54 -0500 (CDT)
Received: (from hniksic@localhost)
          by jagor.srce.hr (8.8.5/8.8.4)
	  id GAA13771; Fri, 27 Jun 1997 06:14:55 +0200 (MET DST)
To: XEmacs Developers <xemacs-beta@xemacs.org>
Subject: Re: New version of Fexpand_abbrev available for alpha-testing
References: <kign2ocd718.fsf@jagor.srce.hr>
X-Attribution: Hrv
X-Face: Mie8:rOV<\c/~z{s.X4A{!?vY7{drJ([U]0O=W/<W*SMo/Mv:58:*_y~ki>xDi&N7XG
        KV^$k0m3Oe/)'e%3=$PCR&3ITUXH,cK>]bci&<qQ>Ff%x_>1`T(+M2Gg/fgndU%k*ft
        [(7._6e0n-V%|%'[c|q:;}td$#INd+;?!-V=c8Pqf}3J
From: Hrvoje Niksic <hniksic@srce.hr>
Date: 27 Jun 1997 06:14:55 +0200
In-Reply-To: Hrvoje Niksic's message of "27 Jun 1997 06:05:55 +0200"
Message-ID: <kigk9jgd6m8.fsf@jagor.srce.hr>
Lines: 216
X-Mailer: Gnus v5.4.59/XEmacs 20.3(beta9) - "Sofia"

Hrvoje Niksic <hniksic@srce.hr> writes:
[...]

Of course, I forgot the patch itself.  And one more thing: please try
it out with large abbrev tables and tell me if it causes *any* visible 
slowdown.  The code to convert old-style abbrev-table to new-style is
something like this:

(setq abbrev-table (let (x)
                     (mapatoms
                       (lambda (sym)
                         (push (cons (symbol-name sym) (symbol-value sym)) x))
                       OBARRAY)))

where OBARRAY is the variable keping your old abbrev-table.


--- src/abbrev.c.orig	Thu Jun 26 16:03:23 1997
+++ src/abbrev.c	Fri Jun 27 05:41:24 1997
@@ -73,11 +73,153 @@
 /* Hook to run before expanding any abbrev.  */
 Lisp_Object Vpre_abbrev_expand_hook, Qpre_abbrev_expand_hook;
 
+/* My new variable */
+Lisp_Object Vabbrev_table;
+
+
+/* Expands the abbreviation before point, if any. */
+
+DEFUN ("expand-abbrev", Fexpand_abbrev, 0, 0, "", /*
+Expand the abbrev before point, if any.
+Effective when explicitly called even when `abbrev-mode' is nil.
+Returns t if expansion took place.
+
+NOTE #1: Some variables are now obsolete.  For instance,
+`abbrev-start-location' and `abbrev-start-location-buffer' are now
+unnecessary because `expand-abbrev' will find abbreviations anywhere,
+by default.
+
+CAVEAT #1: For this to work properly, abbreviations containing a
+common non-word prefix must be sorted by length.  For instance, if "f
+b" is abbreviated to "fubar" and "f b x" to "foo bar", "f b x" will
+have to come first in the alist.  Then pressing `f<SPC>b<SPC>' will
+expand to "fubar", and pressing `f<SPC>b<C-q><SPC>x<SPC>' will expand
+to "foo bar".
+
+CAVEAT #2: The old abbrev variables will not work on this new routine.  */
+       ())
+{
+  /* This function can GC */
+  struct buffer *buf = current_buffer; /* This might conceivably be
+					  something else. */
+  int oldmodiff = BUF_MODIFF (buf);
+  Bufpos point;			/* position of point */
+  Bufpos abbrev_start;		/* position of abbreviation beginning */
+  Lisp_Object tail, value;
+  Charcount maxlen;
+
+  struct Lisp_String *abbrev;
+  Lisp_Object expansion;
+  Charcount abbrev_length, ind;
+  int found;
+
+  run_hook (Qpre_abbrev_expand_hook);
+  /* If the hook changes the buffer, treat that as having "done an
+     expansion".  */
+  value = (BUF_MODIFF (buf) != oldmodiff ? Qt : Qnil);
+
+  /* NOTE: we hope that `pre-abbrev-expand-hook' didn't do something
+     nasty, such as changed (or killed) the buffer.  */
+  point = BUF_PT (buf);
+  maxlen = point - BUF_BEGV (buf);
+
+  found = 0;
+  tail = Vabbrev_table;
+  for (; !NILP (tail); tail = Fcdr (tail))
+    {
+      CHECK_CONS (XCAR (tail));
+      CHECK_STRING (XCAR (XCAR (tail)));
+      abbrev = XSTRING (XCAR (XCAR (tail)));
+      abbrev_length = string_char_length (abbrev);
+      if (abbrev_length > maxlen)
+	/* This abbrev is too large -- it wouldn't fit. */
+	continue;
+      /* I think the following should be fast enough, not requiring
+         some further optimization even for a large number of abbrevs.  */
+      for (ind = abbrev_length - 1; ind >= 0; ind--)
+	{
+	  if (DOWNCASE (buf,
+			BUF_FETCH_CHAR (buf, point - (abbrev_length - ind)))
+	      != DOWNCASE (buf, string_char (abbrev, ind)))
+	    break;
+	}
+      if (ind < 0)
+	{
+	  found = 1;
+	  break;
+	}
+    } /* for */
+  if (!found)
+    return value;
+
+  /* OK, we're out of the must-be-fast part.  An abbreviation matched.
+     Now insert the expansion and make it look pretty. */
+  expansion = XCDR (XCAR (tail));
+  CHECK_STRING (expansion);
+  abbrev_start = point - abbrev_length;
+  Vlast_abbrev = Qnil; /* ### This should probably be more intelligent. */
+  /* This is like `abbrev', but with the original case information. */
+  Vlast_abbrev_text = make_string_from_buffer (buf, abbrev_start,
+					       abbrev_length);
+
+  /* Remove the abbrev */
+  buffer_delete_range (buf, abbrev_start, point, 0);
+  /* And insert the expansion. */
+  buffer_insert_lisp_string (buf, expansion);
+  point = BUF_PT (buf);
+
+  /* Now analyze the abbrev for case. */
+  {
+    int uccount = 0, lccount = 0;
+    struct Lisp_String *orig = XSTRING (Vlast_abbrev_text);
+    Emchar c;
+
+    for (ind = 0; ind < abbrev_length; ind++)
+      {
+	c = string_char (orig, ind);
+	if (UPPERCASEP (buf, c))
+	  ++uccount;
+	else if (LOWERCASEP (buf, c))
+	  ++lccount;
+      }
+    if (uccount && !lccount)
+      {
+	/* Abbrev was all caps */
+	if (!abbrev_all_caps
+	    && scan_words (buf, point, -1) > scan_words (buf, abbrev_start, 1))
+	  {
+	    Fupcase_initials_region (make_int (abbrev_start), make_int (point),
+				     make_buffer (buf));
+	  }
+	else
+	  {
+	    /* If expansion is one word, or if user says so, upcase it all. */
+	    Fupcase_region (make_int (abbrev_start), make_int (point),
+			    make_buffer (buf));
+	  }
+      }
+    else if (uccount)
+      {
+	/* Abbrev included some caps.  Cap first initial of expansion */
+	Bufpos pos = abbrev_start;
+	/* Find the initial.  */
+	while (pos < point
+	       && !WORD_SYNTAX_P (XCHAR_TABLE (buf->mirror_syntax_table),
+				  BUF_FETCH_CHAR (buf, pos)))
+	  pos++;
+	/* Change just that.  */
+	Fupcase_initials_region (make_int (pos), make_int (pos + 1),
+				 make_buffer (buf));
+      }
+  }
+  return Qt;
+}
+
 
 /* Expand the word before point, if it is an abbrev.
    Returns Qt if an expansion is done. */
 
-DEFUN ("expand-abbrev", Fexpand_abbrev, 0, 0, "", /*
+DEFUN ("expand-abbrev-old", Fexpand_abbrev_old, 0, 0, "", /*
 Expand the abbrev before point, if there is an abbrev there.
 Effective when explicitly called even when `abbrev-mode' is nil.
 Returns t if expansion took place.
@@ -227,11 +369,18 @@
 {
   defsymbol (&Qpre_abbrev_expand_hook, "pre-abbrev-expand-hook");
   DEFSUBR (Fexpand_abbrev);
+  DEFSUBR (Fexpand_abbrev_old);
 }
 
 void
 vars_of_abbrev (void)
 {
+  DEFVAR_LISP ("abbrev-table", &Vabbrev_table /*
+*Alist with each element of the form (ABBREV . EXPANSION).
+I am a very silly variable, and will go away.
+*/ );
+  Vabbrev_table = Qnil;
+
   DEFVAR_LISP ("global-abbrev-table", &Vglobal_abbrev_table /*
 The abbrev table whose abbrevs affect all buffers.
 Each buffer may also have a local abbrev table.
@@ -258,6 +407,7 @@
   Vlast_abbrev_text = Qnil;
   last_abbrev_point = 0;
 
+#if 0
   DEFVAR_LISP ("abbrev-start-location", &Vabbrev_start_location /*
 Buffer position for `expand-abbrev' to use as the start of the abbrev.
 nil means use the word before point as the abbrev.
@@ -270,6 +420,7 @@
 Trying to expand an abbrev in any other buffer clears `abbrev-start-location'.
 */ );
   Vabbrev_start_location_buffer = Qnil;
+#endif
 
   DEFVAR_BOOL ("abbrev-all-caps", &abbrev_all_caps /*
 *Set non-nil means expand multi-word abbrevs all caps if abbrev was so.


-- 
Hrvoje Niksic <hniksic@srce.hr> | Student at FER Zagreb, Croatia
--------------------------------+--------------------------------
Then...  his face does a complete change of expression.  It goes from
a "Vengeance is mine" expression, to a "What the fuck" blank look.

