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.
This commit is contained in:
parent
dd7066901f
commit
cf24b61985
20 changed files with 607 additions and 81 deletions
|
@ -121,6 +121,9 @@ screen, and send the appropriate key events to Emacs after completion.
|
|||
However, on screen keyboard input methods directly perform edits to
|
||||
the selected window of each frame; this is known as ``text
|
||||
conversion'', or ``string conversion'' under the X Window System.
|
||||
Emacs enables these input methods whenever the buffer local value of
|
||||
@code{text-conversion-style} is non-@code{nil}, normally inside
|
||||
derivatives of @code{text-mode} and @code{prog-mode}.
|
||||
|
||||
Text conversion is performed asynchronously whenever Emacs receives
|
||||
a request to perform the conversion from the input method. After the
|
||||
|
|
|
@ -2205,9 +2205,51 @@ A few other event types represent occurrences within the system.
|
|||
This kind of event is sent @strong{after} a system-wide input method
|
||||
performs an edit to one or more buffers.
|
||||
|
||||
Once the event is sent, the input method may already have made changes
|
||||
to multiple frames. @c TODO: allow querying which frames to which
|
||||
@c changes have been made.
|
||||
@vindex text-conversion-edits
|
||||
Once the event is sent, the input method may already have made
|
||||
changes to multiple buffers inside many different frames. To
|
||||
determine which buffers have been changed, and what edits have
|
||||
been made to them, use the variable
|
||||
@code{text-conversion-edits}, which is set prior to each
|
||||
@code{text-conversion} event being sent; it is a list of the
|
||||
form:
|
||||
|
||||
@indentedblock
|
||||
@w{@code{(@var{buffer} @var{beg} @var{end} @var{ephemeral})}}
|
||||
@end indentedblock
|
||||
|
||||
Where @var{ephemeral} is the buffer which was modified,
|
||||
@var{beg} and @var{end} are the positions of the edit at the
|
||||
time it was completed, and @var{ephemeral} is either a string,
|
||||
containing any text which was inserted, @code{t}, meaning that
|
||||
the edit is a temporary edit made by the input method, and
|
||||
@code{nil}, meaning that some text was deleted.
|
||||
|
||||
@vindex text-conversion-style
|
||||
Whether or not this event is sent depends on the value of the
|
||||
buffer-local variable @code{text-conversion-style}, which determines
|
||||
how an input method that wishes to make edits to buffer contents will
|
||||
behave.
|
||||
|
||||
This variable can have one three values:
|
||||
|
||||
@table @code
|
||||
@item nil
|
||||
This means that the input method will be disabled entirely, and key
|
||||
events will be sent instead of text conversion events.
|
||||
|
||||
@item action
|
||||
This means that the input method will be enabled, but @key{RET} will
|
||||
be sent wherever the input method wanted to insert a new line.
|
||||
|
||||
@item t
|
||||
This, or any other value, means that the input method will be enabled
|
||||
and make edits terminated by @code{text-conversion} events.
|
||||
@end itemize
|
||||
|
||||
Changes to the value of this variable will only take effect upon
|
||||
the next redisplay after the buffer becomes the selected buffer
|
||||
of a frame.
|
||||
|
||||
@cindex @code{delete-frame} event
|
||||
@item (delete-frame (@var{frame}))
|
||||
|
|
|
@ -55,7 +55,9 @@ public class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
beginBatchEdit ()
|
||||
{
|
||||
Log.d (TAG, "beginBatchEdit");
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "beginBatchEdit");
|
||||
|
||||
EmacsNative.beginBatchEdit (windowHandle);
|
||||
return true;
|
||||
}
|
||||
|
@ -64,7 +66,9 @@ public class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
endBatchEdit ()
|
||||
{
|
||||
Log.d (TAG, "endBatchEdit");
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "endBatchEdit");
|
||||
|
||||
EmacsNative.endBatchEdit (windowHandle);
|
||||
return true;
|
||||
}
|
||||
|
@ -73,7 +77,9 @@ public class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
commitCompletion (CompletionInfo info)
|
||||
{
|
||||
Log.d (TAG, "commitCompletion: " + info);
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "commitCompletion: " + info);
|
||||
|
||||
EmacsNative.commitCompletion (windowHandle,
|
||||
info.getText ().toString (),
|
||||
info.getPosition ());
|
||||
|
@ -84,7 +90,9 @@ public class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
commitText (CharSequence text, int newCursorPosition)
|
||||
{
|
||||
Log.d (TAG, "commitText: " + text + " " + newCursorPosition);
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "commitText: " + text + " " + newCursorPosition);
|
||||
|
||||
EmacsNative.commitText (windowHandle, text.toString (),
|
||||
newCursorPosition);
|
||||
return true;
|
||||
|
@ -94,8 +102,10 @@ public class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
deleteSurroundingText (int leftLength, int rightLength)
|
||||
{
|
||||
Log.d (TAG, ("deleteSurroundingText: "
|
||||
+ leftLength + " " + rightLength));
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, ("deleteSurroundingText: "
|
||||
+ leftLength + " " + rightLength));
|
||||
|
||||
EmacsNative.deleteSurroundingText (windowHandle, leftLength,
|
||||
rightLength);
|
||||
return true;
|
||||
|
@ -105,7 +115,8 @@ public class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
finishComposingText ()
|
||||
{
|
||||
Log.d (TAG, "finishComposingText");
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "finishComposingText");
|
||||
|
||||
EmacsNative.finishComposingText (windowHandle);
|
||||
return true;
|
||||
|
@ -115,7 +126,8 @@ public class EmacsInputConnection extends BaseInputConnection
|
|||
public String
|
||||
getSelectedText (int flags)
|
||||
{
|
||||
Log.d (TAG, "getSelectedText: " + flags);
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getSelectedText: " + flags);
|
||||
|
||||
return EmacsNative.getSelectedText (windowHandle, flags);
|
||||
}
|
||||
|
@ -124,27 +136,44 @@ public class EmacsInputConnection extends BaseInputConnection
|
|||
public String
|
||||
getTextAfterCursor (int length, int flags)
|
||||
{
|
||||
Log.d (TAG, "getTextAfterCursor: " + length + " " + flags);
|
||||
String string;
|
||||
|
||||
return EmacsNative.getTextAfterCursor (windowHandle, length,
|
||||
flags);
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getTextAfterCursor: " + length + " " + flags);
|
||||
|
||||
string = EmacsNative.getTextAfterCursor (windowHandle, length,
|
||||
flags);
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, " --> " + string);
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String
|
||||
getTextBeforeCursor (int length, int flags)
|
||||
{
|
||||
Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags);
|
||||
String string;
|
||||
|
||||
return EmacsNative.getTextBeforeCursor (windowHandle, length,
|
||||
flags);
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags);
|
||||
|
||||
string = EmacsNative.getTextBeforeCursor (windowHandle, length,
|
||||
flags);
|
||||
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, " --> " + string);
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
setComposingText (CharSequence text, int newCursorPosition)
|
||||
{
|
||||
Log.d (TAG, "setComposingText: " + newCursorPosition);
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "setComposingText: " + newCursorPosition);
|
||||
|
||||
EmacsNative.setComposingText (windowHandle, text.toString (),
|
||||
newCursorPosition);
|
||||
|
@ -155,7 +184,8 @@ public class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
setComposingRegion (int start, int end)
|
||||
{
|
||||
Log.d (TAG, "setComposingRegion: " + start + " " + end);
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "setComposingRegion: " + start + " " + end);
|
||||
|
||||
EmacsNative.setComposingRegion (windowHandle, start, end);
|
||||
return true;
|
||||
|
@ -165,7 +195,8 @@ public class EmacsInputConnection extends BaseInputConnection
|
|||
public boolean
|
||||
performEditorAction (int editorAction)
|
||||
{
|
||||
Log.d (TAG, "performEditorAction: " + editorAction);
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "performEditorAction: " + editorAction);
|
||||
|
||||
EmacsNative.performEditorAction (windowHandle, editorAction);
|
||||
return true;
|
||||
|
@ -175,7 +206,8 @@ public class EmacsInputConnection extends BaseInputConnection
|
|||
public ExtractedText
|
||||
getExtractedText (ExtractedTextRequest request, int flags)
|
||||
{
|
||||
Log.d (TAG, "getExtractedText: " + request + " " + flags);
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, "getExtractedText: " + request + " " + flags);
|
||||
|
||||
return EmacsNative.getExtractedText (windowHandle, request,
|
||||
flags);
|
||||
|
|
|
@ -88,6 +88,8 @@ public class EmacsService extends Service
|
|||
/* Display metrics used by font backends. */
|
||||
public DisplayMetrics metrics;
|
||||
|
||||
public static final boolean DEBUG_IC = false;
|
||||
|
||||
@Override
|
||||
public int
|
||||
onStartCommand (Intent intent, int flags, int startId)
|
||||
|
@ -612,10 +614,11 @@ invocation of app_process (through android-emacs) can
|
|||
int newSelectionEnd, int composingRegionStart,
|
||||
int composingRegionEnd)
|
||||
{
|
||||
Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart
|
||||
+ " " + newSelectionEnd + " "
|
||||
+ composingRegionStart + " "
|
||||
+ composingRegionEnd));
|
||||
if (DEBUG_IC)
|
||||
Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart
|
||||
+ " " + newSelectionEnd + " "
|
||||
+ composingRegionStart + " "
|
||||
+ composingRegionEnd));
|
||||
window.view.imManager.updateSelection (window.view,
|
||||
newSelectionStart,
|
||||
newSelectionEnd,
|
||||
|
@ -626,7 +629,8 @@ invocation of app_process (through android-emacs) can
|
|||
public void
|
||||
resetIC (EmacsWindow window, int icMode)
|
||||
{
|
||||
Log.d (TAG, "resetIC: " + window);
|
||||
if (DEBUG_IC)
|
||||
Log.d (TAG, "resetIC: " + window);
|
||||
|
||||
window.view.setICMode (icMode);
|
||||
window.view.imManager.restartInput (window.view);
|
||||
|
|
|
@ -1522,7 +1522,7 @@ if `inhibit-field-text-motion' is non-nil."
|
|||
(define-key special-event-map [sigusr2] 'ignore)
|
||||
|
||||
;; Text conversion
|
||||
(define-key global-map [text-conversion] 'ignore)
|
||||
(define-key global-map [text-conversion] 'analyze-text-conversion)
|
||||
|
||||
;; Don't look for autoload cookies in this file.
|
||||
;; Local Variables:
|
||||
|
|
|
@ -294,6 +294,7 @@ or comment."
|
|||
|
||||
;;;###autoload
|
||||
(define-key global-map "\C-j" 'electric-newline-and-maybe-indent)
|
||||
|
||||
;;;###autoload
|
||||
(defun electric-newline-and-maybe-indent ()
|
||||
"Insert a newline.
|
||||
|
|
|
@ -336,6 +336,8 @@ support it."
|
|||
(setq-local require-final-newline mode-require-final-newline)
|
||||
(setq-local parse-sexp-ignore-comments t)
|
||||
(add-hook 'context-menu-functions 'prog-context-menu 10 t)
|
||||
;; Enable text conversion in this buffer.
|
||||
(setq-local text-conversion-style t)
|
||||
;; Any programming language is always written left to right.
|
||||
(setq bidi-paragraph-direction 'left-to-right))
|
||||
|
||||
|
|
|
@ -10864,6 +10864,58 @@ If the buffer doesn't exist, create it first."
|
|||
"Change value in PLIST of PROP to VAL, comparing with `equal'."
|
||||
(declare (obsolete plist-put "29.1"))
|
||||
(plist-put plist prop val #'equal))
|
||||
|
||||
|
||||
|
||||
;; Text conversion support. See textconv.c for more details about
|
||||
;; what this is.
|
||||
|
||||
|
||||
;; Actually in textconv.c.
|
||||
(defvar text-conversion-edits)
|
||||
|
||||
(defun analyze-text-conversion ()
|
||||
"Analyze the results of the previous text conversion event.
|
||||
|
||||
For each insertion:
|
||||
|
||||
- Look for the insertion of a string starting or ending with a
|
||||
character inside `auto-fill-chars', and fill the text around
|
||||
it if `auto-fill-mode' is enabled.
|
||||
|
||||
- Look for the insertion of a new line, and cause automatic
|
||||
line breaking of the previous line when `auto-fill-mode' is
|
||||
enabled.
|
||||
|
||||
- Look for the insertion of a new line, and indent this new
|
||||
line if `electric-indent-mode' is enabled."
|
||||
(interactive)
|
||||
(dolist (edit text-conversion-edits)
|
||||
;; Filter out ephemeral edits and deletions.
|
||||
(when (and (not (eq (nth 1 edit) (nth 2 edit)))
|
||||
(stringp (nth 3 edit)))
|
||||
(with-current-buffer (car edit)
|
||||
(let* ((inserted (nth 3 edit))
|
||||
;; Get the first and last characters.
|
||||
(start (aref inserted 0))
|
||||
(end (aref inserted (1- (length inserted))))
|
||||
;; Figure out whether or not to auto-fill.
|
||||
(auto-fill-p (or (aref auto-fill-chars start)
|
||||
(aref auto-fill-chars end)))
|
||||
;; Figure out whether or not a newline was inserted.
|
||||
(newline-p (string-search "\n" inserted)))
|
||||
(save-excursion
|
||||
(if (and auto-fill-function newline-p)
|
||||
(progn (goto-char (nth 2 edit))
|
||||
(previous-logical-line)
|
||||
(funcall auto-fill-function))
|
||||
(when (and auto-fill-function auto-fill-p)
|
||||
(progn (goto-char (nth 2 edit))
|
||||
(funcall auto-fill-function)))))
|
||||
(when (and electric-indent-mode newline-p)
|
||||
(goto-char (nth 2 edit))
|
||||
(indent-according-to-mode)))))))
|
||||
|
||||
|
||||
|
||||
(provide 'simple)
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
"Non-nil if this buffer's major mode is a variant of Text mode.")
|
||||
(make-obsolete-variable 'text-mode-variant 'derived-mode-p "27.1")
|
||||
|
||||
;; Actually defined in textconv.c.
|
||||
(defvar text-conversion-style)
|
||||
|
||||
(defvar text-mode-syntax-table
|
||||
(let ((st (make-syntax-table)))
|
||||
(modify-syntax-entry ?\" ". " st)
|
||||
|
@ -125,6 +128,9 @@ You can thus get the full benefit of adaptive filling
|
|||
Turning on Text mode runs the normal hook `text-mode-hook'."
|
||||
(setq-local text-mode-variant t)
|
||||
(setq-local require-final-newline mode-require-final-newline)
|
||||
|
||||
;; Enable text conversion in this buffer.
|
||||
(setq-local text-conversion-style t)
|
||||
(add-hook 'context-menu-functions 'text-mode-context-menu 10 t))
|
||||
|
||||
(define-derived-mode paragraph-indent-text-mode text-mode "Parindent"
|
||||
|
|
|
@ -621,8 +621,9 @@ android_handle_ime_event (union android_event *event, struct frame *f)
|
|||
break;
|
||||
|
||||
case ANDROID_IME_SET_POINT:
|
||||
textconv_set_point (f, event->ime.position,
|
||||
event->ime.counter);
|
||||
textconv_set_point_and_mark (f, event->ime.start,
|
||||
event->ime.end,
|
||||
event->ime.counter);
|
||||
break;
|
||||
|
||||
case ANDROID_IME_START_BATCH_EDIT:
|
||||
|
@ -4305,7 +4306,7 @@ static sem_t edit_sem;
|
|||
|
||||
Every time one of the text retrieval functions is called and an
|
||||
editing request is made, Emacs gives the main thread approximately
|
||||
50 ms to process it, in order to mostly keep the input method in
|
||||
100 ms to process it, in order to mostly keep the input method in
|
||||
sync with the buffer contents. */
|
||||
|
||||
static void
|
||||
|
@ -4319,7 +4320,7 @@ android_sync_edit (void)
|
|||
return;
|
||||
|
||||
start = current_timespec ();
|
||||
end = timespec_add (start, make_timespec (0, 50000000));
|
||||
end = timespec_add (start, make_timespec (0, 100000000));
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
@ -4550,8 +4551,7 @@ android_perform_conversion_query (void *data)
|
|||
if (!f)
|
||||
return;
|
||||
|
||||
textconv_query (f, &context->query,
|
||||
TEXTCONV_SKIP_CONVERSION_REGION);
|
||||
textconv_query (f, &context->query, TEXTCONV_SKIP_ACTIVE_REGION);
|
||||
|
||||
/* context->query.text will have been set even if textconv_query
|
||||
returns 1. */
|
||||
|
@ -4648,13 +4648,6 @@ android_text_to_string (JNIEnv *env, char *buffer, ptrdiff_t n,
|
|||
return string;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object,
|
||||
jshort window)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
NATIVE_NAME (getTextAfterCursor) (JNIEnv *env, jobject object, jshort window,
|
||||
jint length, jint flags)
|
||||
|
@ -4805,8 +4798,8 @@ NATIVE_NAME (setSelection) (JNIEnv *env, jobject object, jshort window,
|
|||
event.ime.serial = ++event_serial;
|
||||
event.ime.window = window;
|
||||
event.ime.operation = ANDROID_IME_SET_POINT;
|
||||
event.ime.start = 0;
|
||||
event.ime.end = 0;
|
||||
event.ime.start = start;
|
||||
event.ime.end = end;
|
||||
event.ime.length = 0;
|
||||
event.ime.position = start;
|
||||
event.ime.text = NULL;
|
||||
|
@ -5068,6 +5061,34 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object,
|
|||
return object;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object,
|
||||
jshort window)
|
||||
{
|
||||
struct android_get_extracted_text_context context;
|
||||
jstring string;
|
||||
|
||||
context.hint_max_chars = -1;
|
||||
context.token = 0;
|
||||
context.text = NULL;
|
||||
context.window = window;
|
||||
|
||||
android_sync_edit ();
|
||||
if (android_run_in_emacs_thread (android_get_extracted_text,
|
||||
&context))
|
||||
return NULL;
|
||||
|
||||
if (!context.text)
|
||||
return NULL;
|
||||
|
||||
/* Encode the returned text. */
|
||||
string = android_text_to_string (env, context.text, context.length,
|
||||
context.bytes);
|
||||
free (context.text);
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
|
@ -5083,7 +5104,8 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object,
|
|||
static void
|
||||
android_update_selection (struct frame *f, struct window *w)
|
||||
{
|
||||
ptrdiff_t start, end, point;
|
||||
ptrdiff_t start, end, point, mark;
|
||||
struct buffer *b;
|
||||
|
||||
if (MARKERP (f->conversion.compose_region_start))
|
||||
{
|
||||
|
@ -5103,12 +5125,20 @@ android_update_selection (struct frame *f, struct window *w)
|
|||
if (!w)
|
||||
w = XWINDOW (f->selected_window);
|
||||
|
||||
/* Figure out where the point is. */
|
||||
/* Figure out where the point and mark are. If the mark is not
|
||||
active, then point is set to equal mark. */
|
||||
b = XBUFFER (w->contents);
|
||||
point = min (w->last_point, TYPE_MAXIMUM (jint));
|
||||
mark = ((!NILP (BVAR (b, mark_active))
|
||||
&& w->last_mark != -1)
|
||||
? min (w->last_mark, TYPE_MAXIMUM (jint))
|
||||
: point);
|
||||
|
||||
/* Send the update. */
|
||||
android_update_ic (FRAME_ANDROID_WINDOW (f), point, point,
|
||||
start, end);
|
||||
/* Send the update. Android doesn't have a concept of ``point'' and
|
||||
``mark''; instead, it only has a selection, where the start of
|
||||
the selection is less than or equal to the end. */
|
||||
android_update_ic (FRAME_ANDROID_WINDOW (f), min (point, mark),
|
||||
max (point, mark), start, end);
|
||||
}
|
||||
|
||||
/* 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)
|
|||
static void
|
||||
android_reset_conversion (struct frame *f)
|
||||
{
|
||||
enum android_ic_mode mode;
|
||||
struct window *w;
|
||||
struct buffer *buffer;
|
||||
|
||||
/* Reset the input method.
|
||||
|
||||
Pick an appropriate ``input mode'' based on whether or not the
|
||||
minibuffer window is selected; this controls whether or not
|
||||
``RET'' inserts a newline or sends an actual key event. */
|
||||
|
||||
w = XWINDOW (f->selected_window);
|
||||
buffer = XBUFFER (WINDOW_BUFFER (w));
|
||||
|
||||
if (NILP (BVAR (buffer, text_conversion_style)))
|
||||
mode = ANDROID_IC_MODE_NULL;
|
||||
else if (EQ (BVAR (buffer, text_conversion_style),
|
||||
Qaction))
|
||||
mode = ANDROID_IC_MODE_ACTION;
|
||||
else
|
||||
mode = ANDROID_IC_MODE_TEXT;
|
||||
|
||||
android_reset_ic (FRAME_ANDROID_WINDOW (f),
|
||||
(EQ (f->selected_window,
|
||||
f->minibuffer_window)
|
||||
? ANDROID_IC_MODE_ACTION
|
||||
: ANDROID_IC_MODE_TEXT));
|
||||
: mode));
|
||||
|
||||
/* Move its selection to the specified position. */
|
||||
android_update_selection (f, NULL);
|
||||
|
|
20
src/buffer.c
20
src/buffer.c
|
@ -4710,6 +4710,7 @@ init_buffer_once (void)
|
|||
#ifdef HAVE_TREE_SITTER
|
||||
XSETFASTINT (BVAR (&buffer_local_flags, ts_parser_list), idx); ++idx;
|
||||
#endif
|
||||
XSETFASTINT (BVAR (&buffer_local_flags, text_conversion_style), idx); ++idx;
|
||||
XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx;
|
||||
|
||||
/* buffer_local_flags contains no pointers, so it's safe to treat it
|
||||
|
@ -4780,6 +4781,9 @@ init_buffer_once (void)
|
|||
bset_extra_line_spacing (&buffer_defaults, Qnil);
|
||||
#ifdef HAVE_TREE_SITTER
|
||||
bset_ts_parser_list (&buffer_defaults, Qnil);
|
||||
#endif
|
||||
#ifdef HAVE_TEXT_CONVERSION
|
||||
bset_text_conversion_style (&buffer_defaults, Qnil);
|
||||
#endif
|
||||
bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
|
||||
|
||||
|
@ -5852,6 +5856,22 @@ If t, displays a cursor related to the usual cursor type
|
|||
You can also specify the cursor type as in the `cursor-type' variable.
|
||||
Use Custom to set this variable and update the display. */);
|
||||
|
||||
/* While this is defined here, each *term.c module must implement
|
||||
the logic itself. */
|
||||
|
||||
DEFVAR_PER_BUFFER ("text-conversion-style", &BVAR (current_buffer,
|
||||
text_conversion_style),
|
||||
Qnil,
|
||||
"How the on screen keyboard's input method should insert in this buffer.\n\
|
||||
When nil, the input method will be disabled and an ordinary keyboard\n\
|
||||
will be displayed in its place.\n\
|
||||
When the symbol `action', the input method will insert text directly, but\n\
|
||||
will send `return' key events instead of inserting new line characters.\n\
|
||||
Any other value means that the input method will insert text directly.\n\
|
||||
\n\
|
||||
This variable does not take immediate effect when set; rather, it takes\n\
|
||||
effect upon the next redisplay after the selected window or buffer changes.");
|
||||
|
||||
DEFVAR_LISP ("kill-buffer-query-functions", Vkill_buffer_query_functions,
|
||||
doc: /* List of functions called with no args to query before killing a buffer.
|
||||
The buffer being killed will be current while the functions are running.
|
||||
|
|
11
src/buffer.h
11
src/buffer.h
|
@ -566,6 +566,11 @@ struct buffer
|
|||
/* A list of tree-sitter parsers for this buffer. */
|
||||
Lisp_Object ts_parser_list_;
|
||||
#endif
|
||||
|
||||
/* What type of text conversion the input method should apply to
|
||||
this buffer. */
|
||||
Lisp_Object text_conversion_style_;
|
||||
|
||||
/* Cursor type to display in non-selected windows.
|
||||
t means to use hollow box cursor.
|
||||
See `cursor-type' for other values. */
|
||||
|
@ -842,6 +847,12 @@ bset_width_table (struct buffer *b, Lisp_Object val)
|
|||
b->width_table_ = val;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
bset_text_conversion_style (struct buffer *b, Lisp_Object val)
|
||||
{
|
||||
b->text_conversion_style_ = val;
|
||||
}
|
||||
|
||||
/* BUFFER_CEILING_OF (resp. BUFFER_FLOOR_OF), when applied to n, return
|
||||
the max (resp. min) p such that
|
||||
|
||||
|
|
|
@ -2000,6 +2000,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
|
|||
#ifdef HAVE_WINDOW_SYSTEM
|
||||
init_fringe_once (); /* Swap bitmaps if necessary. */
|
||||
#endif /* HAVE_WINDOW_SYSTEM */
|
||||
#ifdef HAVE_TEXT_CONVERSION
|
||||
syms_of_textconv ();
|
||||
#endif
|
||||
}
|
||||
|
||||
init_alloc ();
|
||||
|
|
|
@ -86,7 +86,7 @@ enum text_conversion_operation
|
|||
TEXTCONV_FINISH_COMPOSING_TEXT,
|
||||
TEXTCONV_SET_COMPOSING_TEXT,
|
||||
TEXTCONV_SET_COMPOSING_REGION,
|
||||
TEXTCONV_SET_POINT,
|
||||
TEXTCONV_SET_POINT_AND_MARK,
|
||||
TEXTCONV_DELETE_SURROUNDING_TEXT,
|
||||
};
|
||||
|
||||
|
|
|
@ -5234,6 +5234,7 @@ extern void reset_frame_state (struct frame *);
|
|||
extern void report_selected_window_change (struct frame *);
|
||||
extern void report_point_change (struct frame *, struct window *,
|
||||
struct buffer *);
|
||||
extern void syms_of_textconv (void);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NATIVE_COMP
|
||||
|
|
26
src/marker.c
26
src/marker.c
|
@ -23,6 +23,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||
#include "lisp.h"
|
||||
#include "character.h"
|
||||
#include "buffer.h"
|
||||
#include "window.h"
|
||||
|
||||
/* Record one cached position found recently by
|
||||
buf_charpos_to_bytepos or buf_bytepos_to_charpos. */
|
||||
|
@ -566,6 +567,31 @@ set_marker_internal (Lisp_Object marker, Lisp_Object position,
|
|||
|
||||
attach_marker (m, b, charpos, bytepos);
|
||||
}
|
||||
|
||||
#ifdef HAVE_TEXT_CONVERSION
|
||||
|
||||
/* If B is the buffer's mark and there is a window displaying B, and
|
||||
text conversion is enabled while the mark is active, redisplay
|
||||
the buffer.
|
||||
|
||||
propagate_window_redisplay will propagate this redisplay to the
|
||||
window, which will eventually reach
|
||||
mark_window_display_accurate_1. At that point,
|
||||
report_point_change will be told to update the mark as seen by
|
||||
the input method.
|
||||
|
||||
This is done all the way in (the seemingly irrelevant) redisplay
|
||||
because the selection reported to the input method is actually what
|
||||
is visible on screen, namely w->last_point. */
|
||||
|
||||
if (m->buffer
|
||||
&& EQ (marker, BVAR (m->buffer, mark))
|
||||
&& !NILP (BVAR (m->buffer, mark_active))
|
||||
&& buffer_window_count (m->buffer))
|
||||
bset_redisplay (m->buffer);
|
||||
|
||||
#endif
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
|
|
315
src/textconv.c
315
src/textconv.c
|
@ -89,13 +89,27 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
|
|||
size = end0 - beg0;
|
||||
memcpy (buffer, BYTE_POS_ADDR (beg0), size);
|
||||
if (beg1 != -1)
|
||||
memcpy (buffer, BEG_ADDR + beg1, end1 - beg1);
|
||||
memcpy (buffer + size, BEG_ADDR + beg1, end1 - beg1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Conversion query. */
|
||||
|
||||
/* Return the position of the active mark, or -1 if there is no mark
|
||||
or it is not active. */
|
||||
|
||||
static ptrdiff_t
|
||||
get_mark (void)
|
||||
{
|
||||
if (!NILP (BVAR (current_buffer, mark_active))
|
||||
&& XMARKER (BVAR (current_buffer, mark))->buffer)
|
||||
return marker_position (BVAR (current_buffer,
|
||||
mark));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Perform the text conversion operation specified in QUERY and return
|
||||
the results.
|
||||
|
||||
|
@ -109,6 +123,9 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
|
|||
If FLAGS & TEXTCONV_SKIP_CONVERSION_REGION, then first move PT past
|
||||
the conversion region in the specified direction if it is inside.
|
||||
|
||||
If FLAGS & TEXTCONV_SKIP_ACTIVE_REGION, then also move PT past the
|
||||
region if the mark is active.
|
||||
|
||||
Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION
|
||||
or if deleting the text was successful, and 1 otherwise. */
|
||||
|
||||
|
@ -169,6 +186,39 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query,
|
|||
}
|
||||
}
|
||||
|
||||
/* Likewise for the region if the mark is active. */
|
||||
|
||||
if (flags & TEXTCONV_SKIP_ACTIVE_REGION)
|
||||
{
|
||||
temp = get_mark ();
|
||||
|
||||
if (temp == -1)
|
||||
goto escape;
|
||||
|
||||
start = min (temp, PT);
|
||||
end = max (temp, PT);
|
||||
|
||||
if (pos >= start && pos < end)
|
||||
{
|
||||
switch (query->direction)
|
||||
{
|
||||
case TEXTCONV_FORWARD_CHAR:
|
||||
case TEXTCONV_FORWARD_WORD:
|
||||
case TEXTCONV_CARET_DOWN:
|
||||
case TEXTCONV_NEXT_LINE:
|
||||
case TEXTCONV_LINE_START:
|
||||
pos = end;
|
||||
break;
|
||||
|
||||
default:
|
||||
pos = max (BEGV, start);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
escape:
|
||||
|
||||
/* If pos is outside the accessible part of the buffer or if it
|
||||
overflows, move back to point or to the extremes of the
|
||||
accessible region. */
|
||||
|
@ -335,6 +385,55 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Update the overlay displaying the conversion area on F after a
|
||||
change to the conversion region. */
|
||||
|
||||
static void
|
||||
sync_overlay (struct frame *f)
|
||||
{
|
||||
if (MARKERP (f->conversion.compose_region_start))
|
||||
{
|
||||
if (NILP (f->conversion.compose_region_overlay))
|
||||
{
|
||||
f->conversion.compose_region_overlay
|
||||
= Fmake_overlay (f->conversion.compose_region_start,
|
||||
f->conversion.compose_region_end, Qnil,
|
||||
Qt, Qnil);
|
||||
Foverlay_put (f->conversion.compose_region_overlay,
|
||||
Qface, Qunderline);
|
||||
}
|
||||
|
||||
Fmove_overlay (f->conversion.compose_region_overlay,
|
||||
f->conversion.compose_region_start,
|
||||
f->conversion.compose_region_end, Qnil);
|
||||
}
|
||||
else if (!NILP (f->conversion.compose_region_overlay))
|
||||
{
|
||||
Fdelete_overlay (f->conversion.compose_region_overlay);
|
||||
f->conversion.compose_region_overlay = Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
/* Record a change to the current buffer as a result of an
|
||||
asynchronous text conversion operation on F.
|
||||
|
||||
Consult the doc string of `text-conversion-edits' for the meaning
|
||||
of BEG, END, and EPHEMERAL. */
|
||||
|
||||
static void
|
||||
record_buffer_change (ptrdiff_t beg, ptrdiff_t end,
|
||||
Lisp_Object ephemeral)
|
||||
{
|
||||
Lisp_Object buffer;
|
||||
|
||||
XSETBUFFER (buffer, current_buffer);
|
||||
|
||||
Vtext_conversion_edits
|
||||
= Fcons (list4 (buffer, make_fixnum (beg),
|
||||
make_fixnum (end), ephemeral),
|
||||
Vtext_conversion_edits);
|
||||
}
|
||||
|
||||
/* Reset F's text conversion state. Delete any overlays or
|
||||
markers inside. */
|
||||
|
||||
|
@ -438,8 +537,10 @@ really_commit_text (struct frame *f, EMACS_INT position,
|
|||
/* Replace its contents. */
|
||||
start = marker_position (f->conversion.compose_region_start);
|
||||
end = marker_position (f->conversion.compose_region_end);
|
||||
safe_del_range (start, end);
|
||||
del_range (start, end);
|
||||
record_buffer_change (start, start, Qnil);
|
||||
Finsert (1, &text);
|
||||
record_buffer_change (start, PT, text);
|
||||
|
||||
/* Move to a the position specified in POSITION. */
|
||||
|
||||
|
@ -493,6 +594,7 @@ really_commit_text (struct frame *f, EMACS_INT position,
|
|||
location. */
|
||||
wanted = PT;
|
||||
Finsert (1, &text);
|
||||
record_buffer_change (wanted, PT, text);
|
||||
|
||||
if (position < 0)
|
||||
{
|
||||
|
@ -520,6 +622,8 @@ really_commit_text (struct frame *f, EMACS_INT position,
|
|||
}
|
||||
}
|
||||
|
||||
/* This should deactivate the mark. */
|
||||
call0 (Qdeactivate_mark);
|
||||
unbind_to (count, Qnil);
|
||||
}
|
||||
|
||||
|
@ -575,12 +679,11 @@ really_set_composing_text (struct frame *f, ptrdiff_t position,
|
|||
|
||||
if (!MARKERP (f->conversion.compose_region_start))
|
||||
{
|
||||
f->conversion.compose_region_start = Fmake_marker ();
|
||||
f->conversion.compose_region_end = Fmake_marker ();
|
||||
Fset_marker (f->conversion.compose_region_start,
|
||||
Fpoint (), Qnil);
|
||||
Fset_marker (f->conversion.compose_region_end,
|
||||
Fpoint (), Qnil);
|
||||
f->conversion.compose_region_start
|
||||
= build_marker (current_buffer, PT, PT_BYTE);
|
||||
f->conversion.compose_region_end
|
||||
= build_marker (current_buffer, PT, PT_BYTE);
|
||||
|
||||
Fset_marker_insertion_type (f->conversion.compose_region_end,
|
||||
Qt);
|
||||
|
||||
|
@ -595,13 +698,19 @@ really_set_composing_text (struct frame *f, ptrdiff_t position,
|
|||
Fwiden ();
|
||||
start = marker_position (f->conversion.compose_region_start);
|
||||
end = marker_position (f->conversion.compose_region_end);
|
||||
safe_del_range (start, end);
|
||||
del_range (start, end);
|
||||
set_point (start);
|
||||
|
||||
if (start != end)
|
||||
record_buffer_change (start, start, Qnil);
|
||||
}
|
||||
|
||||
/* Insert the new text. */
|
||||
Finsert (1, &text);
|
||||
|
||||
if (start != PT)
|
||||
record_buffer_change (start, PT, Qnil);
|
||||
|
||||
/* Now move point to an appropriate location. */
|
||||
if (position < 0)
|
||||
{
|
||||
|
@ -632,6 +741,12 @@ really_set_composing_text (struct frame *f, ptrdiff_t position,
|
|||
|
||||
set_point (wanted);
|
||||
|
||||
/* This should deactivate the mark. */
|
||||
call0 (Qdeactivate_mark);
|
||||
|
||||
/* Move the composition overlay. */
|
||||
sync_overlay (f);
|
||||
|
||||
/* If PT hasn't changed, the conversion region definitely has.
|
||||
Otherwise, redisplay will update the input method instead. */
|
||||
|
||||
|
@ -693,18 +808,21 @@ really_set_composing_region (struct frame *f, ptrdiff_t start,
|
|||
make_fixnum (start), Qnil);
|
||||
Fset_marker (f->conversion.compose_region_end,
|
||||
make_fixnum (end), Qnil);
|
||||
sync_overlay (f);
|
||||
|
||||
unbind_to (count, Qnil);
|
||||
}
|
||||
|
||||
/* Delete LEFT and RIGHT chars around point. */
|
||||
/* Delete LEFT and RIGHT chars around point or the active mark,
|
||||
whichever is larger, avoiding the composing region if
|
||||
necessary. */
|
||||
|
||||
static void
|
||||
really_delete_surrounding_text (struct frame *f, ptrdiff_t left,
|
||||
ptrdiff_t right)
|
||||
{
|
||||
specpdl_ref count;
|
||||
ptrdiff_t start, end;
|
||||
ptrdiff_t start, end, a, b, a1, b1, lstart, rstart;
|
||||
|
||||
/* If F's old selected window is no longer live, fail. */
|
||||
|
||||
|
@ -719,21 +837,71 @@ really_delete_surrounding_text (struct frame *f, ptrdiff_t left,
|
|||
redisplay. */
|
||||
Fselect_window (f->old_selected_window, Qt);
|
||||
|
||||
start = max (BEGV, PT - left);
|
||||
end = min (ZV, PT + right);
|
||||
/* Figure out where to start deleting from. */
|
||||
|
||||
a = get_mark ();
|
||||
|
||||
if (a != -1 && a != PT)
|
||||
lstart = rstart = max (a, PT);
|
||||
else
|
||||
lstart = rstart = PT;
|
||||
|
||||
/* Avoid the composing text. This behavior is identical to how
|
||||
Android's BaseInputConnection actually implements avoiding the
|
||||
composing span. */
|
||||
|
||||
if (MARKERP (f->conversion.compose_region_start))
|
||||
{
|
||||
a = marker_position (f->conversion.compose_region_start);
|
||||
b = marker_position (f->conversion.compose_region_end);
|
||||
|
||||
a1 = min (a, b);
|
||||
b1 = max (a, b);
|
||||
|
||||
lstart = min (lstart, min (PT, a1));
|
||||
rstart = max (rstart, max (PT, b1));
|
||||
}
|
||||
|
||||
if (lstart == rstart)
|
||||
{
|
||||
start = max (BEGV, lstart - left);
|
||||
end = min (ZV, rstart + right);
|
||||
|
||||
del_range (start, end);
|
||||
record_buffer_change (start, start, Qnil);
|
||||
}
|
||||
else
|
||||
{
|
||||
start = max (BEGV, lstart - left);
|
||||
end = lstart;
|
||||
|
||||
del_range (start, end);
|
||||
record_buffer_change (start, start, Qnil);
|
||||
|
||||
start = rstart;
|
||||
end = min (ZV, rstart + right);
|
||||
del_range (start, end);
|
||||
record_buffer_change (start, start, Qnil);
|
||||
}
|
||||
|
||||
/* if the mark is now equal to start, deactivate it. */
|
||||
|
||||
if (get_mark () == PT)
|
||||
call0 (Qdeactivate_mark);
|
||||
|
||||
safe_del_range (start, end);
|
||||
unbind_to (count, Qnil);
|
||||
}
|
||||
|
||||
/* Set point in F to POSITION.
|
||||
/* Set point in F to POSITION. If MARK is not POSITION, activate the
|
||||
mark and set MARK to that as well.
|
||||
|
||||
If it has not changed, signal an update through the text input
|
||||
interface, which is necessary for the IME to acknowledge that the
|
||||
change has completed. */
|
||||
|
||||
static void
|
||||
really_set_point (struct frame *f, ptrdiff_t point)
|
||||
really_set_point_and_mark (struct frame *f, ptrdiff_t point,
|
||||
ptrdiff_t mark)
|
||||
{
|
||||
specpdl_ref count;
|
||||
|
||||
|
@ -763,6 +931,11 @@ really_set_point (struct frame *f, ptrdiff_t point)
|
|||
/* Set the point. */
|
||||
Fgoto_char (make_fixnum (point));
|
||||
|
||||
if (mark == point && BVAR (current_buffer, mark_active))
|
||||
call0 (Qdeactivate_mark);
|
||||
else
|
||||
call1 (Qpush_mark, make_fixnum (mark));
|
||||
|
||||
unbind_to (count, Qnil);
|
||||
}
|
||||
|
||||
|
@ -845,8 +1018,9 @@ handle_pending_conversion_events_1 (struct frame *f,
|
|||
XFIXNUM (XCDR (data)));
|
||||
break;
|
||||
|
||||
case TEXTCONV_SET_POINT:
|
||||
really_set_point (f, XFIXNUM (data));
|
||||
case TEXTCONV_SET_POINT_AND_MARK:
|
||||
really_set_point_and_mark (f, XFIXNUM (XCAR (data)),
|
||||
XFIXNUM (XCDR (data)));
|
||||
break;
|
||||
|
||||
case TEXTCONV_DELETE_SURROUNDING_TEXT:
|
||||
|
@ -858,6 +1032,17 @@ handle_pending_conversion_events_1 (struct frame *f,
|
|||
unbind_to (count, Qnil);
|
||||
}
|
||||
|
||||
/* Decrement the variable pointed to by *PTR. */
|
||||
|
||||
static void
|
||||
decrement_inside (void *ptr)
|
||||
{
|
||||
int *i;
|
||||
|
||||
i = ptr;
|
||||
(*i)--;
|
||||
}
|
||||
|
||||
/* Process any outstanding text conversion events.
|
||||
This may run Lisp or signal. */
|
||||
|
||||
|
@ -868,9 +1053,22 @@ handle_pending_conversion_events (void)
|
|||
Lisp_Object tail, frame;
|
||||
struct text_conversion_action *action, *next;
|
||||
bool handled;
|
||||
static int inside;
|
||||
specpdl_ref count;
|
||||
|
||||
handled = false;
|
||||
|
||||
/* Reset Vtext_conversion_edits. Do not do this if called
|
||||
reentrantly. */
|
||||
|
||||
if (!inside)
|
||||
Vtext_conversion_edits = Qnil;
|
||||
|
||||
inside++;
|
||||
|
||||
count = SPECPDL_INDEX ();
|
||||
record_unwind_protect_ptr (decrement_inside, &inside);
|
||||
|
||||
FOR_EACH_FRAME (tail, frame)
|
||||
{
|
||||
f = XFRAME (frame);
|
||||
|
@ -905,6 +1103,8 @@ handle_pending_conversion_events (void)
|
|||
handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
unbind_to (count, Qnil);
|
||||
}
|
||||
|
||||
/* 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,
|
|||
input_pending = true;
|
||||
}
|
||||
|
||||
/* Move point in F's selected buffer to POINT.
|
||||
/* Move point in F's selected buffer to POINT and maybe push MARK.
|
||||
|
||||
COUNTER means the same as in `start_batch_edit'. */
|
||||
|
||||
void
|
||||
textconv_set_point (struct frame *f, ptrdiff_t point,
|
||||
unsigned long counter)
|
||||
textconv_set_point_and_mark (struct frame *f, ptrdiff_t point,
|
||||
ptrdiff_t mark, unsigned long counter)
|
||||
{
|
||||
struct text_conversion_action *action, **last;
|
||||
|
||||
|
@ -1071,8 +1271,9 @@ textconv_set_point (struct frame *f, ptrdiff_t point,
|
|||
point = MOST_POSITIVE_FIXNUM;
|
||||
|
||||
action = xmalloc (sizeof *action);
|
||||
action->operation = TEXTCONV_SET_POINT;
|
||||
action->data = make_fixnum (point);
|
||||
action->operation = TEXTCONV_SET_POINT_AND_MARK;
|
||||
action->data = Fcons (make_fixnum (point),
|
||||
make_fixnum (mark));
|
||||
action->next = NULL;
|
||||
action->counter = counter;
|
||||
for (last = &f->conversion.actions; *last; last = &(*last)->next)
|
||||
|
@ -1105,6 +1306,9 @@ delete_surrounding_text (struct frame *f, ptrdiff_t left,
|
|||
/* Return N characters of text around point in F's old selected
|
||||
window.
|
||||
|
||||
If N is -1, return the text between point and mark instead, given
|
||||
that the mark is active.
|
||||
|
||||
Set *N to the actual number of characters returned, *START_RETURN
|
||||
to the position of the first character returned, *OFFSET to the
|
||||
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,
|
|||
/* Temporarily switch to F's selected window at the time of the last
|
||||
redisplay. */
|
||||
Fselect_window (f->old_selected_window, Qt);
|
||||
buffer = NULL;
|
||||
|
||||
/* Figure out the bounds of the text to return. */
|
||||
start = PT - n / 2;
|
||||
end = PT + n - n / 2;
|
||||
if (n != -1)
|
||||
{
|
||||
start = PT - n / 2;
|
||||
end = PT + n - n / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!NILP (BVAR (current_buffer, mark_active))
|
||||
&& XMARKER (BVAR (current_buffer, mark))->buffer)
|
||||
{
|
||||
start = marker_position (BVAR (current_buffer, mark));
|
||||
end = PT;
|
||||
|
||||
/* Sort start and end. start_byte is used to hold a
|
||||
temporary value. */
|
||||
|
||||
if (start > end)
|
||||
{
|
||||
start_byte = end;
|
||||
end = start;
|
||||
start = start_byte;
|
||||
}
|
||||
}
|
||||
else
|
||||
goto finish;
|
||||
}
|
||||
|
||||
start = max (start, BEGV);
|
||||
end = min (end, ZV);
|
||||
buffer = NULL;
|
||||
|
||||
/* Detect overflow. */
|
||||
|
||||
|
@ -1218,3 +1447,37 @@ register_textconv_interface (struct textconv_interface *interface)
|
|||
{
|
||||
text_interface = interface;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
syms_of_textconv (void)
|
||||
{
|
||||
DEFSYM (Qaction, "action");
|
||||
DEFSYM (Qtext_conversion, "text-conversion");
|
||||
DEFSYM (Qpush_mark, "push-mark");
|
||||
DEFSYM (Qunderline, "underline");
|
||||
|
||||
DEFVAR_LISP ("text-conversion-edits", Vtext_conversion_edits,
|
||||
doc: /* List of buffers that were last edited as a result of text conversion.
|
||||
|
||||
This list can be used while handling a `text-conversion' event to
|
||||
determine the changes which have taken place.
|
||||
|
||||
Each element of the list describes a single edit in a buffer, of the
|
||||
form:
|
||||
|
||||
(BUFFER BEG END EPHEMERAL)
|
||||
|
||||
If an insertion or a change occured, then BEG and END are buffer
|
||||
positions denote the bounds of the text that was changed or inserted.
|
||||
If EPHEMERAL is t, then the input method will shortly make more
|
||||
changes to the text, so any actions that would otherwise be taken
|
||||
(such as indenting or automatically filling text) should not take
|
||||
place; otherwise, it is a string describing the text which was
|
||||
inserted.
|
||||
|
||||
If a deletion occured, then BEG and END are the same, and EPHEMERAL is
|
||||
nil. */);
|
||||
Vtext_conversion_edits = Qnil;
|
||||
}
|
||||
|
|
|
@ -35,9 +35,10 @@ struct textconv_interface
|
|||
unexpected buffer change occurs.) */
|
||||
void (*reset) (struct frame *);
|
||||
|
||||
/* Notice that point has moved in the specified frame's selected
|
||||
window's selected buffer. The second argument is the window
|
||||
whose point changed, and the third argument is the buffer. */
|
||||
/* Notice that point or mark has moved in the specified frame's
|
||||
selected window's selected buffer. The second argument is the
|
||||
window whose point changed, and the third argument is the
|
||||
buffer. */
|
||||
void (*point_changed) (struct frame *, struct window *,
|
||||
struct buffer *);
|
||||
|
||||
|
@ -118,6 +119,7 @@ struct textconv_callback_struct
|
|||
};
|
||||
|
||||
#define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0)
|
||||
#define TEXTCONV_SKIP_ACTIVE_REGION (1 << 1)
|
||||
|
||||
extern int textconv_query (struct frame *, struct textconv_callback_struct *,
|
||||
int);
|
||||
|
@ -132,7 +134,8 @@ extern void set_composing_text (struct frame *, Lisp_Object,
|
|||
ptrdiff_t, unsigned long);
|
||||
extern void set_composing_region (struct frame *, ptrdiff_t, ptrdiff_t,
|
||||
unsigned long);
|
||||
extern void textconv_set_point (struct frame *, ptrdiff_t, unsigned long);
|
||||
extern void textconv_set_point_and_mark (struct frame *, ptrdiff_t,
|
||||
ptrdiff_t, unsigned long);
|
||||
extern void delete_surrounding_text (struct frame *, ptrdiff_t,
|
||||
ptrdiff_t, unsigned long);
|
||||
extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
|
||||
|
|
|
@ -286,6 +286,10 @@ struct window
|
|||
it should be positive. */
|
||||
ptrdiff_t last_point;
|
||||
|
||||
/* Value of mark in the selected window at the time of the last
|
||||
redisplay. */
|
||||
ptrdiff_t last_mark;
|
||||
|
||||
/* Line number and position of a line somewhere above the top of the
|
||||
screen. If this field is zero, it means we don't have a base line. */
|
||||
ptrdiff_t base_line_number;
|
||||
|
|
11
src/xdisp.c
11
src/xdisp.c
|
@ -17268,7 +17268,7 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p)
|
|||
{
|
||||
struct buffer *b = XBUFFER (w->contents);
|
||||
#ifdef HAVE_TEXT_CONVERSION
|
||||
ptrdiff_t prev_point;
|
||||
ptrdiff_t prev_point, prev_mark;
|
||||
#endif
|
||||
|
||||
w->last_modified = accurate_p ? BUF_MODIFF (b) : 0;
|
||||
|
@ -17301,6 +17301,7 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p)
|
|||
|
||||
#ifdef HAVE_TEXT_CONVERSION
|
||||
prev_point = w->last_point;
|
||||
prev_mark = w->last_mark;
|
||||
#endif
|
||||
|
||||
if (w == XWINDOW (selected_window))
|
||||
|
@ -17308,6 +17309,11 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p)
|
|||
else
|
||||
w->last_point = marker_position (w->pointm);
|
||||
|
||||
if (XMARKER (BVAR (b, mark))->buffer == b)
|
||||
w->last_mark = marker_position (BVAR (b, mark));
|
||||
else
|
||||
w->last_mark = -1;
|
||||
|
||||
#ifdef HAVE_TEXT_CONVERSION
|
||||
/* Point motion is only propagated to the input method for use
|
||||
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)
|
|||
during such a change, so the consequences are not that
|
||||
severe. */
|
||||
|
||||
if (prev_point != w->last_point
|
||||
if ((prev_point != w->last_point
|
||||
|| prev_mark != w->last_mark)
|
||||
&& FRAME_WINDOW_P (WINDOW_XFRAME (w))
|
||||
&& w == XWINDOW (WINDOW_XFRAME (w)->selected_window))
|
||||
report_point_change (WINDOW_XFRAME (w), w, b);
|
||||
|
|
Loading…
Add table
Reference in a new issue