aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPo Lu2023-02-15 22:51:44 +0800
committerPo Lu2023-02-15 22:51:44 +0800
commitcf24b61985c26cbf2e5a24cb0b64a8528aa3a9cc (patch)
treeb69f8dbb50e3e2f6f09caa05aecbee5241876f62
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.
-rw-r--r--doc/emacs/input.texi3
-rw-r--r--doc/lispref/commands.texi48
-rw-r--r--java/org/gnu/emacs/EmacsInputConnection.java68
-rw-r--r--java/org/gnu/emacs/EmacsService.java14
-rw-r--r--lisp/bindings.el2
-rw-r--r--lisp/electric.el1
-rw-r--r--lisp/progmodes/prog-mode.el2
-rw-r--r--lisp/simple.el52
-rw-r--r--lisp/textmodes/text-mode.el6
-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
20 files changed, 607 insertions, 81 deletions
diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi
index 2463a75edcd..154b7025ff4 100644
--- a/doc/emacs/input.texi
+++ b/doc/emacs/input.texi
@@ -121,6 +121,9 @@ screen, and send the appropriate key events to Emacs after completion.
121 However, on screen keyboard input methods directly perform edits to 121 However, on screen keyboard input methods directly perform edits to
122the selected window of each frame; this is known as ``text 122the selected window of each frame; this is known as ``text
123conversion'', or ``string conversion'' under the X Window System. 123conversion'', or ``string conversion'' under the X Window System.
124Emacs enables these input methods whenever the buffer local value of
125@code{text-conversion-style} is non-@code{nil}, normally inside
126derivatives of @code{text-mode} and @code{prog-mode}.
124 127
125 Text conversion is performed asynchronously whenever Emacs receives 128 Text conversion is performed asynchronously whenever Emacs receives
126a request to perform the conversion from the input method. After the 129a request to perform the conversion from the input method. After the
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index 2807d3d61b2..5fb2217a03b 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -2205,9 +2205,51 @@ A few other event types represent occurrences within the system.
2205This kind of event is sent @strong{after} a system-wide input method 2205This kind of event is sent @strong{after} a system-wide input method
2206performs an edit to one or more buffers. 2206performs an edit to one or more buffers.
2207 2207
2208Once the event is sent, the input method may already have made changes 2208@vindex text-conversion-edits
2209to multiple frames. @c TODO: allow querying which frames to which 2209Once the event is sent, the input method may already have made
2210@c changes have been made. 2210changes to multiple buffers inside many different frames. To
2211determine which buffers have been changed, and what edits have
2212been made to them, use the variable
2213@code{text-conversion-edits}, which is set prior to each
2214@code{text-conversion} event being sent; it is a list of the
2215form:
2216
2217@indentedblock
2218@w{@code{(@var{buffer} @var{beg} @var{end} @var{ephemeral})}}
2219@end indentedblock
2220
2221Where @var{ephemeral} is the buffer which was modified,
2222@var{beg} and @var{end} are the positions of the edit at the
2223time it was completed, and @var{ephemeral} is either a string,
2224containing any text which was inserted, @code{t}, meaning that
2225the edit is a temporary edit made by the input method, and
2226@code{nil}, meaning that some text was deleted.
2227
2228@vindex text-conversion-style
2229Whether or not this event is sent depends on the value of the
2230buffer-local variable @code{text-conversion-style}, which determines
2231how an input method that wishes to make edits to buffer contents will
2232behave.
2233
2234This variable can have one three values:
2235
2236@table @code
2237@item nil
2238This means that the input method will be disabled entirely, and key
2239events will be sent instead of text conversion events.
2240
2241@item action
2242This means that the input method will be enabled, but @key{RET} will
2243be sent wherever the input method wanted to insert a new line.
2244
2245@item t
2246This, or any other value, means that the input method will be enabled
2247and make edits terminated by @code{text-conversion} events.
2248@end itemize
2249
2250Changes to the value of this variable will only take effect upon
2251the next redisplay after the buffer becomes the selected buffer
2252of a frame.
2211 2253
2212@cindex @code{delete-frame} event 2254@cindex @code{delete-frame} event
2213@item (delete-frame (@var{frame})) 2255@item (delete-frame (@var{frame}))
diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java
index 3cf4419838b..5eb56d5aa71 100644
--- a/java/org/gnu/emacs/EmacsInputConnection.java
+++ b/java/org/gnu/emacs/EmacsInputConnection.java
@@ -55,7 +55,9 @@ public class EmacsInputConnection extends BaseInputConnection
55 public boolean 55 public boolean
56 beginBatchEdit () 56 beginBatchEdit ()
57 { 57 {
58 Log.d (TAG, "beginBatchEdit"); 58 if (EmacsService.DEBUG_IC)
59 Log.d (TAG, "beginBatchEdit");
60
59 EmacsNative.beginBatchEdit (windowHandle); 61 EmacsNative.beginBatchEdit (windowHandle);
60 return true; 62 return true;
61 } 63 }
@@ -64,7 +66,9 @@ public class EmacsInputConnection extends BaseInputConnection
64 public boolean 66 public boolean
65 endBatchEdit () 67 endBatchEdit ()
66 { 68 {
67 Log.d (TAG, "endBatchEdit"); 69 if (EmacsService.DEBUG_IC)
70 Log.d (TAG, "endBatchEdit");
71
68 EmacsNative.endBatchEdit (windowHandle); 72 EmacsNative.endBatchEdit (windowHandle);
69 return true; 73 return true;
70 } 74 }
@@ -73,7 +77,9 @@ public class EmacsInputConnection extends BaseInputConnection
73 public boolean 77 public boolean
74 commitCompletion (CompletionInfo info) 78 commitCompletion (CompletionInfo info)
75 { 79 {
76 Log.d (TAG, "commitCompletion: " + info); 80 if (EmacsService.DEBUG_IC)
81 Log.d (TAG, "commitCompletion: " + info);
82
77 EmacsNative.commitCompletion (windowHandle, 83 EmacsNative.commitCompletion (windowHandle,
78 info.getText ().toString (), 84 info.getText ().toString (),
79 info.getPosition ()); 85 info.getPosition ());
@@ -84,7 +90,9 @@ public class EmacsInputConnection extends BaseInputConnection
84 public boolean 90 public boolean
85 commitText (CharSequence text, int newCursorPosition) 91 commitText (CharSequence text, int newCursorPosition)
86 { 92 {
87 Log.d (TAG, "commitText: " + text + " " + newCursorPosition); 93 if (EmacsService.DEBUG_IC)
94 Log.d (TAG, "commitText: " + text + " " + newCursorPosition);
95
88 EmacsNative.commitText (windowHandle, text.toString (), 96 EmacsNative.commitText (windowHandle, text.toString (),
89 newCursorPosition); 97 newCursorPosition);
90 return true; 98 return true;
@@ -94,8 +102,10 @@ public class EmacsInputConnection extends BaseInputConnection
94 public boolean 102 public boolean
95 deleteSurroundingText (int leftLength, int rightLength) 103 deleteSurroundingText (int leftLength, int rightLength)
96 { 104 {
97 Log.d (TAG, ("deleteSurroundingText: " 105 if (EmacsService.DEBUG_IC)
98 + leftLength + " " + rightLength)); 106 Log.d (TAG, ("deleteSurroundingText: "
107 + leftLength + " " + rightLength));
108
99 EmacsNative.deleteSurroundingText (windowHandle, leftLength, 109 EmacsNative.deleteSurroundingText (windowHandle, leftLength,
100 rightLength); 110 rightLength);
101 return true; 111 return true;
@@ -105,7 +115,8 @@ public class EmacsInputConnection extends BaseInputConnection
105 public boolean 115 public boolean
106 finishComposingText () 116 finishComposingText ()
107 { 117 {
108 Log.d (TAG, "finishComposingText"); 118 if (EmacsService.DEBUG_IC)
119 Log.d (TAG, "finishComposingText");
109 120
110 EmacsNative.finishComposingText (windowHandle); 121 EmacsNative.finishComposingText (windowHandle);
111 return true; 122 return true;
@@ -115,7 +126,8 @@ public class EmacsInputConnection extends BaseInputConnection
115 public String 126 public String
116 getSelectedText (int flags) 127 getSelectedText (int flags)
117 { 128 {
118 Log.d (TAG, "getSelectedText: " + flags); 129 if (EmacsService.DEBUG_IC)
130 Log.d (TAG, "getSelectedText: " + flags);
119 131
120 return EmacsNative.getSelectedText (windowHandle, flags); 132 return EmacsNative.getSelectedText (windowHandle, flags);
121 } 133 }
@@ -124,27 +136,44 @@ public class EmacsInputConnection extends BaseInputConnection
124 public String 136 public String
125 getTextAfterCursor (int length, int flags) 137 getTextAfterCursor (int length, int flags)
126 { 138 {
127 Log.d (TAG, "getTextAfterCursor: " + length + " " + flags); 139 String string;
140
141 if (EmacsService.DEBUG_IC)
142 Log.d (TAG, "getTextAfterCursor: " + length + " " + flags);
128 143
129 return EmacsNative.getTextAfterCursor (windowHandle, length, 144 string = EmacsNative.getTextAfterCursor (windowHandle, length,
130 flags); 145 flags);
146
147 if (EmacsService.DEBUG_IC)
148 Log.d (TAG, " --> " + string);
149
150 return string;
131 } 151 }
132 152
133 @Override 153 @Override
134 public String 154 public String
135 getTextBeforeCursor (int length, int flags) 155 getTextBeforeCursor (int length, int flags)
136 { 156 {
137 Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags); 157 String string;
158
159 if (EmacsService.DEBUG_IC)
160 Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags);
161
162 string = EmacsNative.getTextBeforeCursor (windowHandle, length,
163 flags);
164
165 if (EmacsService.DEBUG_IC)
166 Log.d (TAG, " --> " + string);
138 167
139 return EmacsNative.getTextBeforeCursor (windowHandle, length, 168 return string;
140 flags);
141 } 169 }
142 170
143 @Override 171 @Override
144 public boolean 172 public boolean
145 setComposingText (CharSequence text, int newCursorPosition) 173 setComposingText (CharSequence text, int newCursorPosition)
146 { 174 {
147 Log.d (TAG, "setComposingText: " + newCursorPosition); 175 if (EmacsService.DEBUG_IC)
176 Log.d (TAG, "setComposingText: " + newCursorPosition);
148 177
149 EmacsNative.setComposingText (windowHandle, text.toString (), 178 EmacsNative.setComposingText (windowHandle, text.toString (),
150 newCursorPosition); 179 newCursorPosition);
@@ -155,7 +184,8 @@ public class EmacsInputConnection extends BaseInputConnection
155 public boolean 184 public boolean
156 setComposingRegion (int start, int end) 185 setComposingRegion (int start, int end)
157 { 186 {
158 Log.d (TAG, "setComposingRegion: " + start + " " + end); 187 if (EmacsService.DEBUG_IC)
188 Log.d (TAG, "setComposingRegion: " + start + " " + end);
159 189
160 EmacsNative.setComposingRegion (windowHandle, start, end); 190 EmacsNative.setComposingRegion (windowHandle, start, end);
161 return true; 191 return true;
@@ -165,7 +195,8 @@ public class EmacsInputConnection extends BaseInputConnection
165 public boolean 195 public boolean
166 performEditorAction (int editorAction) 196 performEditorAction (int editorAction)
167 { 197 {
168 Log.d (TAG, "performEditorAction: " + editorAction); 198 if (EmacsService.DEBUG_IC)
199 Log.d (TAG, "performEditorAction: " + editorAction);
169 200
170 EmacsNative.performEditorAction (windowHandle, editorAction); 201 EmacsNative.performEditorAction (windowHandle, editorAction);
171 return true; 202 return true;
@@ -175,7 +206,8 @@ public class EmacsInputConnection extends BaseInputConnection
175 public ExtractedText 206 public ExtractedText
176 getExtractedText (ExtractedTextRequest request, int flags) 207 getExtractedText (ExtractedTextRequest request, int flags)
177 { 208 {
178 Log.d (TAG, "getExtractedText: " + request + " " + flags); 209 if (EmacsService.DEBUG_IC)
210 Log.d (TAG, "getExtractedText: " + request + " " + flags);
179 211
180 return EmacsNative.getExtractedText (windowHandle, request, 212 return EmacsNative.getExtractedText (windowHandle, request,
181 flags); 213 flags);
diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java
index 855a738a30f..2acb3ead086 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -88,6 +88,8 @@ public class EmacsService extends Service
88 /* Display metrics used by font backends. */ 88 /* Display metrics used by font backends. */
89 public DisplayMetrics metrics; 89 public DisplayMetrics metrics;
90 90
91 public static final boolean DEBUG_IC = false;
92
91 @Override 93 @Override
92 public int 94 public int
93 onStartCommand (Intent intent, int flags, int startId) 95 onStartCommand (Intent intent, int flags, int startId)
@@ -612,10 +614,11 @@ public class EmacsService extends Service
612 int newSelectionEnd, int composingRegionStart, 614 int newSelectionEnd, int composingRegionStart,
613 int composingRegionEnd) 615 int composingRegionEnd)
614 { 616 {
615 Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart 617 if (DEBUG_IC)
616 + " " + newSelectionEnd + " " 618 Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart
617 + composingRegionStart + " " 619 + " " + newSelectionEnd + " "
618 + composingRegionEnd)); 620 + composingRegionStart + " "
621 + composingRegionEnd));
619 window.view.imManager.updateSelection (window.view, 622 window.view.imManager.updateSelection (window.view,
620 newSelectionStart, 623 newSelectionStart,
621 newSelectionEnd, 624 newSelectionEnd,
@@ -626,7 +629,8 @@ public class EmacsService extends Service
626 public void 629 public void
627 resetIC (EmacsWindow window, int icMode) 630 resetIC (EmacsWindow window, int icMode)
628 { 631 {
629 Log.d (TAG, "resetIC: " + window); 632 if (DEBUG_IC)
633 Log.d (TAG, "resetIC: " + window);
630 634
631 window.view.setICMode (icMode); 635 window.view.setICMode (icMode);
632 window.view.imManager.restartInput (window.view); 636 window.view.imManager.restartInput (window.view);
diff --git a/lisp/bindings.el b/lisp/bindings.el
index 057c870958d..eec51a4e413 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -1522,7 +1522,7 @@ if `inhibit-field-text-motion' is non-nil."
1522(define-key special-event-map [sigusr2] 'ignore) 1522(define-key special-event-map [sigusr2] 'ignore)
1523 1523
1524;; Text conversion 1524;; Text conversion
1525(define-key global-map [text-conversion] 'ignore) 1525(define-key global-map [text-conversion] 'analyze-text-conversion)
1526 1526
1527;; Don't look for autoload cookies in this file. 1527;; Don't look for autoload cookies in this file.
1528;; Local Variables: 1528;; Local Variables:
diff --git a/lisp/electric.el b/lisp/electric.el
index bac3f5a2b3c..3865d1d5234 100644
--- a/lisp/electric.el
+++ b/lisp/electric.el
@@ -294,6 +294,7 @@ or comment."
294 294
295;;;###autoload 295;;;###autoload
296(define-key global-map "\C-j" 'electric-newline-and-maybe-indent) 296(define-key global-map "\C-j" 'electric-newline-and-maybe-indent)
297
297;;;###autoload 298;;;###autoload
298(defun electric-newline-and-maybe-indent () 299(defun electric-newline-and-maybe-indent ()
299 "Insert a newline. 300 "Insert a newline.
diff --git a/lisp/progmodes/prog-mode.el b/lisp/progmodes/prog-mode.el
index 04071703184..7a53399ad14 100644
--- a/lisp/progmodes/prog-mode.el
+++ b/lisp/progmodes/prog-mode.el
@@ -336,6 +336,8 @@ support it."
336 (setq-local require-final-newline mode-require-final-newline) 336 (setq-local require-final-newline mode-require-final-newline)
337 (setq-local parse-sexp-ignore-comments t) 337 (setq-local parse-sexp-ignore-comments t)
338 (add-hook 'context-menu-functions 'prog-context-menu 10 t) 338 (add-hook 'context-menu-functions 'prog-context-menu 10 t)
339 ;; Enable text conversion in this buffer.
340 (setq-local text-conversion-style t)
339 ;; Any programming language is always written left to right. 341 ;; Any programming language is always written left to right.
340 (setq bidi-paragraph-direction 'left-to-right)) 342 (setq bidi-paragraph-direction 'left-to-right))
341 343
diff --git a/lisp/simple.el b/lisp/simple.el
index bed6dfb8292..6a12585a55d 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -10864,6 +10864,58 @@ If the buffer doesn't exist, create it first."
10864 "Change value in PLIST of PROP to VAL, comparing with `equal'." 10864 "Change value in PLIST of PROP to VAL, comparing with `equal'."
10865 (declare (obsolete plist-put "29.1")) 10865 (declare (obsolete plist-put "29.1"))
10866 (plist-put plist prop val #'equal)) 10866 (plist-put plist prop val #'equal))
10867
10868
10869
10870;; Text conversion support. See textconv.c for more details about
10871;; what this is.
10872
10873
10874;; Actually in textconv.c.
10875(defvar text-conversion-edits)
10876
10877(defun analyze-text-conversion ()
10878 "Analyze the results of the previous text conversion event.
10879
10880For each insertion:
10881
10882 - Look for the insertion of a string starting or ending with a
10883 character inside `auto-fill-chars', and fill the text around
10884 it if `auto-fill-mode' is enabled.
10885
10886 - Look for the insertion of a new line, and cause automatic
10887 line breaking of the previous line when `auto-fill-mode' is
10888 enabled.
10889
10890 - Look for the insertion of a new line, and indent this new
10891 line if `electric-indent-mode' is enabled."
10892 (interactive)
10893 (dolist (edit text-conversion-edits)
10894 ;; Filter out ephemeral edits and deletions.
10895 (when (and (not (eq (nth 1 edit) (nth 2 edit)))
10896 (stringp (nth 3 edit)))
10897 (with-current-buffer (car edit)
10898 (let* ((inserted (nth 3 edit))
10899 ;; Get the first and last characters.
10900 (start (aref inserted 0))
10901 (end (aref inserted (1- (length inserted))))
10902 ;; Figure out whether or not to auto-fill.
10903 (auto-fill-p (or (aref auto-fill-chars start)
10904 (aref auto-fill-chars end)))
10905 ;; Figure out whether or not a newline was inserted.
10906 (newline-p (string-search "\n" inserted)))
10907 (save-excursion
10908 (if (and auto-fill-function newline-p)
10909 (progn (goto-char (nth 2 edit))
10910 (previous-logical-line)
10911 (funcall auto-fill-function))
10912 (when (and auto-fill-function auto-fill-p)
10913 (progn (goto-char (nth 2 edit))
10914 (funcall auto-fill-function)))))
10915 (when (and electric-indent-mode newline-p)
10916 (goto-char (nth 2 edit))
10917 (indent-according-to-mode)))))))
10918
10867 10919
10868 10920
10869(provide 'simple) 10921(provide 'simple)
diff --git a/lisp/textmodes/text-mode.el b/lisp/textmodes/text-mode.el
index 48cefc74d06..ccba1b063ab 100644
--- a/lisp/textmodes/text-mode.el
+++ b/lisp/textmodes/text-mode.el
@@ -41,6 +41,9 @@
41 "Non-nil if this buffer's major mode is a variant of Text mode.") 41 "Non-nil if this buffer's major mode is a variant of Text mode.")
42(make-obsolete-variable 'text-mode-variant 'derived-mode-p "27.1") 42(make-obsolete-variable 'text-mode-variant 'derived-mode-p "27.1")
43 43
44;; Actually defined in textconv.c.
45(defvar text-conversion-style)
46
44(defvar text-mode-syntax-table 47(defvar text-mode-syntax-table
45 (let ((st (make-syntax-table))) 48 (let ((st (make-syntax-table)))
46 (modify-syntax-entry ?\" ". " st) 49 (modify-syntax-entry ?\" ". " st)
@@ -125,6 +128,9 @@ You can thus get the full benefit of adaptive filling
125Turning on Text mode runs the normal hook `text-mode-hook'." 128Turning on Text mode runs the normal hook `text-mode-hook'."
126 (setq-local text-mode-variant t) 129 (setq-local text-mode-variant t)
127 (setq-local require-final-newline mode-require-final-newline) 130 (setq-local require-final-newline mode-require-final-newline)
131
132 ;; Enable text conversion in this buffer.
133 (setq-local text-conversion-style t)
128 (add-hook 'context-menu-functions 'text-mode-context-menu 10 t)) 134 (add-hook 'context-menu-functions 'text-mode-context-menu 10 t))
129 135
130(define-derived-mode paragraph-indent-text-mode text-mode "Parindent" 136(define-derived-mode paragraph-indent-text-mode text-mode "Parindent"
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);