(timezone-parse-date): Use < 69 not < 70 to distinguish 20YY from 19YY.
[bpt/emacs.git] / src / abbrev.c
1 /* Primitives for word-abbrev mode.
2 Copyright (C) 1985, 1986, 1993, 1996, 1998 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21
22 #include <config.h>
23 #include <stdio.h>
24 #include "lisp.h"
25 #include "commands.h"
26 #include "buffer.h"
27 #include "window.h"
28 #include "charset.h"
29 #include "syntax.h"
30
31 /* An abbrev table is an obarray.
32 Each defined abbrev is represented by a symbol in that obarray
33 whose print name is the abbreviation.
34 The symbol's value is a string which is the expansion.
35 If its function definition is non-nil, it is called
36 after the expansion is done.
37 The plist slot of the abbrev symbol is its usage count. */
38
39 /* List of all abbrev-table name symbols:
40 symbols whose values are abbrev tables. */
41
42 Lisp_Object Vabbrev_table_name_list;
43
44 /* The table of global abbrevs. These are in effect
45 in any buffer in which abbrev mode is turned on. */
46
47 Lisp_Object Vglobal_abbrev_table;
48
49 /* The local abbrev table used by default (in Fundamental Mode buffers) */
50
51 Lisp_Object Vfundamental_mode_abbrev_table;
52
53 /* Set nonzero when an abbrev definition is changed */
54
55 int abbrevs_changed;
56
57 int abbrev_all_caps;
58
59 /* Non-nil => use this location as the start of abbrev to expand
60 (rather than taking the word before point as the abbrev) */
61
62 Lisp_Object Vabbrev_start_location;
63
64 /* Buffer that Vabbrev_start_location applies to */
65 Lisp_Object Vabbrev_start_location_buffer;
66
67 /* The symbol representing the abbrev most recently expanded */
68
69 Lisp_Object Vlast_abbrev;
70
71 /* A string for the actual text of the abbrev most recently expanded.
72 This has more info than Vlast_abbrev since case is significant. */
73
74 Lisp_Object Vlast_abbrev_text;
75
76 /* Character address of start of last abbrev expanded */
77
78 int last_abbrev_point;
79
80 /* Hook to run before expanding any abbrev. */
81
82 Lisp_Object Vpre_abbrev_expand_hook, Qpre_abbrev_expand_hook;
83 \f
84 DEFUN ("make-abbrev-table", Fmake_abbrev_table, Smake_abbrev_table, 0, 0, 0,
85 "Create a new, empty abbrev table object.")
86 ()
87 {
88 return Fmake_vector (make_number (59), make_number (0));
89 }
90
91 DEFUN ("clear-abbrev-table", Fclear_abbrev_table, Sclear_abbrev_table, 1, 1, 0,
92 "Undefine all abbrevs in abbrev table TABLE, leaving it empty.")
93 (table)
94 Lisp_Object table;
95 {
96 int i, size;
97
98 CHECK_VECTOR (table, 0);
99 size = XVECTOR (table)->size;
100 abbrevs_changed = 1;
101 for (i = 0; i < size; i++)
102 XVECTOR (table)->contents[i] = make_number (0);
103 return Qnil;
104 }
105 \f
106 DEFUN ("define-abbrev", Fdefine_abbrev, Sdefine_abbrev, 3, 5, 0,
107 "Define an abbrev in TABLE named NAME, to expand to EXPANSION and call HOOK.\n\
108 NAME must be a string.\n\
109 EXPANSION should usually be a string.\n\
110 To undefine an abbrev, define it with EXPANSION = nil.\n\
111 If HOOK is non-nil, it should be a function of no arguments;\n\
112 it is called after EXPANSION is inserted.\n\
113 If EXPANSION is not a string, the abbrev is a special one,\n\
114 which does not expand in the usual way but only runs HOOK.\n\
115 COUNT, if specified, initializes the abbrev's usage-count\n\
116 which is incremented each time the abbrev is used.")
117 (table, name, expansion, hook, count)
118 Lisp_Object table, name, expansion, hook, count;
119 {
120 Lisp_Object sym, oexp, ohook, tem;
121 CHECK_VECTOR (table, 0);
122 CHECK_STRING (name, 1);
123
124 if (NILP (count))
125 count = make_number (0);
126 else
127 CHECK_NUMBER (count, 0);
128
129 sym = Fintern (name, table);
130
131 oexp = XSYMBOL (sym)->value;
132 ohook = XSYMBOL (sym)->function;
133 if (!((EQ (oexp, expansion)
134 || (STRINGP (oexp) && STRINGP (expansion)
135 && (tem = Fstring_equal (oexp, expansion), !NILP (tem))))
136 &&
137 (EQ (ohook, hook)
138 || (tem = Fequal (ohook, hook), !NILP (tem)))))
139 abbrevs_changed = 1;
140
141 Fset (sym, expansion);
142 Ffset (sym, hook);
143 Fsetplist (sym, count);
144
145 return name;
146 }
147
148 DEFUN ("define-global-abbrev", Fdefine_global_abbrev, Sdefine_global_abbrev, 2, 2,
149 "sDefine global abbrev: \nsExpansion for %s: ",
150 "Define ABBREV as a global abbreviation for EXPANSION.")
151 (abbrev, expansion)
152 Lisp_Object abbrev, expansion;
153 {
154 Fdefine_abbrev (Vglobal_abbrev_table, Fdowncase (abbrev),
155 expansion, Qnil, make_number (0));
156 return abbrev;
157 }
158
159 DEFUN ("define-mode-abbrev", Fdefine_mode_abbrev, Sdefine_mode_abbrev, 2, 2,
160 "sDefine mode abbrev: \nsExpansion for %s: ",
161 "Define ABBREV as a mode-specific abbreviation for EXPANSION.")
162 (abbrev, expansion)
163 Lisp_Object abbrev, expansion;
164 {
165 if (NILP (current_buffer->abbrev_table))
166 error ("Major mode has no abbrev table");
167
168 Fdefine_abbrev (current_buffer->abbrev_table, Fdowncase (abbrev),
169 expansion, Qnil, make_number (0));
170 return abbrev;
171 }
172
173 DEFUN ("abbrev-symbol", Fabbrev_symbol, Sabbrev_symbol, 1, 2, 0,
174 "Return the symbol representing abbrev named ABBREV.\n\
175 This symbol's name is ABBREV, but it is not the canonical symbol of that name;\n\
176 it is interned in an abbrev-table rather than the normal obarray.\n\
177 The value is nil if that abbrev is not defined.\n\
178 Optional second arg TABLE is abbrev table to look it up in.\n\
179 The default is to try buffer's mode-specific abbrev table, then global table.")
180 (abbrev, table)
181 Lisp_Object abbrev, table;
182 {
183 Lisp_Object sym;
184 CHECK_STRING (abbrev, 0);
185 if (!NILP (table))
186 sym = Fintern_soft (abbrev, table);
187 else
188 {
189 sym = Qnil;
190 if (!NILP (current_buffer->abbrev_table))
191 sym = Fintern_soft (abbrev, current_buffer->abbrev_table);
192 if (NILP (XSYMBOL (sym)->value))
193 sym = Qnil;
194 if (NILP (sym))
195 sym = Fintern_soft (abbrev, Vglobal_abbrev_table);
196 }
197 if (NILP (XSYMBOL (sym)->value)) return Qnil;
198 return sym;
199 }
200
201 DEFUN ("abbrev-expansion", Fabbrev_expansion, Sabbrev_expansion, 1, 2, 0,
202 "Return the string that ABBREV expands into in the current buffer.\n\
203 Optionally specify an abbrev table as second arg;\n\
204 then ABBREV is looked up in that table only.")
205 (abbrev, table)
206 Lisp_Object abbrev, table;
207 {
208 Lisp_Object sym;
209 sym = Fabbrev_symbol (abbrev, table);
210 if (NILP (sym)) return sym;
211 return Fsymbol_value (sym);
212 }
213 \f
214 /* Expand the word before point, if it is an abbrev.
215 Returns 1 if an expansion is done. */
216
217 DEFUN ("expand-abbrev", Fexpand_abbrev, Sexpand_abbrev, 0, 0, "",
218 "Expand the abbrev before point, if there is an abbrev there.\n\
219 Effective when explicitly called even when `abbrev-mode' is nil.\n\
220 Returns the abbrev symbol, if expansion took place.")
221 ()
222 {
223 register char *buffer, *p;
224 int wordstart, wordend;
225 register int wordstart_byte, wordend_byte, idx;
226 int whitecnt;
227 int uccount = 0, lccount = 0;
228 register Lisp_Object sym;
229 Lisp_Object expansion, hook, tem;
230 int oldmodiff = MODIFF;
231 Lisp_Object value;
232
233 value = Qnil;
234
235 if (!NILP (Vrun_hooks))
236 call1 (Vrun_hooks, Qpre_abbrev_expand_hook);
237
238 wordstart = 0;
239 if (!(BUFFERP (Vabbrev_start_location_buffer)
240 && XBUFFER (Vabbrev_start_location_buffer) == current_buffer))
241 Vabbrev_start_location = Qnil;
242 if (!NILP (Vabbrev_start_location))
243 {
244 tem = Vabbrev_start_location;
245 CHECK_NUMBER_COERCE_MARKER (tem, 0);
246 wordstart = XINT (tem);
247 Vabbrev_start_location = Qnil;
248 if (wordstart < BEGV || wordstart > ZV)
249 wordstart = 0;
250 if (wordstart && wordstart != ZV)
251 {
252 wordstart_byte = CHAR_TO_BYTE (wordstart);
253 if (FETCH_BYTE (wordstart_byte) == '-')
254 del_range (wordstart, wordstart + 1);
255 }
256 }
257 if (!wordstart)
258 wordstart = scan_words (PT, -1);
259
260 if (!wordstart)
261 return value;
262
263 wordstart_byte = CHAR_TO_BYTE (wordstart);
264 wordend = scan_words (wordstart, 1);
265 if (!wordend)
266 return value;
267
268 if (wordend > PT)
269 wordend = PT;
270
271 wordend_byte = CHAR_TO_BYTE (wordend);
272 whitecnt = PT - wordend;
273 if (wordend <= wordstart)
274 return value;
275
276 p = buffer = (char *) alloca (wordend_byte - wordstart_byte);
277
278 for (idx = wordstart_byte; idx < wordend_byte; idx++)
279 {
280 /* ??? This loop needs to go by characters! */
281 register int c = FETCH_BYTE (idx);
282 if (UPPERCASEP (c))
283 c = DOWNCASE (c), uccount++;
284 else if (! NOCASEP (c))
285 lccount++;
286 *p++ = c;
287 }
288
289 if (VECTORP (current_buffer->abbrev_table))
290 sym = oblookup (current_buffer->abbrev_table, buffer,
291 wordend - wordstart, wordend_byte - wordstart_byte);
292 else
293 XSETFASTINT (sym, 0);
294 if (INTEGERP (sym) || NILP (XSYMBOL (sym)->value))
295 sym = oblookup (Vglobal_abbrev_table, buffer,
296 wordend - wordstart, wordend_byte - wordstart_byte);
297 if (INTEGERP (sym) || NILP (XSYMBOL (sym)->value))
298 return value;
299
300 if (INTERACTIVE && !EQ (minibuf_window, selected_window))
301 {
302 /* Add an undo boundary, in case we are doing this for
303 a self-inserting command which has avoided making one so far. */
304 SET_PT (wordend);
305 Fundo_boundary ();
306 }
307
308 Vlast_abbrev_text
309 = Fbuffer_substring (make_number (wordstart), make_number (wordend));
310
311 /* Now sym is the abbrev symbol. */
312 Vlast_abbrev = sym;
313 value = sym;
314 last_abbrev_point = wordstart;
315
316 if (INTEGERP (XSYMBOL (sym)->plist))
317 XSETINT (XSYMBOL (sym)->plist,
318 XINT (XSYMBOL (sym)->plist) + 1); /* Increment use count */
319
320 /* If this abbrev has an expansion, delete the abbrev
321 and insert the expansion. */
322 expansion = XSYMBOL (sym)->value;
323 if (STRINGP (expansion))
324 {
325 SET_PT (wordstart);
326
327 del_range_both (wordstart, wordstart_byte, wordend, wordend_byte, 1);
328
329 insert_from_string (expansion, 0, 0, XSTRING (expansion)->size,
330 STRING_BYTES (XSTRING (expansion)), 1);
331 SET_PT (PT + whitecnt);
332
333 if (uccount && !lccount)
334 {
335 /* Abbrev was all caps */
336 /* If expansion is multiple words, normally capitalize each word */
337 /* This used to be if (!... && ... >= ...) Fcapitalize; else Fupcase
338 but Megatest 68000 compiler can't handle that */
339 if (!abbrev_all_caps)
340 if (scan_words (PT, -1) > scan_words (wordstart, 1))
341 {
342 Fupcase_initials_region (make_number (wordstart),
343 make_number (PT));
344 goto caped;
345 }
346 /* If expansion is one word, or if user says so, upcase it all. */
347 Fupcase_region (make_number (wordstart), make_number (PT));
348 caped: ;
349 }
350 else if (uccount)
351 {
352 /* Abbrev included some caps. Cap first initial of expansion */
353 int pos = wordstart_byte;
354
355 /* Find the initial. */
356 while (pos < PT_BYTE
357 && SYNTAX (*BUF_BYTE_ADDRESS (current_buffer, pos)) != Sword)
358 pos++;
359
360 /* Change just that. */
361 pos = BYTE_TO_CHAR (pos);
362 Fupcase_initials_region (make_number (pos), make_number (pos + 1));
363 }
364 }
365
366 hook = XSYMBOL (sym)->function;
367 if (!NILP (hook))
368 call0 (hook);
369
370 return value;
371 }
372
373 DEFUN ("unexpand-abbrev", Funexpand_abbrev, Sunexpand_abbrev, 0, 0, "",
374 "Undo the expansion of the last abbrev that expanded.\n\
375 This differs from ordinary undo in that other editing done since then\n\
376 is not undone.")
377 ()
378 {
379 int opoint = PT;
380 int adjust = 0;
381 if (last_abbrev_point < BEGV
382 || last_abbrev_point > ZV)
383 return Qnil;
384 SET_PT (last_abbrev_point);
385 if (STRINGP (Vlast_abbrev_text))
386 {
387 /* This isn't correct if Vlast_abbrev->function was used
388 to do the expansion */
389 Lisp_Object val;
390 int zv_before;
391
392 val = XSYMBOL (Vlast_abbrev)->value;
393 if (!STRINGP (val))
394 error ("value of abbrev-symbol must be a string");
395 zv_before = ZV;
396 del_range_byte (PT_BYTE, PT_BYTE + STRING_BYTES (XSTRING (val)), 1);
397 /* Don't inherit properties here; just copy from old contents. */
398 insert_from_string (Vlast_abbrev_text, 0, 0,
399 XSTRING (Vlast_abbrev_text)->size,
400 STRING_BYTES (XSTRING (Vlast_abbrev_text)), 0);
401 Vlast_abbrev_text = Qnil;
402 /* Total number of characters deleted. */
403 adjust = ZV - zv_before;
404 }
405 SET_PT (last_abbrev_point < opoint ? opoint + adjust : opoint);
406 return Qnil;
407 }
408 \f
409 static void
410 write_abbrev (sym, stream)
411 Lisp_Object sym, stream;
412 {
413 Lisp_Object name;
414 if (NILP (XSYMBOL (sym)->value))
415 return;
416 insert (" (", 5);
417 XSETSTRING (name, XSYMBOL (sym)->name);
418 Fprin1 (name, stream);
419 insert (" ", 1);
420 Fprin1 (XSYMBOL (sym)->value, stream);
421 insert (" ", 1);
422 Fprin1 (XSYMBOL (sym)->function, stream);
423 insert (" ", 1);
424 Fprin1 (XSYMBOL (sym)->plist, stream);
425 insert (")\n", 2);
426 }
427
428 static void
429 describe_abbrev (sym, stream)
430 Lisp_Object sym, stream;
431 {
432 Lisp_Object one;
433
434 if (NILP (XSYMBOL (sym)->value))
435 return;
436 one = make_number (1);
437 Fprin1 (Fsymbol_name (sym), stream);
438 Findent_to (make_number (15), one);
439 Fprin1 (XSYMBOL (sym)->plist, stream);
440 Findent_to (make_number (20), one);
441 Fprin1 (XSYMBOL (sym)->value, stream);
442 if (!NILP (XSYMBOL (sym)->function))
443 {
444 Findent_to (make_number (45), one);
445 Fprin1 (XSYMBOL (sym)->function, stream);
446 }
447 Fterpri (stream);
448 }
449
450 DEFUN ("insert-abbrev-table-description", Finsert_abbrev_table_description,
451 Sinsert_abbrev_table_description, 1, 2, 0,
452 "Insert before point a full description of abbrev table named NAME.\n\
453 NAME is a symbol whose value is an abbrev table.\n\
454 If optional 2nd arg READABLE is non-nil, a human-readable description\n\
455 is inserted. Otherwise the description is an expression,\n\
456 a call to `define-abbrev-table', which would\n\
457 define the abbrev table NAME exactly as it is currently defined.")
458 (name, readable)
459 Lisp_Object name, readable;
460 {
461 Lisp_Object table;
462 Lisp_Object stream;
463
464 CHECK_SYMBOL (name, 0);
465 table = Fsymbol_value (name);
466 CHECK_VECTOR (table, 0);
467
468 XSETBUFFER (stream, current_buffer);
469
470 if (!NILP (readable))
471 {
472 insert_string ("(");
473 Fprin1 (name, stream);
474 insert_string (")\n\n");
475 map_obarray (table, describe_abbrev, stream);
476 insert_string ("\n\n");
477 }
478 else
479 {
480 insert_string ("(define-abbrev-table '");
481 Fprin1 (name, stream);
482 insert_string (" '(\n");
483 map_obarray (table, write_abbrev, stream);
484 insert_string (" ))\n\n");
485 }
486
487 return Qnil;
488 }
489 \f
490 DEFUN ("define-abbrev-table", Fdefine_abbrev_table, Sdefine_abbrev_table,
491 2, 2, 0,
492 "Define TABLENAME (a symbol) as an abbrev table name.\n\
493 Define abbrevs in it according to DEFINITIONS, which is a list of elements\n\
494 of the form (ABBREVNAME EXPANSION HOOK USECOUNT).")
495 (tablename, definitions)
496 Lisp_Object tablename, definitions;
497 {
498 Lisp_Object name, exp, hook, count;
499 Lisp_Object table, elt;
500
501 CHECK_SYMBOL (tablename, 0);
502 table = Fboundp (tablename);
503 if (NILP (table) || (table = Fsymbol_value (tablename), NILP (table)))
504 {
505 table = Fmake_abbrev_table ();
506 Fset (tablename, table);
507 Vabbrev_table_name_list = Fcons (tablename, Vabbrev_table_name_list);
508 }
509 CHECK_VECTOR (table, 0);
510
511 for (; !NILP (definitions); definitions = Fcdr (definitions))
512 {
513 elt = Fcar (definitions);
514 name = Fcar (elt); elt = Fcdr (elt);
515 exp = Fcar (elt); elt = Fcdr (elt);
516 hook = Fcar (elt); elt = Fcdr (elt);
517 count = Fcar (elt);
518 Fdefine_abbrev (table, name, exp, hook, count);
519 }
520 return Qnil;
521 }
522 \f
523 void
524 syms_of_abbrev ()
525 {
526 DEFVAR_LISP ("abbrev-table-name-list", &Vabbrev_table_name_list,
527 "List of symbols whose values are abbrev tables.");
528 Vabbrev_table_name_list = Fcons (intern ("fundamental-mode-abbrev-table"),
529 Fcons (intern ("global-abbrev-table"),
530 Qnil));
531
532 DEFVAR_LISP ("global-abbrev-table", &Vglobal_abbrev_table,
533 "The abbrev table whose abbrevs affect all buffers.\n\
534 Each buffer may also have a local abbrev table.\n\
535 If it does, the local table overrides the global one\n\
536 for any particular abbrev defined in both.");
537 Vglobal_abbrev_table = Fmake_abbrev_table ();
538
539 DEFVAR_LISP ("fundamental-mode-abbrev-table", &Vfundamental_mode_abbrev_table,
540 "The abbrev table of mode-specific abbrevs for Fundamental Mode.");
541 Vfundamental_mode_abbrev_table = Fmake_abbrev_table ();
542 current_buffer->abbrev_table = Vfundamental_mode_abbrev_table;
543
544 DEFVAR_LISP ("last-abbrev", &Vlast_abbrev,
545 "The abbrev-symbol of the last abbrev expanded. See `abbrev-symbol'.");
546
547 DEFVAR_LISP ("last-abbrev-text", &Vlast_abbrev_text,
548 "The exact text of the last abbrev expanded.\n\
549 nil if the abbrev has already been unexpanded.");
550
551 DEFVAR_INT ("last-abbrev-location", &last_abbrev_point,
552 "The location of the start of the last abbrev expanded.");
553
554 Vlast_abbrev = Qnil;
555 Vlast_abbrev_text = Qnil;
556 last_abbrev_point = 0;
557
558 DEFVAR_LISP ("abbrev-start-location", &Vabbrev_start_location,
559 "Buffer position for `expand-abbrev' to use as the start of the abbrev.\n\
560 nil means use the word before point as the abbrev.\n\
561 Calling `expand-abbrev' sets this to nil.");
562 Vabbrev_start_location = Qnil;
563
564 DEFVAR_LISP ("abbrev-start-location-buffer", &Vabbrev_start_location_buffer,
565 "Buffer that `abbrev-start-location' has been set for.\n\
566 Trying to expand an abbrev in any other buffer clears `abbrev-start-location'.");
567 Vabbrev_start_location_buffer = Qnil;
568
569 DEFVAR_PER_BUFFER ("local-abbrev-table", &current_buffer->abbrev_table, Qnil,
570 "Local (mode-specific) abbrev table of current buffer.");
571
572 DEFVAR_BOOL ("abbrevs-changed", &abbrevs_changed,
573 "Set non-nil by defining or altering any word abbrevs.\n\
574 This causes `save-some-buffers' to offer to save the abbrevs.");
575 abbrevs_changed = 0;
576
577 DEFVAR_BOOL ("abbrev-all-caps", &abbrev_all_caps,
578 "*Set non-nil means expand multi-word abbrevs all caps if abbrev was so.");
579 abbrev_all_caps = 0;
580
581 DEFVAR_LISP ("pre-abbrev-expand-hook", &Vpre_abbrev_expand_hook,
582 "Function or functions to be called before abbrev expansion is done.\n\
583 This is the first thing that `expand-abbrev' does, and so this may change\n\
584 the current abbrev table before abbrev lookup happens.");
585 Vpre_abbrev_expand_hook = Qnil;
586 Qpre_abbrev_expand_hook = intern ("pre-abbrev-expand-hook");
587 staticpro (&Qpre_abbrev_expand_hook);
588
589 defsubr (&Smake_abbrev_table);
590 defsubr (&Sclear_abbrev_table);
591 defsubr (&Sdefine_abbrev);
592 defsubr (&Sdefine_global_abbrev);
593 defsubr (&Sdefine_mode_abbrev);
594 defsubr (&Sabbrev_expansion);
595 defsubr (&Sabbrev_symbol);
596 defsubr (&Sexpand_abbrev);
597 defsubr (&Sunexpand_abbrev);
598 defsubr (&Sinsert_abbrev_table_description);
599 defsubr (&Sdefine_abbrev_table);
600 }