aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPo Lu2023-02-15 22:51:44 +0800
committerPo Lu2023-02-15 22:51:44 +0800
commitcf24b61985c26cbf2e5a24cb0b64a8528aa3a9cc (patch)
treeb69f8dbb50e3e2f6f09caa05aecbee5241876f62 /src
parentdd7066901f67233c09f3b0409a57db7686c7ea5b (diff)
downloademacs-cf24b61985c26cbf2e5a24cb0b64a8528aa3a9cc.tar.gz
emacs-cf24b61985c26cbf2e5a24cb0b64a8528aa3a9cc.zip
Update Android port
* doc/emacs/input.texi (On-Screen Keyboards): * doc/lispref/commands.texi (Misc Events): Improve documentation of text conversion stuff. * java/org/gnu/emacs/EmacsInputConnection.java (beginBatchEdit) (endBatchEdit, commitCompletion, commitText, deleteSurroundingText) (finishComposingText, getSelectedText, getTextAfterCursor) (EmacsInputConnection, setComposingRegion, performEditorAction) (getExtractedText): Condition debug code on DEBUG_IC. * java/org/gnu/emacs/EmacsService.java (EmacsService, updateIC): Likewise. * lisp/bindings.el (global-map): * lisp/electric.el (global-map): Make `text-conversion' `analyze-text-conversion'. * lisp/progmodes/prog-mode.el (prog-mode): Enable text conversion in input methods. * lisp/simple.el (analyze-text-conversion): New function. * lisp/textmodes/text-mode.el (text-conversion-style) (text-mode): Likewise. * src/androidterm.c (android_handle_ime_event): Handle set_point_and_mark. (android_sync_edit): Give Emacs 100 ms instead. (android_perform_conversion_query): Skip the active region, not the conversion region. (getSelectedText): Implement properly. (android_update_selection): Expose mark to input methods. (android_reset_conversion): Handle `text-conversion-style'. * src/buffer.c (init_buffer_once, syms_of_buffer): Add buffer local variable `text-conversion-style'. * src/buffer.h (struct buffer, bset_text_conversion_style): New fields. * src/emacs.c (android_emacs_init): Call syms_of_textconv. * src/frame.h (enum text_conversion_operation): Rename TEXTCONV_SET_POINT. * src/lisp.h: Export syms_of_textconv. * src/marker.c (set_marker_internal): Force redisplay when the mark is set and the buffer is visible on builds that use text conversion. Explain why. * src/textconv.c (copy_buffer): Fix copying past gap. (get_mark): New function. (textconv_query): Implement new flag. (sync_overlay): New function. Display conversion text in an overlay. (record_buffer_change, really_commit_text) (really_set_composing_text, really_set_composing_region) (really_delete_surrounding_text, really_set_point) (handle_pending_conversion_events_1, decrement_inside) (handle_pending_conversion_events, textconv_set_point) (get_extracted_text, register_textconv_interface): Various fixes and improvements. * src/textconv.h (struct textconv_interface): Update documentation. * src/window.h (GCALIGNED_STRUCT): New field `prev_mark'. * src/xdisp.c (mark_window_display_accurate_1): Handle prev_mark.
Diffstat (limited to 'src')
-rw-r--r--src/androidterm.c88
-rw-r--r--src/buffer.c20
-rw-r--r--src/buffer.h11
-rw-r--r--src/emacs.c3
-rw-r--r--src/frame.h2
-rw-r--r--src/lisp.h1
-rw-r--r--src/marker.c26
-rw-r--r--src/textconv.c315
-rw-r--r--src/textconv.h11
-rw-r--r--src/window.h4
-rw-r--r--src/xdisp.c11
11 files changed, 438 insertions, 54 deletions
diff --git a/src/androidterm.c b/src/androidterm.c
index 767b7d8240c..0c990d3d2d2 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -621,8 +621,9 @@ android_handle_ime_event (union android_event *event, struct frame *f)
621 break; 621 break;
622 622
623 case ANDROID_IME_SET_POINT: 623 case ANDROID_IME_SET_POINT:
624 textconv_set_point (f, event->ime.position, 624 textconv_set_point_and_mark (f, event->ime.start,
625 event->ime.counter); 625 event->ime.end,
626 event->ime.counter);
626 break; 627 break;
627 628
628 case ANDROID_IME_START_BATCH_EDIT: 629 case ANDROID_IME_START_BATCH_EDIT:
@@ -4305,7 +4306,7 @@ static sem_t edit_sem;
4305 4306
4306 Every time one of the text retrieval functions is called and an 4307 Every time one of the text retrieval functions is called and an
4307 editing request is made, Emacs gives the main thread approximately 4308 editing request is made, Emacs gives the main thread approximately
4308 50 ms to process it, in order to mostly keep the input method in 4309 100 ms to process it, in order to mostly keep the input method in
4309 sync with the buffer contents. */ 4310 sync with the buffer contents. */
4310 4311
4311static void 4312static void
@@ -4319,7 +4320,7 @@ android_sync_edit (void)
4319 return; 4320 return;
4320 4321
4321 start = current_timespec (); 4322 start = current_timespec ();
4322 end = timespec_add (start, make_timespec (0, 50000000)); 4323 end = timespec_add (start, make_timespec (0, 100000000));
4323 4324
4324 while (true) 4325 while (true)
4325 { 4326 {
@@ -4550,8 +4551,7 @@ android_perform_conversion_query (void *data)
4550 if (!f) 4551 if (!f)
4551 return; 4552 return;
4552 4553
4553 textconv_query (f, &context->query, 4554 textconv_query (f, &context->query, TEXTCONV_SKIP_ACTIVE_REGION);
4554 TEXTCONV_SKIP_CONVERSION_REGION);
4555 4555
4556 /* context->query.text will have been set even if textconv_query 4556 /* context->query.text will have been set even if textconv_query
4557 returns 1. */ 4557 returns 1. */
@@ -4649,13 +4649,6 @@ android_text_to_string (JNIEnv *env, char *buffer, ptrdiff_t n,
4649} 4649}
4650 4650
4651JNIEXPORT jstring JNICALL 4651JNIEXPORT jstring JNICALL
4652NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object,
4653 jshort window)
4654{
4655 return NULL;
4656}
4657
4658JNIEXPORT jstring JNICALL
4659NATIVE_NAME (getTextAfterCursor) (JNIEnv *env, jobject object, jshort window, 4652NATIVE_NAME (getTextAfterCursor) (JNIEnv *env, jobject object, jshort window,
4660 jint length, jint flags) 4653 jint length, jint flags)
4661{ 4654{
@@ -4805,8 +4798,8 @@ NATIVE_NAME (setSelection) (JNIEnv *env, jobject object, jshort window,
4805 event.ime.serial = ++event_serial; 4798 event.ime.serial = ++event_serial;
4806 event.ime.window = window; 4799 event.ime.window = window;
4807 event.ime.operation = ANDROID_IME_SET_POINT; 4800 event.ime.operation = ANDROID_IME_SET_POINT;
4808 event.ime.start = 0; 4801 event.ime.start = start;
4809 event.ime.end = 0; 4802 event.ime.end = end;
4810 event.ime.length = 0; 4803 event.ime.length = 0;
4811 event.ime.position = start; 4804 event.ime.position = start;
4812 event.ime.text = NULL; 4805 event.ime.text = NULL;
@@ -5068,6 +5061,34 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object,
5068 return object; 5061 return object;
5069} 5062}
5070 5063
5064JNIEXPORT jstring JNICALL
5065NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object,
5066 jshort window)
5067{
5068 struct android_get_extracted_text_context context;
5069 jstring string;
5070
5071 context.hint_max_chars = -1;
5072 context.token = 0;
5073 context.text = NULL;
5074 context.window = window;
5075
5076 android_sync_edit ();
5077 if (android_run_in_emacs_thread (android_get_extracted_text,
5078 &context))
5079 return NULL;
5080
5081 if (!context.text)
5082 return NULL;
5083
5084 /* Encode the returned text. */
5085 string = android_text_to_string (env, context.text, context.length,
5086 context.bytes);
5087 free (context.text);
5088
5089 return string;
5090}
5091
5071#ifdef __clang__ 5092#ifdef __clang__
5072#pragma clang diagnostic pop 5093#pragma clang diagnostic pop
5073#else 5094#else
@@ -5083,7 +5104,8 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object,
5083static void 5104static void
5084android_update_selection (struct frame *f, struct window *w) 5105android_update_selection (struct frame *f, struct window *w)
5085{ 5106{
5086 ptrdiff_t start, end, point; 5107 ptrdiff_t start, end, point, mark;
5108 struct buffer *b;
5087 5109
5088 if (MARKERP (f->conversion.compose_region_start)) 5110 if (MARKERP (f->conversion.compose_region_start))
5089 { 5111 {
@@ -5103,12 +5125,20 @@ android_update_selection (struct frame *f, struct window *w)
5103 if (!w) 5125 if (!w)
5104 w = XWINDOW (f->selected_window); 5126 w = XWINDOW (f->selected_window);
5105 5127
5106 /* Figure out where the point is. */ 5128 /* Figure out where the point and mark are. If the mark is not
5129 active, then point is set to equal mark. */
5130 b = XBUFFER (w->contents);
5107 point = min (w->last_point, TYPE_MAXIMUM (jint)); 5131 point = min (w->last_point, TYPE_MAXIMUM (jint));
5132 mark = ((!NILP (BVAR (b, mark_active))
5133 && w->last_mark != -1)
5134 ? min (w->last_mark, TYPE_MAXIMUM (jint))
5135 : point);
5108 5136
5109 /* Send the update. */ 5137 /* Send the update. Android doesn't have a concept of ``point'' and
5110 android_update_ic (FRAME_ANDROID_WINDOW (f), point, point, 5138 ``mark''; instead, it only has a selection, where the start of
5111 start, end); 5139 the selection is less than or equal to the end. */
5140 android_update_ic (FRAME_ANDROID_WINDOW (f), min (point, mark),
5141 max (point, mark), start, end);
5112} 5142}
5113 5143
5114/* Notice that the input method connection to F should be reset as a 5144/* Notice that the input method connection to F should be reset as a
@@ -5117,16 +5147,32 @@ android_update_selection (struct frame *f, struct window *w)
5117static void 5147static void
5118android_reset_conversion (struct frame *f) 5148android_reset_conversion (struct frame *f)
5119{ 5149{
5150 enum android_ic_mode mode;
5151 struct window *w;
5152 struct buffer *buffer;
5153
5120 /* Reset the input method. 5154 /* Reset the input method.
5121 5155
5122 Pick an appropriate ``input mode'' based on whether or not the 5156 Pick an appropriate ``input mode'' based on whether or not the
5123 minibuffer window is selected; this controls whether or not 5157 minibuffer window is selected; this controls whether or not
5124 ``RET'' inserts a newline or sends an actual key event. */ 5158 ``RET'' inserts a newline or sends an actual key event. */
5159
5160 w = XWINDOW (f->selected_window);
5161 buffer = XBUFFER (WINDOW_BUFFER (w));
5162
5163 if (NILP (BVAR (buffer, text_conversion_style)))
5164 mode = ANDROID_IC_MODE_NULL;
5165 else if (EQ (BVAR (buffer, text_conversion_style),
5166 Qaction))
5167 mode = ANDROID_IC_MODE_ACTION;
5168 else
5169 mode = ANDROID_IC_MODE_TEXT;
5170
5125 android_reset_ic (FRAME_ANDROID_WINDOW (f), 5171 android_reset_ic (FRAME_ANDROID_WINDOW (f),
5126 (EQ (f->selected_window, 5172 (EQ (f->selected_window,
5127 f->minibuffer_window) 5173 f->minibuffer_window)
5128 ? ANDROID_IC_MODE_ACTION 5174 ? ANDROID_IC_MODE_ACTION
5129 : ANDROID_IC_MODE_TEXT)); 5175 : mode));
5130 5176
5131 /* Move its selection to the specified position. */ 5177 /* Move its selection to the specified position. */
5132 android_update_selection (f, NULL); 5178 android_update_selection (f, NULL);
diff --git a/src/buffer.c b/src/buffer.c
index 38648519ba0..af4aa583c96 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -4710,6 +4710,7 @@ init_buffer_once (void)
4710#ifdef HAVE_TREE_SITTER 4710#ifdef HAVE_TREE_SITTER
4711 XSETFASTINT (BVAR (&buffer_local_flags, ts_parser_list), idx); ++idx; 4711 XSETFASTINT (BVAR (&buffer_local_flags, ts_parser_list), idx); ++idx;
4712#endif 4712#endif
4713 XSETFASTINT (BVAR (&buffer_local_flags, text_conversion_style), idx); ++idx;
4713 XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx; 4714 XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx;
4714 4715
4715 /* buffer_local_flags contains no pointers, so it's safe to treat it 4716 /* buffer_local_flags contains no pointers, so it's safe to treat it
@@ -4781,6 +4782,9 @@ init_buffer_once (void)
4781#ifdef HAVE_TREE_SITTER 4782#ifdef HAVE_TREE_SITTER
4782 bset_ts_parser_list (&buffer_defaults, Qnil); 4783 bset_ts_parser_list (&buffer_defaults, Qnil);
4783#endif 4784#endif
4785#ifdef HAVE_TEXT_CONVERSION
4786 bset_text_conversion_style (&buffer_defaults, Qnil);
4787#endif
4784 bset_cursor_in_non_selected_windows (&buffer_defaults, Qt); 4788 bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
4785 4789
4786 bset_enable_multibyte_characters (&buffer_defaults, Qt); 4790 bset_enable_multibyte_characters (&buffer_defaults, Qt);
@@ -5852,6 +5856,22 @@ If t, displays a cursor related to the usual cursor type
5852You can also specify the cursor type as in the `cursor-type' variable. 5856You can also specify the cursor type as in the `cursor-type' variable.
5853Use Custom to set this variable and update the display. */); 5857Use Custom to set this variable and update the display. */);
5854 5858
5859 /* While this is defined here, each *term.c module must implement
5860 the logic itself. */
5861
5862 DEFVAR_PER_BUFFER ("text-conversion-style", &BVAR (current_buffer,
5863 text_conversion_style),
5864 Qnil,
5865 "How the on screen keyboard's input method should insert in this buffer.\n\
5866When nil, the input method will be disabled and an ordinary keyboard\n\
5867will be displayed in its place.\n\
5868When the symbol `action', the input method will insert text directly, but\n\
5869will send `return' key events instead of inserting new line characters.\n\
5870Any other value means that the input method will insert text directly.\n\
5871\n\
5872This variable does not take immediate effect when set; rather, it takes\n\
5873effect upon the next redisplay after the selected window or buffer changes.");
5874
5855 DEFVAR_LISP ("kill-buffer-query-functions", Vkill_buffer_query_functions, 5875 DEFVAR_LISP ("kill-buffer-query-functions", Vkill_buffer_query_functions,
5856 doc: /* List of functions called with no args to query before killing a buffer. 5876 doc: /* List of functions called with no args to query before killing a buffer.
5857The buffer being killed will be current while the functions are running. 5877The buffer being killed will be current while the functions are running.
diff --git a/src/buffer.h b/src/buffer.h
index e700297a264..e71ffe28045 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -566,6 +566,11 @@ struct buffer
566 /* A list of tree-sitter parsers for this buffer. */ 566 /* A list of tree-sitter parsers for this buffer. */
567 Lisp_Object ts_parser_list_; 567 Lisp_Object ts_parser_list_;
568#endif 568#endif
569
570 /* What type of text conversion the input method should apply to
571 this buffer. */
572 Lisp_Object text_conversion_style_;
573
569 /* Cursor type to display in non-selected windows. 574 /* Cursor type to display in non-selected windows.
570 t means to use hollow box cursor. 575 t means to use hollow box cursor.
571 See `cursor-type' for other values. */ 576 See `cursor-type' for other values. */
@@ -842,6 +847,12 @@ bset_width_table (struct buffer *b, Lisp_Object val)
842 b->width_table_ = val; 847 b->width_table_ = val;
843} 848}
844 849
850INLINE void
851bset_text_conversion_style (struct buffer *b, Lisp_Object val)
852{
853 b->text_conversion_style_ = val;
854}
855
845/* BUFFER_CEILING_OF (resp. BUFFER_FLOOR_OF), when applied to n, return 856/* BUFFER_CEILING_OF (resp. BUFFER_FLOOR_OF), when applied to n, return
846 the max (resp. min) p such that 857 the max (resp. min) p such that
847 858
diff --git a/src/emacs.c b/src/emacs.c
index d7de3c85bbe..2f953510a3d 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -2000,6 +2000,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
2000#ifdef HAVE_WINDOW_SYSTEM 2000#ifdef HAVE_WINDOW_SYSTEM
2001 init_fringe_once (); /* Swap bitmaps if necessary. */ 2001 init_fringe_once (); /* Swap bitmaps if necessary. */
2002#endif /* HAVE_WINDOW_SYSTEM */ 2002#endif /* HAVE_WINDOW_SYSTEM */
2003#ifdef HAVE_TEXT_CONVERSION
2004 syms_of_textconv ();
2005#endif
2003 } 2006 }
2004 2007
2005 init_alloc (); 2008 init_alloc ();
diff --git a/src/frame.h b/src/frame.h
index 27484936ff1..ca4cca17d74 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -86,7 +86,7 @@ enum text_conversion_operation
86 TEXTCONV_FINISH_COMPOSING_TEXT, 86 TEXTCONV_FINISH_COMPOSING_TEXT,
87 TEXTCONV_SET_COMPOSING_TEXT, 87 TEXTCONV_SET_COMPOSING_TEXT,
88 TEXTCONV_SET_COMPOSING_REGION, 88 TEXTCONV_SET_COMPOSING_REGION,
89 TEXTCONV_SET_POINT, 89 TEXTCONV_SET_POINT_AND_MARK,
90 TEXTCONV_DELETE_SURROUNDING_TEXT, 90 TEXTCONV_DELETE_SURROUNDING_TEXT,
91 }; 91 };
92 92
diff --git a/src/lisp.h b/src/lisp.h
index 2dc51d32481..a39ca8cc541 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -5234,6 +5234,7 @@ extern void reset_frame_state (struct frame *);
5234extern void report_selected_window_change (struct frame *); 5234extern void report_selected_window_change (struct frame *);
5235extern void report_point_change (struct frame *, struct window *, 5235extern void report_point_change (struct frame *, struct window *,
5236 struct buffer *); 5236 struct buffer *);
5237extern void syms_of_textconv (void);
5237#endif 5238#endif
5238 5239
5239#ifdef HAVE_NATIVE_COMP 5240#ifdef HAVE_NATIVE_COMP
diff --git a/src/marker.c b/src/marker.c
index e42c49a5434..7b15cd62f1e 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -23,6 +23,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
23#include "lisp.h" 23#include "lisp.h"
24#include "character.h" 24#include "character.h"
25#include "buffer.h" 25#include "buffer.h"
26#include "window.h"
26 27
27/* Record one cached position found recently by 28/* Record one cached position found recently by
28 buf_charpos_to_bytepos or buf_bytepos_to_charpos. */ 29 buf_charpos_to_bytepos or buf_bytepos_to_charpos. */
@@ -566,6 +567,31 @@ set_marker_internal (Lisp_Object marker, Lisp_Object position,
566 567
567 attach_marker (m, b, charpos, bytepos); 568 attach_marker (m, b, charpos, bytepos);
568 } 569 }
570
571#ifdef HAVE_TEXT_CONVERSION
572
573 /* If B is the buffer's mark and there is a window displaying B, and
574 text conversion is enabled while the mark is active, redisplay
575 the buffer.
576
577 propagate_window_redisplay will propagate this redisplay to the
578 window, which will eventually reach
579 mark_window_display_accurate_1. At that point,
580 report_point_change will be told to update the mark as seen by
581 the input method.
582
583 This is done all the way in (the seemingly irrelevant) redisplay
584 because the selection reported to the input method is actually what
585 is visible on screen, namely w->last_point. */
586
587 if (m->buffer
588 && EQ (marker, BVAR (m->buffer, mark))
589 && !NILP (BVAR (m->buffer, mark_active))
590 && buffer_window_count (m->buffer))
591 bset_redisplay (m->buffer);
592
593#endif
594
569 return marker; 595 return marker;
570} 596}
571 597
diff --git a/src/textconv.c b/src/textconv.c
index a39748457d3..835d03f3037 100644
--- a/src/textconv.c
+++ b/src/textconv.c
@@ -89,13 +89,27 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
89 size = end0 - beg0; 89 size = end0 - beg0;
90 memcpy (buffer, BYTE_POS_ADDR (beg0), size); 90 memcpy (buffer, BYTE_POS_ADDR (beg0), size);
91 if (beg1 != -1) 91 if (beg1 != -1)
92 memcpy (buffer, BEG_ADDR + beg1, end1 - beg1); 92 memcpy (buffer + size, BEG_ADDR + beg1, end1 - beg1);
93} 93}
94 94
95 95
96 96
97/* Conversion query. */ 97/* Conversion query. */
98 98
99/* Return the position of the active mark, or -1 if there is no mark
100 or it is not active. */
101
102static ptrdiff_t
103get_mark (void)
104{
105 if (!NILP (BVAR (current_buffer, mark_active))
106 && XMARKER (BVAR (current_buffer, mark))->buffer)
107 return marker_position (BVAR (current_buffer,
108 mark));
109
110 return -1;
111}
112
99/* Perform the text conversion operation specified in QUERY and return 113/* Perform the text conversion operation specified in QUERY and return
100 the results. 114 the results.
101 115
@@ -109,6 +123,9 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
109 If FLAGS & TEXTCONV_SKIP_CONVERSION_REGION, then first move PT past 123 If FLAGS & TEXTCONV_SKIP_CONVERSION_REGION, then first move PT past
110 the conversion region in the specified direction if it is inside. 124 the conversion region in the specified direction if it is inside.
111 125
126 If FLAGS & TEXTCONV_SKIP_ACTIVE_REGION, then also move PT past the
127 region if the mark is active.
128
112 Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION 129 Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION
113 or if deleting the text was successful, and 1 otherwise. */ 130 or if deleting the text was successful, and 1 otherwise. */
114 131
@@ -169,6 +186,39 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query,
169 } 186 }
170 } 187 }
171 188
189 /* Likewise for the region if the mark is active. */
190
191 if (flags & TEXTCONV_SKIP_ACTIVE_REGION)
192 {
193 temp = get_mark ();
194
195 if (temp == -1)
196 goto escape;
197
198 start = min (temp, PT);
199 end = max (temp, PT);
200
201 if (pos >= start && pos < end)
202 {
203 switch (query->direction)
204 {
205 case TEXTCONV_FORWARD_CHAR:
206 case TEXTCONV_FORWARD_WORD:
207 case TEXTCONV_CARET_DOWN:
208 case TEXTCONV_NEXT_LINE:
209 case TEXTCONV_LINE_START:
210 pos = end;
211 break;
212
213 default:
214 pos = max (BEGV, start);
215 break;
216 }
217 }
218 }
219
220 escape:
221
172 /* If pos is outside the accessible part of the buffer or if it 222 /* If pos is outside the accessible part of the buffer or if it
173 overflows, move back to point or to the extremes of the 223 overflows, move back to point or to the extremes of the
174 accessible region. */ 224 accessible region. */
@@ -335,6 +385,55 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query,
335 return 0; 385 return 0;
336} 386}
337 387
388/* Update the overlay displaying the conversion area on F after a
389 change to the conversion region. */
390
391static void
392sync_overlay (struct frame *f)
393{
394 if (MARKERP (f->conversion.compose_region_start))
395 {
396 if (NILP (f->conversion.compose_region_overlay))
397 {
398 f->conversion.compose_region_overlay
399 = Fmake_overlay (f->conversion.compose_region_start,
400 f->conversion.compose_region_end, Qnil,
401 Qt, Qnil);
402 Foverlay_put (f->conversion.compose_region_overlay,
403 Qface, Qunderline);
404 }
405
406 Fmove_overlay (f->conversion.compose_region_overlay,
407 f->conversion.compose_region_start,
408 f->conversion.compose_region_end, Qnil);
409 }
410 else if (!NILP (f->conversion.compose_region_overlay))
411 {
412 Fdelete_overlay (f->conversion.compose_region_overlay);
413 f->conversion.compose_region_overlay = Qnil;
414 }
415}
416
417/* Record a change to the current buffer as a result of an
418 asynchronous text conversion operation on F.
419
420 Consult the doc string of `text-conversion-edits' for the meaning
421 of BEG, END, and EPHEMERAL. */
422
423static void
424record_buffer_change (ptrdiff_t beg, ptrdiff_t end,
425 Lisp_Object ephemeral)
426{
427 Lisp_Object buffer;
428
429 XSETBUFFER (buffer, current_buffer);
430
431 Vtext_conversion_edits
432 = Fcons (list4 (buffer, make_fixnum (beg),
433 make_fixnum (end), ephemeral),
434 Vtext_conversion_edits);
435}
436
338/* Reset F's text conversion state. Delete any overlays or 437/* Reset F's text conversion state. Delete any overlays or
339 markers inside. */ 438 markers inside. */
340 439
@@ -438,8 +537,10 @@ really_commit_text (struct frame *f, EMACS_INT position,
438 /* Replace its contents. */ 537 /* Replace its contents. */
439 start = marker_position (f->conversion.compose_region_start); 538 start = marker_position (f->conversion.compose_region_start);
440 end = marker_position (f->conversion.compose_region_end); 539 end = marker_position (f->conversion.compose_region_end);
441 safe_del_range (start, end); 540 del_range (start, end);
541 record_buffer_change (start, start, Qnil);
442 Finsert (1, &text); 542 Finsert (1, &text);
543 record_buffer_change (start, PT, text);
443 544
444 /* Move to a the position specified in POSITION. */ 545 /* Move to a the position specified in POSITION. */
445 546
@@ -493,6 +594,7 @@ really_commit_text (struct frame *f, EMACS_INT position,
493 location. */ 594 location. */
494 wanted = PT; 595 wanted = PT;
495 Finsert (1, &text); 596 Finsert (1, &text);
597 record_buffer_change (wanted, PT, text);
496 598
497 if (position < 0) 599 if (position < 0)
498 { 600 {
@@ -520,6 +622,8 @@ really_commit_text (struct frame *f, EMACS_INT position,
520 } 622 }
521 } 623 }
522 624
625 /* This should deactivate the mark. */
626 call0 (Qdeactivate_mark);
523 unbind_to (count, Qnil); 627 unbind_to (count, Qnil);
524} 628}
525 629
@@ -575,12 +679,11 @@ really_set_composing_text (struct frame *f, ptrdiff_t position,
575 679
576 if (!MARKERP (f->conversion.compose_region_start)) 680 if (!MARKERP (f->conversion.compose_region_start))
577 { 681 {
578 f->conversion.compose_region_start = Fmake_marker (); 682 f->conversion.compose_region_start
579 f->conversion.compose_region_end = Fmake_marker (); 683 = build_marker (current_buffer, PT, PT_BYTE);
580 Fset_marker (f->conversion.compose_region_start, 684 f->conversion.compose_region_end
581 Fpoint (), Qnil); 685 = build_marker (current_buffer, PT, PT_BYTE);
582 Fset_marker (f->conversion.compose_region_end, 686
583 Fpoint (), Qnil);
584 Fset_marker_insertion_type (f->conversion.compose_region_end, 687 Fset_marker_insertion_type (f->conversion.compose_region_end,
585 Qt); 688 Qt);
586 689
@@ -595,13 +698,19 @@ really_set_composing_text (struct frame *f, ptrdiff_t position,
595 Fwiden (); 698 Fwiden ();
596 start = marker_position (f->conversion.compose_region_start); 699 start = marker_position (f->conversion.compose_region_start);
597 end = marker_position (f->conversion.compose_region_end); 700 end = marker_position (f->conversion.compose_region_end);
598 safe_del_range (start, end); 701 del_range (start, end);
599 set_point (start); 702 set_point (start);
703
704 if (start != end)
705 record_buffer_change (start, start, Qnil);
600 } 706 }
601 707
602 /* Insert the new text. */ 708 /* Insert the new text. */
603 Finsert (1, &text); 709 Finsert (1, &text);
604 710
711 if (start != PT)
712 record_buffer_change (start, PT, Qnil);
713
605 /* Now move point to an appropriate location. */ 714 /* Now move point to an appropriate location. */
606 if (position < 0) 715 if (position < 0)
607 { 716 {
@@ -632,6 +741,12 @@ really_set_composing_text (struct frame *f, ptrdiff_t position,
632 741
633 set_point (wanted); 742 set_point (wanted);
634 743
744 /* This should deactivate the mark. */
745 call0 (Qdeactivate_mark);
746
747 /* Move the composition overlay. */
748 sync_overlay (f);
749
635 /* If PT hasn't changed, the conversion region definitely has. 750 /* If PT hasn't changed, the conversion region definitely has.
636 Otherwise, redisplay will update the input method instead. */ 751 Otherwise, redisplay will update the input method instead. */
637 752
@@ -693,18 +808,21 @@ really_set_composing_region (struct frame *f, ptrdiff_t start,
693 make_fixnum (start), Qnil); 808 make_fixnum (start), Qnil);
694 Fset_marker (f->conversion.compose_region_end, 809 Fset_marker (f->conversion.compose_region_end,
695 make_fixnum (end), Qnil); 810 make_fixnum (end), Qnil);
811 sync_overlay (f);
696 812
697 unbind_to (count, Qnil); 813 unbind_to (count, Qnil);
698} 814}
699 815
700/* Delete LEFT and RIGHT chars around point. */ 816/* Delete LEFT and RIGHT chars around point or the active mark,
817 whichever is larger, avoiding the composing region if
818 necessary. */
701 819
702static void 820static void
703really_delete_surrounding_text (struct frame *f, ptrdiff_t left, 821really_delete_surrounding_text (struct frame *f, ptrdiff_t left,
704 ptrdiff_t right) 822 ptrdiff_t right)
705{ 823{
706 specpdl_ref count; 824 specpdl_ref count;
707 ptrdiff_t start, end; 825 ptrdiff_t start, end, a, b, a1, b1, lstart, rstart;
708 826
709 /* If F's old selected window is no longer live, fail. */ 827 /* If F's old selected window is no longer live, fail. */
710 828
@@ -719,21 +837,71 @@ really_delete_surrounding_text (struct frame *f, ptrdiff_t left,
719 redisplay. */ 837 redisplay. */
720 Fselect_window (f->old_selected_window, Qt); 838 Fselect_window (f->old_selected_window, Qt);
721 839
722 start = max (BEGV, PT - left); 840 /* Figure out where to start deleting from. */
723 end = min (ZV, PT + right); 841
842 a = get_mark ();
843
844 if (a != -1 && a != PT)
845 lstart = rstart = max (a, PT);
846 else
847 lstart = rstart = PT;
848
849 /* Avoid the composing text. This behavior is identical to how
850 Android's BaseInputConnection actually implements avoiding the
851 composing span. */
852
853 if (MARKERP (f->conversion.compose_region_start))
854 {
855 a = marker_position (f->conversion.compose_region_start);
856 b = marker_position (f->conversion.compose_region_end);
857
858 a1 = min (a, b);
859 b1 = max (a, b);
860
861 lstart = min (lstart, min (PT, a1));
862 rstart = max (rstart, max (PT, b1));
863 }
864
865 if (lstart == rstart)
866 {
867 start = max (BEGV, lstart - left);
868 end = min (ZV, rstart + right);
869
870 del_range (start, end);
871 record_buffer_change (start, start, Qnil);
872 }
873 else
874 {
875 start = max (BEGV, lstart - left);
876 end = lstart;
877
878 del_range (start, end);
879 record_buffer_change (start, start, Qnil);
880
881 start = rstart;
882 end = min (ZV, rstart + right);
883 del_range (start, end);
884 record_buffer_change (start, start, Qnil);
885 }
886
887 /* if the mark is now equal to start, deactivate it. */
888
889 if (get_mark () == PT)
890 call0 (Qdeactivate_mark);
724 891
725 safe_del_range (start, end);
726 unbind_to (count, Qnil); 892 unbind_to (count, Qnil);
727} 893}
728 894
729/* Set point in F to POSITION. 895/* Set point in F to POSITION. If MARK is not POSITION, activate the
896 mark and set MARK to that as well.
730 897
731 If it has not changed, signal an update through the text input 898 If it has not changed, signal an update through the text input
732 interface, which is necessary for the IME to acknowledge that the 899 interface, which is necessary for the IME to acknowledge that the
733 change has completed. */ 900 change has completed. */
734 901
735static void 902static void
736really_set_point (struct frame *f, ptrdiff_t point) 903really_set_point_and_mark (struct frame *f, ptrdiff_t point,
904 ptrdiff_t mark)
737{ 905{
738 specpdl_ref count; 906 specpdl_ref count;
739 907
@@ -763,6 +931,11 @@ really_set_point (struct frame *f, ptrdiff_t point)
763 /* Set the point. */ 931 /* Set the point. */
764 Fgoto_char (make_fixnum (point)); 932 Fgoto_char (make_fixnum (point));
765 933
934 if (mark == point && BVAR (current_buffer, mark_active))
935 call0 (Qdeactivate_mark);
936 else
937 call1 (Qpush_mark, make_fixnum (mark));
938
766 unbind_to (count, Qnil); 939 unbind_to (count, Qnil);
767} 940}
768 941
@@ -845,8 +1018,9 @@ handle_pending_conversion_events_1 (struct frame *f,
845 XFIXNUM (XCDR (data))); 1018 XFIXNUM (XCDR (data)));
846 break; 1019 break;
847 1020
848 case TEXTCONV_SET_POINT: 1021 case TEXTCONV_SET_POINT_AND_MARK:
849 really_set_point (f, XFIXNUM (data)); 1022 really_set_point_and_mark (f, XFIXNUM (XCAR (data)),
1023 XFIXNUM (XCDR (data)));
850 break; 1024 break;
851 1025
852 case TEXTCONV_DELETE_SURROUNDING_TEXT: 1026 case TEXTCONV_DELETE_SURROUNDING_TEXT:
@@ -858,6 +1032,17 @@ handle_pending_conversion_events_1 (struct frame *f,
858 unbind_to (count, Qnil); 1032 unbind_to (count, Qnil);
859} 1033}
860 1034
1035/* Decrement the variable pointed to by *PTR. */
1036
1037static void
1038decrement_inside (void *ptr)
1039{
1040 int *i;
1041
1042 i = ptr;
1043 (*i)--;
1044}
1045
861/* Process any outstanding text conversion events. 1046/* Process any outstanding text conversion events.
862 This may run Lisp or signal. */ 1047 This may run Lisp or signal. */
863 1048
@@ -868,9 +1053,22 @@ handle_pending_conversion_events (void)
868 Lisp_Object tail, frame; 1053 Lisp_Object tail, frame;
869 struct text_conversion_action *action, *next; 1054 struct text_conversion_action *action, *next;
870 bool handled; 1055 bool handled;
1056 static int inside;
1057 specpdl_ref count;
871 1058
872 handled = false; 1059 handled = false;
873 1060
1061 /* Reset Vtext_conversion_edits. Do not do this if called
1062 reentrantly. */
1063
1064 if (!inside)
1065 Vtext_conversion_edits = Qnil;
1066
1067 inside++;
1068
1069 count = SPECPDL_INDEX ();
1070 record_unwind_protect_ptr (decrement_inside, &inside);
1071
874 FOR_EACH_FRAME (tail, frame) 1072 FOR_EACH_FRAME (tail, frame)
875 { 1073 {
876 f = XFRAME (frame); 1074 f = XFRAME (frame);
@@ -905,6 +1103,8 @@ handle_pending_conversion_events (void)
905 handled = true; 1103 handled = true;
906 } 1104 }
907 } 1105 }
1106
1107 unbind_to (count, Qnil);
908} 1108}
909 1109
910/* Start a ``batch edit'' in F. During a batch edit, point_changed 1110/* Start a ``batch edit'' in F. During a batch edit, point_changed
@@ -1057,13 +1257,13 @@ set_composing_region (struct frame *f, ptrdiff_t start,
1057 input_pending = true; 1257 input_pending = true;
1058} 1258}
1059 1259
1060/* Move point in F's selected buffer to POINT. 1260/* Move point in F's selected buffer to POINT and maybe push MARK.
1061 1261
1062 COUNTER means the same as in `start_batch_edit'. */ 1262 COUNTER means the same as in `start_batch_edit'. */
1063 1263
1064void 1264void
1065textconv_set_point (struct frame *f, ptrdiff_t point, 1265textconv_set_point_and_mark (struct frame *f, ptrdiff_t point,
1066 unsigned long counter) 1266 ptrdiff_t mark, unsigned long counter)
1067{ 1267{
1068 struct text_conversion_action *action, **last; 1268 struct text_conversion_action *action, **last;
1069 1269
@@ -1071,8 +1271,9 @@ textconv_set_point (struct frame *f, ptrdiff_t point,
1071 point = MOST_POSITIVE_FIXNUM; 1271 point = MOST_POSITIVE_FIXNUM;
1072 1272
1073 action = xmalloc (sizeof *action); 1273 action = xmalloc (sizeof *action);
1074 action->operation = TEXTCONV_SET_POINT; 1274 action->operation = TEXTCONV_SET_POINT_AND_MARK;
1075 action->data = make_fixnum (point); 1275 action->data = Fcons (make_fixnum (point),
1276 make_fixnum (mark));
1076 action->next = NULL; 1277 action->next = NULL;
1077 action->counter = counter; 1278 action->counter = counter;
1078 for (last = &f->conversion.actions; *last; last = &(*last)->next) 1279 for (last = &f->conversion.actions; *last; last = &(*last)->next)
@@ -1105,6 +1306,9 @@ delete_surrounding_text (struct frame *f, ptrdiff_t left,
1105/* Return N characters of text around point in F's old selected 1306/* Return N characters of text around point in F's old selected
1106 window. 1307 window.
1107 1308
1309 If N is -1, return the text between point and mark instead, given
1310 that the mark is active.
1311
1108 Set *N to the actual number of characters returned, *START_RETURN 1312 Set *N to the actual number of characters returned, *START_RETURN
1109 to the position of the first character returned, *OFFSET to the 1313 to the position of the first character returned, *OFFSET to the
1110 offset of point within that text, *LENGTH to the actual number of 1314 offset of point within that text, *LENGTH to the actual number of
@@ -1137,13 +1341,38 @@ get_extracted_text (struct frame *f, ptrdiff_t n,
1137 /* Temporarily switch to F's selected window at the time of the last 1341 /* Temporarily switch to F's selected window at the time of the last
1138 redisplay. */ 1342 redisplay. */
1139 Fselect_window (f->old_selected_window, Qt); 1343 Fselect_window (f->old_selected_window, Qt);
1344 buffer = NULL;
1140 1345
1141 /* Figure out the bounds of the text to return. */ 1346 /* Figure out the bounds of the text to return. */
1142 start = PT - n / 2; 1347 if (n != -1)
1143 end = PT + n - n / 2; 1348 {
1349 start = PT - n / 2;
1350 end = PT + n - n / 2;
1351 }
1352 else
1353 {
1354 if (!NILP (BVAR (current_buffer, mark_active))
1355 && XMARKER (BVAR (current_buffer, mark))->buffer)
1356 {
1357 start = marker_position (BVAR (current_buffer, mark));
1358 end = PT;
1359
1360 /* Sort start and end. start_byte is used to hold a
1361 temporary value. */
1362
1363 if (start > end)
1364 {
1365 start_byte = end;
1366 end = start;
1367 start = start_byte;
1368 }
1369 }
1370 else
1371 goto finish;
1372 }
1373
1144 start = max (start, BEGV); 1374 start = max (start, BEGV);
1145 end = min (end, ZV); 1375 end = min (end, ZV);
1146 buffer = NULL;
1147 1376
1148 /* Detect overflow. */ 1377 /* Detect overflow. */
1149 1378
@@ -1218,3 +1447,37 @@ register_textconv_interface (struct textconv_interface *interface)
1218{ 1447{
1219 text_interface = interface; 1448 text_interface = interface;
1220} 1449}
1450
1451
1452
1453void
1454syms_of_textconv (void)
1455{
1456 DEFSYM (Qaction, "action");
1457 DEFSYM (Qtext_conversion, "text-conversion");
1458 DEFSYM (Qpush_mark, "push-mark");
1459 DEFSYM (Qunderline, "underline");
1460
1461 DEFVAR_LISP ("text-conversion-edits", Vtext_conversion_edits,
1462 doc: /* List of buffers that were last edited as a result of text conversion.
1463
1464This list can be used while handling a `text-conversion' event to
1465determine the changes which have taken place.
1466
1467Each element of the list describes a single edit in a buffer, of the
1468form:
1469
1470 (BUFFER BEG END EPHEMERAL)
1471
1472If an insertion or a change occured, then BEG and END are buffer
1473positions denote the bounds of the text that was changed or inserted.
1474If EPHEMERAL is t, then the input method will shortly make more
1475changes to the text, so any actions that would otherwise be taken
1476(such as indenting or automatically filling text) should not take
1477place; otherwise, it is a string describing the text which was
1478inserted.
1479
1480If a deletion occured, then BEG and END are the same, and EPHEMERAL is
1481nil. */);
1482 Vtext_conversion_edits = Qnil;
1483}
diff --git a/src/textconv.h b/src/textconv.h
index ce003847637..034c663521a 100644
--- a/src/textconv.h
+++ b/src/textconv.h
@@ -35,9 +35,10 @@ struct textconv_interface
35 unexpected buffer change occurs.) */ 35 unexpected buffer change occurs.) */
36 void (*reset) (struct frame *); 36 void (*reset) (struct frame *);
37 37
38 /* Notice that point has moved in the specified frame's selected 38 /* Notice that point or mark has moved in the specified frame's
39 window's selected buffer. The second argument is the window 39 selected window's selected buffer. The second argument is the
40 whose point changed, and the third argument is the buffer. */ 40 window whose point changed, and the third argument is the
41 buffer. */
41 void (*point_changed) (struct frame *, struct window *, 42 void (*point_changed) (struct frame *, struct window *,
42 struct buffer *); 43 struct buffer *);
43 44
@@ -118,6 +119,7 @@ struct textconv_callback_struct
118}; 119};
119 120
120#define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0) 121#define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0)
122#define TEXTCONV_SKIP_ACTIVE_REGION (1 << 1)
121 123
122extern int textconv_query (struct frame *, struct textconv_callback_struct *, 124extern int textconv_query (struct frame *, struct textconv_callback_struct *,
123 int); 125 int);
@@ -132,7 +134,8 @@ extern void set_composing_text (struct frame *, Lisp_Object,
132 ptrdiff_t, unsigned long); 134 ptrdiff_t, unsigned long);
133extern void set_composing_region (struct frame *, ptrdiff_t, ptrdiff_t, 135extern void set_composing_region (struct frame *, ptrdiff_t, ptrdiff_t,
134 unsigned long); 136 unsigned long);
135extern void textconv_set_point (struct frame *, ptrdiff_t, unsigned long); 137extern void textconv_set_point_and_mark (struct frame *, ptrdiff_t,
138 ptrdiff_t, unsigned long);
136extern void delete_surrounding_text (struct frame *, ptrdiff_t, 139extern void delete_surrounding_text (struct frame *, ptrdiff_t,
137 ptrdiff_t, unsigned long); 140 ptrdiff_t, unsigned long);
138extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *, 141extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
diff --git a/src/window.h b/src/window.h
index 32b5fe14f4f..463c7f89b9b 100644
--- a/src/window.h
+++ b/src/window.h
@@ -286,6 +286,10 @@ struct window
286 it should be positive. */ 286 it should be positive. */
287 ptrdiff_t last_point; 287 ptrdiff_t last_point;
288 288
289 /* Value of mark in the selected window at the time of the last
290 redisplay. */
291 ptrdiff_t last_mark;
292
289 /* Line number and position of a line somewhere above the top of the 293 /* Line number and position of a line somewhere above the top of the
290 screen. If this field is zero, it means we don't have a base line. */ 294 screen. If this field is zero, it means we don't have a base line. */
291 ptrdiff_t base_line_number; 295 ptrdiff_t base_line_number;
diff --git a/src/xdisp.c b/src/xdisp.c
index 4b91fcf31ca..525eaa64b3a 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -17268,7 +17268,7 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p)
17268{ 17268{
17269 struct buffer *b = XBUFFER (w->contents); 17269 struct buffer *b = XBUFFER (w->contents);
17270#ifdef HAVE_TEXT_CONVERSION 17270#ifdef HAVE_TEXT_CONVERSION
17271 ptrdiff_t prev_point; 17271 ptrdiff_t prev_point, prev_mark;
17272#endif 17272#endif
17273 17273
17274 w->last_modified = accurate_p ? BUF_MODIFF (b) : 0; 17274 w->last_modified = accurate_p ? BUF_MODIFF (b) : 0;
@@ -17301,6 +17301,7 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p)
17301 17301
17302#ifdef HAVE_TEXT_CONVERSION 17302#ifdef HAVE_TEXT_CONVERSION
17303 prev_point = w->last_point; 17303 prev_point = w->last_point;
17304 prev_mark = w->last_mark;
17304#endif 17305#endif
17305 17306
17306 if (w == XWINDOW (selected_window)) 17307 if (w == XWINDOW (selected_window))
@@ -17308,6 +17309,11 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p)
17308 else 17309 else
17309 w->last_point = marker_position (w->pointm); 17310 w->last_point = marker_position (w->pointm);
17310 17311
17312 if (XMARKER (BVAR (b, mark))->buffer == b)
17313 w->last_mark = marker_position (BVAR (b, mark));
17314 else
17315 w->last_mark = -1;
17316
17311#ifdef HAVE_TEXT_CONVERSION 17317#ifdef HAVE_TEXT_CONVERSION
17312 /* Point motion is only propagated to the input method for use 17318 /* Point motion is only propagated to the input method for use
17313 in text conversion during a redisplay. While this can lead 17319 in text conversion during a redisplay. While this can lead
@@ -17323,7 +17329,8 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p)
17323 during such a change, so the consequences are not that 17329 during such a change, so the consequences are not that
17324 severe. */ 17330 severe. */
17325 17331
17326 if (prev_point != w->last_point 17332 if ((prev_point != w->last_point
17333 || prev_mark != w->last_mark)
17327 && FRAME_WINDOW_P (WINDOW_XFRAME (w)) 17334 && FRAME_WINDOW_P (WINDOW_XFRAME (w))
17328 && w == XWINDOW (WINDOW_XFRAME (w)->selected_window)) 17335 && w == XWINDOW (WINDOW_XFRAME (w)->selected_window))
17329 report_point_change (WINDOW_XFRAME (w), w, b); 17336 report_point_change (WINDOW_XFRAME (w), w, b);