Mercurial > hg > xemacs-beta
diff src/abbrev.c @ 167:85ec50267440 r20-3b10
Import from CVS: tag r20-3b10
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:45:46 +0200 |
parents | 131b0175ea99 |
children | 8eaf7971accc |
line wrap: on
line diff
--- a/src/abbrev.c Mon Aug 13 09:44:44 2007 +0200 +++ b/src/abbrev.c Mon Aug 13 09:45:46 2007 +0200 @@ -26,6 +26,7 @@ FSF: Original version; a long time ago. JWZ or Mly: Mostly moved into Lisp; maybe 1992. Ben Wing: Some changes for Mule for 19.12. + Hrvoje Niksic: Largely rewritten in June 1997. */ /* This file has been Mule-ized. */ @@ -70,158 +71,319 @@ /* Character address of start of last abbrev expanded */ int last_abbrev_point; +Lisp_Object oblookup (Lisp_Object, CONST Bufbyte *, Bytecount); + /* Hook to run before expanding any abbrev. */ Lisp_Object Vpre_abbrev_expand_hook, Qpre_abbrev_expand_hook; -/* Expand the word before point, if it is an abbrev. - Returns Qt if an expansion is done. */ +/* Match the buffer text against names of symbols in obarray. Returns + the matching symbol, or 0 if not found. */ + +static struct Lisp_Symbol * +abbrev_match (struct buffer *buf, Lisp_Object obarray) +{ + Bufpos point = BUF_PT (buf); + Bufpos maxlen = point - BUF_BEGV (buf); + Charcount idx; + + struct Lisp_Char_Table *chartab = XCHAR_TABLE (buf->mirror_syntax_table); + struct Lisp_String *abbrev; + struct Lisp_Vector *obvec; + struct Lisp_Symbol *sym; + Charcount abbrev_length; + Lisp_Object tail; + int i, found; + + CHECK_VECTOR (obarray); + obvec = XVECTOR (obarray); + + /* The obarray-traversing code is copied from `map_obarray'. */ + found = 0; + for (i = vector_length (obvec) - 1; i >= 0; i--) + { + tail = vector_data (obvec)[i]; + if (SYMBOLP (tail)) + while (1) + { + sym = XSYMBOL (tail); + if (UNBOUNDP (symbol_value (sym)) || NILP (symbol_value (sym))) + { + /* The symbol value of nil means that abbrev got + undefined. */ + goto next; + } + abbrev = symbol_name (sym); + abbrev_length = string_char_length (abbrev); + if (abbrev_length > maxlen) + { + /* This abbrev is too large -- it wouldn't fit. */ + goto next; + } + /* If `bar' is an abbrev, and a user presses `fubar<SPC>', + we don't normally want to expand it. OTOH, if the + abbrev begins with non-word syntax, it is OK to + abbreviate it anywhere. */ + if (abbrev_length < maxlen && abbrev_length > 0 + && (WORD_SYNTAX_P (chartab, string_char (abbrev, 0))) + && (WORD_SYNTAX_P (chartab, + BUF_FETCH_CHAR (buf, point + - (abbrev_length + 1))))) + { + goto next; + } + /* Match abbreviation string against buffer text. */ + for (idx = abbrev_length - 1; idx >= 0; idx--) + { + if (DOWNCASE (buf, BUF_FETCH_CHAR (buf, point - + (abbrev_length - idx))) + != DOWNCASE (buf, string_char (abbrev, idx))) + break; + } + if (idx < 0) + { + found = 1; + break; + } + next: + sym = symbol_next (XSYMBOL (tail)); + if (!sym) + break; + XSETSYMBOL (tail, sym); + } /* while */ + if (found) + break; + } /* for */ + + return found ? sym : 0; +} + +/* Take the word before point, and look it up in OBARRAY, and return + the symbol (or nil). This used to be the default method of + searching, with the obvious limitation that the abbrevs may consist + only of word characters. It is an order of magnitued faster than + the proper `abbrev_match', but then again, vi is an order of + magnitude faster than Emacs. */ +static struct Lisp_Symbol * +abbrev_oblookup (struct buffer *buf, Lisp_Object obarray) +{ + Bufpos wordstart, wordend; + Bufbyte *word, *p; + Bytecount idx; + Lisp_Object lookup; + + CHECK_VECTOR (obarray); + + if (!NILP (Vabbrev_start_location)) + { + wordstart = get_buffer_pos_char (buf, Vabbrev_start_location, + GB_COERCE_RANGE); + Vabbrev_start_location = Qnil; + if (wordstart != BUF_ZV (buf) + && BUF_FETCH_CHAR (buf, wordstart) == '-') + { + buffer_delete_range (buf, wordstart, wordstart + 1, 0); + } + wordend = BUF_PT (buf); + } + else + { + Bufpos point = BUF_PT (buf); + + wordstart = scan_words (buf, point, -1); + if (!wordstart) + return 0; + + wordend = scan_words (buf, wordstart, 1); + if (!wordend) + return 0; + if (wordend > BUF_ZV (buf)) + wordend = BUF_ZV (buf); + if (wordend > point) + wordend = point; + /* Unlike the original function, we allow expansion only after + the abbrev, not preceded by a number of spaces. This is + because of consistency with abbrev_match. */ + if (wordend < point) + return 0; + if (wordend <= wordstart) + return 0; + } + + p = word = (Bufbyte *) alloca (MAX_EMCHAR_LEN * (wordend - wordstart)); + for (idx = wordstart; idx < wordend; idx++) + { + Emchar c = BUF_FETCH_CHAR (buf, idx); + if (UPPERCASEP (buf, c)) + c = DOWNCASE (buf, c); + p += set_charptr_emchar (p, c); + } + lookup = oblookup (obarray, word, p - word); + if (SYMBOLP (lookup) && !NILP (symbol_value (XSYMBOL (lookup)))) + return XSYMBOL (lookup); + else + return NULL; +} + +/* Return non-zero if OBARRAY contains an interned symbol ` '. */ +static int +obarray_has_blank_p (Lisp_Object obarray) +{ + Lisp_Object lookup; + + lookup = oblookup (obarray, (Bufbyte *)" ", 1); + return SYMBOLP (lookup); +} + +/* Analyze case in the buffer substring, and report it. */ +static void +abbrev_count_case (struct buffer *buf, Bufpos pos, Charcount length, + int *lccount, int *uccount) +{ + Emchar c; + + *lccount = *uccount = 0; + while (length--) + { + c = BUF_FETCH_CHAR (buf, pos); + if (UPPERCASEP (buf, c)) + ++*uccount; + else if (LOWERCASEP (buf, c)) + ++*lccount; + ++pos; + } +} DEFUN ("expand-abbrev", Fexpand_abbrev, 0, 0, "", /* -Expand the abbrev before point, if there is an abbrev there. +Expand the abbrev before point, if any. Effective when explicitly called even when `abbrev-mode' is nil. Returns t if expansion took place. */ ()) { /* This function can GC */ - REGISTER Bufbyte *buffer, *p; - REGISTER Bufpos wordstart, wordend, idx; - Charcount whitecnt; - Charcount uccount = 0, lccount = 0; - REGISTER Lisp_Object sym; - Lisp_Object expansion, hook, value; struct buffer *buf = current_buffer; - Lisp_Object lbuf; int oldmodiff = BUF_MODIFF (buf); + Lisp_Object pre_modiff_p; + Bufpos point; /* position of point */ + Bufpos abbrev_start; /* position of abbreviation beginning */ - XSETBUFFER (lbuf, buf); + struct Lisp_Symbol *(*fun) (struct buffer *, Lisp_Object); + + struct Lisp_Symbol *abbrev_symbol; + struct Lisp_String *abbrev_string; + Lisp_Object expansion, count, hook; + Charcount abbrev_length, idx; + int lccount, uccount; + 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); + pre_modiff_p = (BUF_MODIFF (buf) != oldmodiff ? Qt : Qnil); - wordstart = 0; + abbrev_symbol = NULL; if (!BUFFERP (Vabbrev_start_location_buffer) || XBUFFER (Vabbrev_start_location_buffer) != buf) Vabbrev_start_location = Qnil; - if (!NILP (Vabbrev_start_location)) + /* We use the more general `abbrev_match' if the obarray blank flag + is not set, and Vabbrev_start_location is nil. Otherwise, use + `abbrev_oblookup'. */ +#define MATCHFUN(tbl) ((obarray_has_blank_p (tbl) \ + && NILP (Vabbrev_start_location)) \ + ? abbrev_match : abbrev_oblookup) + if (!NILP (buf->abbrev_table)) { - wordstart = get_buffer_pos_char (buf, Vabbrev_start_location, GB_COERCE_RANGE); - Vabbrev_start_location = Qnil; - if (wordstart < BUF_BEGV (buf) || wordstart > BUF_ZV (buf)) - wordstart = 0; - if (wordstart && wordstart != BUF_ZV (buf) && - BUF_FETCH_CHAR (buf, wordstart) == '-') - buffer_delete_range (buf, wordstart, wordstart + 1, 0); + fun = MATCHFUN (buf->abbrev_table); + abbrev_symbol = fun (buf, buf->abbrev_table); } - if (!wordstart) - wordstart = scan_words (buf, BUF_PT (buf), -1); - - if (!wordstart) - return value; + if (!abbrev_symbol && !NILP (Vglobal_abbrev_table)) + { + fun = MATCHFUN (Vglobal_abbrev_table); + abbrev_symbol = fun (buf, Vglobal_abbrev_table); + } + if (!abbrev_symbol) + return pre_modiff_p; - wordend = scan_words (buf, wordstart, 1); - if (!wordend) - return value; - - if (wordend > BUF_PT (buf)) - wordend = BUF_PT (buf); - whitecnt = BUF_PT (buf) - wordend; - if (wordend <= wordstart) - return value; + /* NOTE: we hope that `pre-abbrev-expand-hook' didn't do something + nasty, such as changed (or killed) the buffer. */ + point = BUF_PT (buf); - p = buffer = (Bufbyte *) alloca (MAX_EMCHAR_LEN*(wordend - wordstart)); - - for (idx = wordstart; idx < wordend; idx++) - { - REGISTER Emchar c = BUF_FETCH_CHAR (buf, idx); - if (UPPERCASEP (buf, c)) - c = DOWNCASE (buf, c), uccount++; - else if (! NOCASEP (buf, c)) - lccount++; - p += set_charptr_emchar (p, c); - } + /* OK, we're out of the must-be-fast part. An abbreviation matched. + Now find the parameters, insert the expansion, and make it all + look pretty. */ + abbrev_string = symbol_name (abbrev_symbol); + abbrev_length = string_char_length (abbrev_string); + abbrev_start = point - abbrev_length; - if (VECTORP (buf->abbrev_table)) - sym = oblookup (buf->abbrev_table, - buffer, - p - buffer); + expansion = symbol_value (abbrev_symbol); + CHECK_STRING (expansion); + + count = symbol_plist (abbrev_symbol); /* Gag */ + if (NILP (count)) + count = make_int (0); else - sym = Qzero; - if (INTP (sym) || NILP (XSYMBOL (sym)->value)) - sym = oblookup (Vglobal_abbrev_table, - buffer, - p - buffer); - if (INTP (sym) || NILP (XSYMBOL (sym)->value)) - return value; + CHECK_NATNUM (count); + symbol_plist (abbrev_symbol) = make_int (1 + XINT (count)); + + /* Count the case in the original text. */ + abbrev_count_case (buf, abbrev_start, abbrev_length, &lccount, &uccount); - if (INTERACTIVE && !EQ (minibuf_window, Fselected_window (Qnil))) - { - /* Add an undo boundary, in case we are doing this for - a self-inserting command which has avoided making one so far. */ - BUF_SET_PT (buf, wordend); - Fundo_boundary (); - } - BUF_SET_PT (buf, wordstart); + /* Remember the last abbrev text, location, etc. */ + XSETSYMBOL (Vlast_abbrev, abbrev_symbol); Vlast_abbrev_text = - make_string_from_buffer (buf, wordstart, wordend - wordstart); - buffer_delete_range (buf, wordstart, wordend, 0); + make_string_from_buffer (buf, abbrev_start, abbrev_length); + last_abbrev_point = abbrev_start; - /* Now sym is the abbrev symbol. */ - Vlast_abbrev = sym; - last_abbrev_point = wordstart; + /* Add an undo boundary, in case we are doing this for a + self-inserting command which has avoided making one so far. */ + if (INTERACTIVE) + Fundo_boundary (); - if (INTP (XSYMBOL (sym)->plist)) - XSETINT (XSYMBOL (sym)->plist, - XINT (XSYMBOL (sym)->plist) + 1); /* Increment use count */ + /* 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); - expansion = XSYMBOL (sym)->value; - buffer_insert_lisp_string (buf, expansion); - BUF_SET_PT (buf, BUF_PT (buf) + whitecnt); - + /* Now fiddle with the case. */ if (uccount && !lccount) { /* Abbrev was all caps */ - /* If expansion is multiple words, normally capitalize each word */ - /* This used to be if (!... && ... >= ...) Fcapitalize; else Fupcase - but Megatest 68000 compiler can't handle that */ - if (!abbrev_all_caps) - if (scan_words (buf, BUF_PT (buf), -1) > - scan_words (buf, wordstart, 1)) - { - Fupcase_initials_region (make_int (wordstart), - make_int (BUF_PT (buf)), - lbuf); - goto caped; - } - /* If expansion is one word, or if user says so, upcase it all. */ - Fupcase_region (make_int (wordstart), make_int (BUF_PT (buf)), - lbuf); - caped: ; + 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 = wordstart; - + Bufpos pos = abbrev_start; /* Find the initial. */ - while (pos < BUF_PT (buf) - && !WORD_SYNTAX_P (XCHAR_TABLE (buf->mirror_syntax_table), + while (pos < point + && !WORD_SYNTAX_P (XCHAR_TABLE (buf->mirror_syntax_table), BUF_FETCH_CHAR (buf, pos))) - pos++; - + pos++; /* Change just that. */ - Fupcase_initials_region (make_int (pos), make_int (pos + 1), lbuf); + Fupcase_initials_region (make_int (pos), make_int (pos + 1), + make_buffer (buf)); } - hook = XSYMBOL (sym)->function; + hook = symbol_function (abbrev_symbol); if (!NILP (hook) && !UNBOUNDP (hook)) call0 (hook); return Qt; } + void syms_of_abbrev (void) { @@ -272,7 +434,7 @@ Vabbrev_start_location_buffer = Qnil; DEFVAR_BOOL ("abbrev-all-caps", &abbrev_all_caps /* -*Set non-nil means expand multi-word abbrevs all caps if abbrev was so. +*Non-nil means expand multi-word abbrevs all caps if abbrev was so. */ ); abbrev_all_caps = 0;