X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/d3f40cbd579627891b9ca5a6dce0d94a94e69796..2f60660a5eae9addccb472243bd88179b8f15d1e:/src/composite.c diff --git a/src/composite.c b/src/composite.c index 085458db52..f8e655a368 100644 --- a/src/composite.c +++ b/src/composite.c @@ -1,6 +1,7 @@ /* Composite sequence support. Copyright (C) 1999 Electrotechnical Laboratory, JAPAN. Licensed to the Free Software Foundation. + Copyright (C) 2001 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -91,32 +92,32 @@ Boston, MA 02111-1307, USA. */ The former is a hash table in which keys are COMPONENTS-VECs and values are the corresponding COMPOSITION-IDs. This hash table is - weak, but as each key (COMPONENTS-VEC) is also kept as a value of + weak, but as each key (COMPONENTS-VEC) is also kept as a value of the `composition' property, it won't be collected as garbage until all - text that have the same COMPONENTS-VEC are deleted. + bits of text that have the same COMPONENTS-VEC are deleted. The latter is a table of pointers to `struct composition' indexed - by COMPOSITION-ID. This structure keep the other information (see + by COMPOSITION-ID. This structure keeps the other information (see composite.h). In general, a text property holds information about individual characters. But, a `composition' property holds information about - a sequence of characters (in this sense, it is like `intangible' + a sequence of characters (in this sense, it is like the `intangible' property). That means that we should not share the property value - in adjacent compositions we can't distinguish them if they have the + in adjacent compositions -- we can't distinguish them if they have the same property. So, after any changes, we call `update_compositions' and change a property of one of adjacent compositions to a copy of it. This function also runs a proper composition modification function to make a composition that gets invalid by the change valid again. - As a value of `composition' property holds information about a + As the value of the `composition' property holds information about a specific range of text, the value gets invalid if we change the - text in the range. We treat `composition' property always + text in the range. We treat the `composition' property as always rear-nonsticky (currently by setting default-text-properties to (rear-nonsticky (composition))) and we never make properties of adjacent compositions identical. Thus, any such changes make the - range just shorter. So, we can check the validity of `composition' + range just shorter. So, we can check the validity of the `composition' property by comparing LENGTH information with the actual length of the composition. @@ -144,6 +145,10 @@ Lisp_Object composition_hash_table; /* Function to call to adjust composition. */ Lisp_Object Vcompose_chars_after_function; +/* Char-table of patterns and functions to make a composition. */ +Lisp_Object Vcomposition_function_table; +Lisp_Object Qcomposition_function_table; + /* Temporary variable used in macros COMPOSITION_XXX. */ Lisp_Object composition_temp; @@ -153,14 +158,6 @@ Lisp_Object composition_temp; #define CHAR_WIDTH(c) \ (SINGLE_BYTE_CHAR_P (c) ? 1 : CHARSET_WIDTH (CHAR_CHARSET (c))) -/* The following macros for hash table are copied from fns.c. */ -/* Return the contents of vector V at index IDX. */ -#define AREF(V, IDX) XVECTOR (V)->contents[IDX] -/* Value is the key part of entry IDX in hash table H. */ -#define HASH_KEY(H, IDX) AREF ((H)->key_and_value, 2 * (IDX)) -/* Value is the value part of entry IDX in hash table H. */ -#define HASH_VALUE(H, IDX) AREF ((H)->key_and_value, 2 * (IDX) + 1) - /* Return COMPOSITION-ID of a composition at buffer position CHARPOS/BYTEPOS and length NCHARS. The `composition' property of the sequence is PROP. STRING, if non-nil, is a string that @@ -246,13 +243,13 @@ get_composition_id (charpos, bytepos, nchars, prop, string) modify the cons cell of PROP because it is not shared. */ key = HASH_KEY (hash_table, hash_index); id = HASH_VALUE (hash_table, hash_index); - XCAR (prop) = id; - XCDR (prop) = Fcons (make_number (nchars), Fcons (key, XCDR (prop))); + XSETCAR (prop, id); + XSETCDR (prop, Fcons (make_number (nchars), Fcons (key, XCDR (prop)))); return XINT (id); } /* This composition is a new one. We must register it. */ - + /* Check if we have sufficient memory to store this information. */ if (composition_table_size == 0) { @@ -294,8 +291,8 @@ get_composition_id (charpos, bytepos, nchars, prop, string) /* Change PROP from Form-A above to Form-B. We can directly modify the cons cell of PROP because it is not shared. */ XSETFASTINT (id, n_compositions); - XCAR (prop) = id; - XCDR (prop) = Fcons (make_number (nchars), Fcons (key, XCDR (prop))); + XSETCAR (prop, id); + XSETCDR (prop, Fcons (make_number (nchars), Fcons (key, XCDR (prop)))); /* Register the composition in composition_hash_table. */ hash_index = hash_put (hash_table, key, id, hash_code); @@ -420,14 +417,25 @@ find_composition (pos, limit, start, end, prop, object) return 0; if (limit > pos) /* search forward */ - val = Fnext_single_property_change (make_number (pos), Qcomposition, - object, make_number (limit)); + { + val = Fnext_single_property_change (make_number (pos), Qcomposition, + object, make_number (limit)); + pos = XINT (val); + if (pos == limit) + return 0; + } else /* search backward */ - val = Fprevious_single_property_change (make_number (pos), Qcomposition, - object, make_number (limit)); - pos = XINT (val); - if (pos == limit) - return 0; + { + if (get_property_and_range (pos - 1, Qcomposition, prop, start, end, + object)) + return 1; + val = Fprevious_single_property_change (make_number (pos), Qcomposition, + object, make_number (limit)); + pos = XINT (val); + if (pos == limit) + return 0; + pos--; + } get_property_and_range (pos, Qcomposition, prop, start, end, object); return 1; } @@ -440,7 +448,7 @@ run_composition_function (from, to, prop) int from, to; Lisp_Object prop; { - Lisp_Object func, val; + Lisp_Object func; int start, end; func = COMPOSITION_MODIFICATION_FUNC (prop); @@ -454,11 +462,11 @@ run_composition_function (from, to, prop) && find_composition (to, -1, &start, &end, &prop, Qnil) && !COMPOSITION_VALID_P (start, end, prop)) to = end; - if (!NILP (func)) + if (!NILP (Ffboundp (func))) call2 (func, make_number (from), make_number (to)); else if (!NILP (Ffboundp (Vcompose_chars_after_function))) - call2 (Vcompose_chars_after_function, - make_number (from), make_number (to)); + call3 (Vcompose_chars_after_function, + make_number (from), make_number (to), Qnil); } /* Make invalid compositions adjacent to or inside FROM and TO valid. @@ -470,11 +478,14 @@ run_composition_function (from, to, prop) void update_compositions (from, to, check_mask) - int from, to; + int from, to, check_mask; { - Lisp_Object prop, hook; + Lisp_Object prop; int start, end; + if (inhibit_modification_hooks) + return; + /* If FROM and TO are not in a valid range, do nothing. */ if (! (BEGV <= from && from <= to && to <= ZV)) return; @@ -496,7 +507,7 @@ update_compositions (from, to, check_mask) run_composition_function (start, end, prop); from = end; } - else if (from < end + else if (from < ZV && find_composition (from, -1, &start, &from, &prop, Qnil)) run_composition_function (start, from, prop); } @@ -534,6 +545,31 @@ update_compositions (from, to, check_mask) } } + +/* Modify composition property values in LIST destructively. LIST is + a list as returned from text_property_list. Change values to the + top-level copies of them so that none of them are `eq'. */ + +void +make_composition_value_copy (list) + Lisp_Object list; +{ + Lisp_Object plist, val; + + for (; CONSP (list); list = XCDR (list)) + { + plist = XCAR (XCDR (XCDR (XCAR (list)))); + while (CONSP (plist) && CONSP (XCDR (plist))) + { + if (EQ (XCAR (plist), Qcomposition) + && (val = XCAR (XCDR (plist)), CONSP (val))) + XSETCAR (XCDR (plist), Fcons (XCAR (val), XCDR (val))); + plist = XCDR (XCDR (plist)); + } + } +} + + /* Make text in the region between START and END a composition that has COMPONENTS and MODIFICATION-FUNC. @@ -553,17 +589,134 @@ compose_text (start, end, components, modification_func, string) Qcomposition, prop, string); } +/* Compose sequences of characters in the region between START and END + by functions registered in Vcomposition_function_table. If STRING + is non-nil, operate on characters contained between indices START + and END in STRING. */ + +void +compose_chars_in_text (start, end, string) + int start, end; + Lisp_Object string; +{ + int count = 0; + struct gcpro gcpro1; + Lisp_Object tail, elt, val, to; + /* Set to nonzero if we don't have to compose ASCII characters. */ + int skip_ascii; + int i, len, stop, c; + const unsigned char *ptr, *pend; + + if (! CHAR_TABLE_P (Vcomposition_function_table)) + return; + + if (STRINGP (string)) + { + count = SPECPDL_INDEX (); + GCPRO1 (string); + stop = end; + ptr = SDATA (string) + string_char_to_byte (string, start); + pend = ptr + SBYTES (string); + } + else + { + record_unwind_protect (save_excursion_restore, save_excursion_save ()); + TEMP_SET_PT (start); + stop = (start < GPT && GPT < end ? GPT : end); + ptr = CHAR_POS_ADDR (start); + pend = CHAR_POS_ADDR (end); + } + + /* Preserve the match data. */ + record_unwind_protect (Fset_match_data, Fmatch_data (Qnil, Qnil)); + + /* If none of ASCII characters have composition functions, we can + skip them quickly. */ + for (i = 0; i < 128; i++) + if (!NILP (CHAR_TABLE_REF (Vcomposition_function_table, i))) + break; + skip_ascii = (i == 128); + + + while (1) + { + if (skip_ascii) + while (start < stop && ASCII_BYTE_P (*ptr)) + start++, ptr++; + + if (start >= stop) + { + if (stop == end || start >= end) + break; + stop = end; + if (STRINGP (string)) + ptr = SDATA (string) + string_char_to_byte (string, start); + else + ptr = CHAR_POS_ADDR (start); + } + + c = STRING_CHAR_AND_LENGTH (ptr, pend - ptr, len); + tail = CHAR_TABLE_REF (Vcomposition_function_table, c); + while (CONSP (tail)) + { + elt = XCAR (tail); + if (CONSP (elt) + && STRINGP (XCAR (elt)) + && !NILP (Ffboundp (XCDR (elt)))) + { + if (STRINGP (string)) + val = Fstring_match (XCAR (elt), string, make_number (start)); + else + { + val = Flooking_at (XCAR (elt)); + if (!NILP (val)) + val = make_number (start); + } + if (INTEGERP (val) && XFASTINT (val) == start) + { + to = Fmatch_end (make_number (0)); + val = call4 (XCDR (elt), val, to, XCAR (elt), string); + if (INTEGERP (val) && XINT (val) > 1) + { + start += XINT (val); + if (STRINGP (string)) + ptr = SDATA (string) + string_char_to_byte (string, start); + else + ptr = CHAR_POS_ADDR (start); + } + else + { + start++; + ptr += len; + } + break; + } + } + tail = XCDR (tail); + } + if (!CONSP (tail)) + { + /* No composition done. Try the next character. */ + start++; + ptr += len; + } + } + + unbind_to (count, Qnil); + if (STRINGP (string)) + UNGCPRO; +} /* Emacs Lisp APIs. */ DEFUN ("compose-region-internal", Fcompose_region_internal, Scompose_region_internal, 2, 4, 0, - "Internal use only.\n\ -\n\ -Compose text in the region between START and END.\n\ -Optional 3rd and 4th arguments are COMPONENTS and MODIFICATION-FUNC\n\ -for the composition. See `compose-region' for more detial.") - (start, end, components, mod_func) + doc: /* Internal use only. + +Compose text in the region between START and END. +Optional 3rd and 4th arguments are COMPONENTS and MODIFICATION-FUNC +for the composition. See `compose-region' for more detail. */) + (start, end, components, mod_func) Lisp_Object start, end, components, mod_func; { validate_region (&start, &end); @@ -571,7 +724,7 @@ for the composition. See `compose-region' for more detial.") && !INTEGERP (components) && !CONSP (components) && !STRINGP (components)) - CHECK_VECTOR (components, 2); + CHECK_VECTOR (components); compose_text (XINT (start), XINT (end), components, mod_func, Qnil); return Qnil; @@ -579,21 +732,21 @@ for the composition. See `compose-region' for more detial.") DEFUN ("compose-string-internal", Fcompose_string_internal, Scompose_string_internal, 3, 5, 0, - "Internal use only.\n\ -\n\ -Compose text between indices START and END of STRING.\n\ -Optional 4th and 5th arguments are COMPONENTS and MODIFICATION-FUNC\n\ -for the composition. See `compose-string' for more detial.") - (string, start, end, components, mod_func) + doc: /* Internal use only. + +Compose text between indices START and END of STRING. +Optional 4th and 5th arguments are COMPONENTS and MODIFICATION-FUNC +for the composition. See `compose-string' for more detail. */) + (string, start, end, components, mod_func) Lisp_Object string, start, end, components, mod_func; { - CHECK_STRING (string, 0); - CHECK_NUMBER (start, 1); - CHECK_NUMBER (end, 2); + CHECK_STRING (string); + CHECK_NUMBER (start); + CHECK_NUMBER (end); if (XINT (start) < 0 || XINT (start) > XINT (end) - || XINT (end) > XSTRING (string)->size) + || XINT (end) > SCHARS (string)) args_out_of_range (start, end); compose_text (XINT (start), XINT (end), components, mod_func, string); @@ -601,29 +754,39 @@ for the composition. See `compose-string' for more detial.") } DEFUN ("find-composition-internal", Ffind_composition_internal, - Sfind_composition_internal, 4, 4, 0, - "Internal use only.\n\ -\n\ -Return information about composition at or nearest to position POS.\n\ -See `find-composition' for more detail.") - (pos, limit, string, detail_p) + Sfind_composition_internal, 4, 4, 0, + doc: /* Internal use only. + +Return information about composition at or nearest to position POS. +See `find-composition' for more detail. */) + (pos, limit, string, detail_p) Lisp_Object pos, limit, string, detail_p; { Lisp_Object prop, tail; int start, end; int id; - CHECK_NUMBER_COERCE_MARKER (pos, 0); + CHECK_NUMBER_COERCE_MARKER (pos); start = XINT (pos); if (!NILP (limit)) { - CHECK_NUMBER_COERCE_MARKER (limit, 1); + CHECK_NUMBER_COERCE_MARKER (limit); end = XINT (limit); } else end = -1; + if (!NILP (string)) - CHECK_STRING (string, 2); + { + CHECK_STRING (string); + if (XINT (pos) < 0 || XINT (pos) > SCHARS (string)) + args_out_of_range (string, pos); + } + else + { + if (XINT (pos) < BEGV || XINT (pos) > ZV) + args_out_of_range (Fcurrent_buffer (), pos); + } if (!find_composition (start, end, &start, &end, &prop, string)) return Qnil; @@ -676,9 +839,14 @@ syms_of_composite () { Lisp_Object args[6]; extern Lisp_Object QCsize; - + args[0] = QCtest; args[1] = Qequal; + /* We used to make the hash table weak so that unreferenced + compostions can be garbage-collected. But, usually once + created compositions are repeatedly used in an Emacs session, + and thus it's not worth to save memory in such a way. So, we + make the table not weak. */ args[2] = QCweakness; args[3] = Qnil; args[4] = QCsize; @@ -692,19 +860,47 @@ syms_of_composite () = Fcons (Fcons (Qcomposition, Qt), Vtext_property_default_nonsticky); DEFVAR_LISP ("compose-chars-after-function", &Vcompose_chars_after_function, - "Function to adjust composition of buffer text.\n\ -\n\ -This function is called after a text with `composition' property is\n\ -inserted or deleted to keep `composition' property of buffer text\n\ -valid.\n\ -\n\ -The function is called with two arguments FROM and TO. They specify\n\ -the range of text of which composition should be adjusted.\n\ -\n\ -The default value is the function `compose-chars-after'."); + doc: /* Function to adjust composition of buffer text. + +The function is called with three arguments FROM, TO, and OBJECT. +FROM and TO specify the range of text of which composition should be +adjusted. OBJECT, if non-nil, is a string that contains the text. + +This function is called after a text with `composition' property is +inserted or deleted to keep `composition' property of buffer text +valid. + +The default value is the function `compose-chars-after'. */); Vcompose_chars_after_function = intern ("compose-chars-after"); + Qcomposition_function_table = intern ("composition-function-table"); + staticpro (&Qcomposition_function_table); + + /* Intern this now in case it isn't already done. + Setting this variable twice is harmless. + But don't staticpro it here--that is done in alloc.c. */ + Qchar_table_extra_slots = intern ("char-table-extra-slots"); + + Fput (Qcomposition_function_table, Qchar_table_extra_slots, make_number (0)); + + DEFVAR_LISP ("composition-function-table", &Vcomposition_function_table, + doc: /* Char table of patterns and functions to make a composition. + +Each element is nil or an alist of PATTERNs vs FUNCs, where PATTERNs +are regular expressions and FUNCs are functions. FUNC is responsible +for composing text matching the corresponding PATTERN. FUNC is called +with three arguments FROM, TO, and PATTERN. See the function +`compose-chars-after' for more detail. + +This table is looked up by the first character of a composition when +the composition gets invalid after a change in a buffer. */); + Vcomposition_function_table + = Fmake_char_table (Qcomposition_function_table, Qnil); + defsubr (&Scompose_region_internal); defsubr (&Scompose_string_internal); defsubr (&Sfind_composition_internal); } + +/* arch-tag: 79cefaf8-ca48-4eed-97e5-d5afb290d272 + (do not change this comment) */