aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPengji Zhang2025-04-02 20:52:30 +0800
committerEli Zaretskii2025-04-05 12:30:46 +0300
commitc94f0d3dc82a72baa0ba8d69beda220aaf0b2d91 (patch)
treea60e9a00236ab43fa7bf5d1aa92a0743a5bf50fa
parent2d0b5f34a008979d34f337c872bcf93a296c6ec2 (diff)
downloademacs-c94f0d3dc82a72baa0ba8d69beda220aaf0b2d91.tar.gz
emacs-c94f0d3dc82a72baa0ba8d69beda220aaf0b2d91.zip
Fix mouse highlighting for compact mode lines (bug#77336)
When 'mode-line-compact' is non-nil, the mode line string is displayed as a whole. That confuses the computation of ranges of mouse highlighting on the mode line because all the glyphs have the same Lisp object source. As such, in this commit we instead split the mode line string by sources, and display those elements one by one, so the boundaries of each element could be correctly detected for the purpose of mouse highlighting. * src/xdisp.c (display_mode_line): Display mode line elements one by one when 'mode-line-compact' is non-nil. (display_mode_element): Record source element number of the stored string via a text property. (Fformat_mode_line): Initialize 'mode_line_elt_no' to 0. (syms_of_xdisp): New symbol for the text property.
-rw-r--r--src/xdisp.c145
1 files changed, 106 insertions, 39 deletions
diff --git a/src/xdisp.c b/src/xdisp.c
index 61c464d0f36..1340716e4a1 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -13674,6 +13674,9 @@ static Lisp_Object mode_line_proptrans_alist;
13674/* List of strings making up the mode-line. */ 13674/* List of strings making up the mode-line. */
13675static Lisp_Object mode_line_string_list; 13675static Lisp_Object mode_line_string_list;
13676 13676
13677/* Element number for the stored mode line string. */
13678static ptrdiff_t mode_line_elt_no;
13679
13677/* Base face property when building propertized mode line string. */ 13680/* Base face property when building propertized mode line string. */
13678static Lisp_Object mode_line_string_face; 13681static Lisp_Object mode_line_string_face;
13679static Lisp_Object mode_line_string_face_prop; 13682static Lisp_Object mode_line_string_face_prop;
@@ -27766,57 +27769,94 @@ display_mode_line (struct window *w, enum face_id face_id, Lisp_Object format)
27766 { 27769 {
27767 /* The window is wide enough; just display the mode line we 27770 /* The window is wide enough; just display the mode line we
27768 just computed. */ 27771 just computed. */
27769 display_string (NULL, mode_string, Qnil, 27772 Lisp_Object start = make_fixnum (0), end;
27770 0, 0, &it, 0, 0, 0, 27773 Lisp_Object elt;
27771 STRING_MULTIBYTE (mode_string)); 27774
27775 /* Display the mode line string one element by one element.
27776 This is to make the ranges of mouse highlighting
27777 correct. */
27778 do
27779 {
27780 end = Fnext_single_property_change (start, Qmode_line_elt_no,
27781 mode_string, Qnil);
27782 elt = Fsubstring (mode_string, start, end);
27783 display_string (NULL, elt, Qnil, 0, 0, &it, 0, 0, 0,
27784 STRING_MULTIBYTE (elt));
27785 start = end;
27786 }
27787 while (!NILP (end));
27772 } 27788 }
27773 else 27789 else
27774 { 27790 {
27775 /* Compress the mode line. */ 27791 /* Compress the mode line. */
27776 ptrdiff_t i = 0, i_byte = 0, start = 0; 27792 ptrdiff_t i = 0, i_byte = 0;
27777 int prev = 0; 27793 int prev = 0;
27794 Lisp_Object start = make_fixnum (0), end;
27795 Lisp_Object elt = empty_unibyte_string;
27778 27796
27779 while (i < SCHARS (mode_string)) 27797 /* Display the mode line string one element by one element.
27798 This is to make the ranges of mouse highlighting
27799 correct. */
27800 do
27780 { 27801 {
27781 int c = fetch_string_char_advance (mode_string, &i, &i_byte); 27802 end = Fnext_single_property_change (start,
27782 if (c == ' ' && prev == ' ') 27803 Qmode_line_elt_no,
27804 mode_string,
27805 make_fixnum (SCHARS (mode_string)));
27806 while (i < XFIXNUM (end))
27783 { 27807 {
27784 Lisp_Object prev_pos = make_fixnum (i - 1); 27808 int c = fetch_string_char_advance (mode_string, &i, &i_byte);
27785 27809 if (c == ' ' && prev == ' ')
27786 /* SPC characters with 'display' properties are not
27787 really "empty", since they have non-trivial visual
27788 effects on the mode line. */
27789 if (NILP (Fget_text_property (prev_pos, Qdisplay,
27790 mode_string)))
27791 { 27810 {
27792 display_string (NULL, 27811 Lisp_Object prev_pos = make_fixnum (i - 1);
27793 Fsubstring (mode_string, 27812 Lisp_Object display = Fget_text_property (prev_pos,
27794 make_fixnum (start), 27813 Qdisplay,
27795 prev_pos), 27814 mode_string);
27796 Qnil, 0, 0, &it, 0, 0, 0, 27815
27797 STRING_MULTIBYTE (mode_string)); 27816 /* SPC characters with 'display' properties are not
27798 /* Skip past the rest of the space characters. */ 27817 really "empty", since they have non-trivial visual
27799 while (c == ' ' && i < SCHARS (mode_string) 27818 effects on the mode line. */
27800 && NILP (Fget_text_property (make_fixnum (i), 27819 if (NILP (display))
27801 Qdisplay,
27802 mode_string)))
27803 { 27820 {
27804 c = fetch_string_char_advance (mode_string, 27821 elt = concat2 (elt, Fsubstring (mode_string,
27805 &i, &i_byte); 27822 start,
27823 prev_pos));
27824
27825 /* Skip past the rest of the space characters. */
27826 Lisp_Object display = Fget_text_property (make_fixnum (i),
27827 Qdisplay,
27828 mode_string);
27829 while (c == ' ' && i < XFIXNUM (end)
27830 && NILP (display))
27831 {
27832 c = fetch_string_char_advance (mode_string,
27833 &i, &i_byte);
27834 display = Fget_text_property (make_fixnum (i),
27835 Qdisplay,
27836 mode_string);
27837 }
27838
27839 /* Skip the final space no matter how the loop
27840 above ends. */
27841 if (c == ' ' && NILP (display))
27842 start = end;
27843 else
27844 start = make_fixnum (i - 1);
27806 } 27845 }
27807 start = i - 1;
27808 } 27846 }
27847 prev = c;
27809 } 27848 }
27810 prev = c;
27811 }
27812 27849
27813 /* Display the final bit. */ 27850 /* Append the final bit. */
27814 if (start < i) 27851 if (XFIXNUM (start) < XFIXNUM (end))
27815 display_string (NULL, 27852 elt = concat2 (elt, Fsubstring (mode_string, start, end));
27816 Fsubstring (mode_string, make_fixnum (start), 27853
27817 make_fixnum (i)), 27854 display_string (NULL, elt, Qnil, 0, 0, &it, 0, 0, 0,
27818 Qnil, 0, 0, &it, 0, 0, 0, 27855 STRING_MULTIBYTE (elt));
27819 STRING_MULTIBYTE (mode_string)); 27856 elt = empty_unibyte_string;
27857 start = end;
27858 }
27859 while (XFIXNUM (end) < SCHARS (mode_string));
27820 } 27860 }
27821 } 27861 }
27822 pop_kboard (); 27862 pop_kboard ();
@@ -27938,6 +27978,20 @@ display_mode_element (struct it *it, int depth, int field_width, int precision,
27938 if (depth > 100) 27978 if (depth > 100)
27939 elt = build_string ("*too-deep*"); 27979 elt = build_string ("*too-deep*");
27940 27980
27981 /* NOTE: Increment the number only for 'MODE_LINE_STRING', because for
27982 other cases this variable is not used. So we do not need to reset
27983 the variable in every callers of this function.
27984
27985 NOTE: This might result in gaps in the stored element numbers
27986 because some elements are processed recursively. However, we
27987 cannot increment this number when the number is stored because an
27988 element could be stored by parts. For example, "a%b" is stored as
27989 two elements in the 'mode_line_string_list', but they should be
27990 considered as one element, so we cannot increment this number when
27991 "a" is stored. */
27992 if (mode_line_target == MODE_LINE_STRING)
27993 mode_line_elt_no++;
27994
27941 depth++; 27995 depth++;
27942 27996
27943 switch (XTYPE (elt)) 27997 switch (XTYPE (elt))
@@ -28035,7 +28089,11 @@ display_mode_element (struct it *it, int depth, int field_width, int precision,
28035 n += store_mode_line_noprop (SSDATA (elt), -1, prec); 28089 n += store_mode_line_noprop (SSDATA (elt), -1, prec);
28036 break; 28090 break;
28037 case MODE_LINE_STRING: 28091 case MODE_LINE_STRING:
28038 n += store_mode_line_string (NULL, elt, true, 0, prec, Qnil); 28092 {
28093 AUTO_LIST2 (src, Qmode_line_elt_no,
28094 make_fixnum (mode_line_elt_no));
28095 n += store_mode_line_string (NULL, elt, true, 0, prec, src);
28096 }
28039 break; 28097 break;
28040 case MODE_LINE_DISPLAY: 28098 case MODE_LINE_DISPLAY:
28041 n += display_string (NULL, elt, Qnil, 0, 0, it, 28099 n += display_string (NULL, elt, Qnil, 0, 0, it,
@@ -28088,8 +28146,10 @@ display_mode_element (struct it *it, int depth, int field_width, int precision,
28088 Lisp_Object mode_string 28146 Lisp_Object mode_string
28089 = Fsubstring (elt, make_fixnum (charpos), 28147 = Fsubstring (elt, make_fixnum (charpos),
28090 make_fixnum (endpos)); 28148 make_fixnum (endpos));
28149 AUTO_LIST2 (src, Qmode_line_elt_no,
28150 make_fixnum (mode_line_elt_no));
28091 n += store_mode_line_string (NULL, mode_string, false, 28151 n += store_mode_line_string (NULL, mode_string, false,
28092 0, 0, Qnil); 28152 0, 0, src);
28093 } 28153 }
28094 break; 28154 break;
28095 case MODE_LINE_DISPLAY: 28155 case MODE_LINE_DISPLAY:
@@ -28169,6 +28229,8 @@ display_mode_element (struct it *it, int depth, int field_width, int precision,
28169 { 28229 {
28170 Lisp_Object tem = build_string (spec); 28230 Lisp_Object tem = build_string (spec);
28171 props = Ftext_properties_at (make_fixnum (charpos), elt); 28231 props = Ftext_properties_at (make_fixnum (charpos), elt);
28232 props = plist_put (props, Qmode_line_elt_no,
28233 make_fixnum (mode_line_elt_no));
28172 /* Should only keep face property in props */ 28234 /* Should only keep face property in props */
28173 n += store_mode_line_string (NULL, tem, false, 28235 n += store_mode_line_string (NULL, tem, false,
28174 field, prec, props); 28236 field, prec, props);
@@ -28384,6 +28446,9 @@ display_mode_element (struct it *it, int depth, int field_width, int precision,
28384 n += store_mode_line_noprop ("", field_width - n, 0); 28446 n += store_mode_line_noprop ("", field_width - n, 0);
28385 break; 28447 break;
28386 case MODE_LINE_STRING: 28448 case MODE_LINE_STRING:
28449 /* NOTE: Padding implicitly has 'mode-line-elt-no' property
28450 to be nil. Normally padding spaces are between two real
28451 elements, so this should be good. */
28387 n += store_mode_line_string ("", Qnil, false, field_width - n, 0, 28452 n += store_mode_line_string ("", Qnil, false, field_width - n, 0,
28388 Qnil); 28453 Qnil);
28389 break; 28454 break;
@@ -28591,6 +28656,7 @@ are the selected window and the WINDOW's buffer). */)
28591 } 28656 }
28592 28657
28593 push_kboard (FRAME_KBOARD (it.f)); 28658 push_kboard (FRAME_KBOARD (it.f));
28659 mode_line_elt_no = 0;
28594 display_mode_element (&it, 0, 0, 0, format, Qnil, false); 28660 display_mode_element (&it, 0, 0, 0, format, Qnil, false);
28595 pop_kboard (); 28661 pop_kboard ();
28596 28662
@@ -37678,6 +37744,7 @@ be let-bound around code that needs to disable messages temporarily. */);
37678 DEFSYM (Qfontification_functions, "fontification-functions"); 37744 DEFSYM (Qfontification_functions, "fontification-functions");
37679 DEFSYM (Qlong_line_optimizations_in_fontification_functions, 37745 DEFSYM (Qlong_line_optimizations_in_fontification_functions,
37680 "long-line-optimizations-in-fontification-functions"); 37746 "long-line-optimizations-in-fontification-functions");
37747 DEFSYM (Qmode_line_elt_no, "mode-line-elt-no");
37681 37748
37682 /* Name of the symbol which disables Lisp evaluation in 'display' 37749 /* Name of the symbol which disables Lisp evaluation in 'display'
37683 properties. This is used by enriched.el. */ 37750 properties. This is used by enriched.el. */