Check in new files
* src/textconv.c (copy_buffer): (textconv_query): (report_selected_window_change): (register_texconv_interface): * src/textconv.h (struct textconv_interface): (enum textconv_caret_direction): (enum textconv_operation): (struct textconv_conversion_text): (struct textconv_callback_struct): New files.
This commit is contained in:
parent
ae4ff4f25f
commit
9510e8ad68
2 changed files with 421 additions and 0 deletions
312
src/textconv.c
Normal file
312
src/textconv.c
Normal file
|
@ -0,0 +1,312 @@
|
|||
/* String conversion support for graphics terminals.
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* String conversion support.
|
||||
|
||||
Many input methods require access to text surrounding the cursor.
|
||||
They may then request that the text editor remove or substitute
|
||||
that text for something else, for example when providing the
|
||||
ability to ``undo'' or ``edit'' previously composed text. This is
|
||||
most commonly seen in input methods for CJK laguages for X Windows,
|
||||
and is extensively used throughout Android by input methods for all
|
||||
kinds of scripts. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "textconv.h"
|
||||
#include "buffer.h"
|
||||
#include "syntax.h"
|
||||
|
||||
|
||||
|
||||
/* The window system's text conversion interface.
|
||||
NULL when the window system has not set up text conversion.
|
||||
|
||||
This interface will later be heavily extended on the
|
||||
feature/android branch to deal with Android's much less
|
||||
straightforward text conversion protocols. */
|
||||
|
||||
static struct textconv_interface *text_interface;
|
||||
|
||||
|
||||
|
||||
/* Copy the portion of the current buffer described by BEG, BEG_BYTE,
|
||||
END, END_BYTE to the buffer BUFFER, which is END_BYTE - BEG_BYTEs
|
||||
long. */
|
||||
|
||||
static void
|
||||
copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
|
||||
ptrdiff_t end, ptrdiff_t end_byte,
|
||||
char *buffer)
|
||||
{
|
||||
ptrdiff_t beg0, end0, beg1, end1, size;
|
||||
|
||||
if (beg_byte < GPT_BYTE && GPT_BYTE < end_byte)
|
||||
{
|
||||
/* Two regions, before and after the gap. */
|
||||
beg0 = beg_byte;
|
||||
end0 = GPT_BYTE;
|
||||
beg1 = GPT_BYTE + GAP_SIZE - BEG_BYTE;
|
||||
end1 = end_byte + GAP_SIZE - BEG_BYTE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The only region. */
|
||||
beg0 = beg_byte;
|
||||
end0 = end_byte;
|
||||
beg1 = -1;
|
||||
end1 = -1;
|
||||
}
|
||||
|
||||
size = end0 - beg0;
|
||||
memcpy (buffer, BYTE_POS_ADDR (beg0), size);
|
||||
if (beg1 != -1)
|
||||
memcpy (buffer, BEG_ADDR + beg1, end1 - beg1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Conversion query. */
|
||||
|
||||
/* Perform the text conversion operation specified in QUERY and return
|
||||
the results.
|
||||
|
||||
Find the text between QUERY->position from point on F's selected
|
||||
window and QUERY->factor times QUERY->direction from that
|
||||
position. Return it in QUERY->text.
|
||||
|
||||
Then, either delete that text from the buffer if QUERY->operation
|
||||
is TEXTCONV_SUBSTITUTION, or return 0.
|
||||
|
||||
Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION
|
||||
or if deleting the text was successful, and 1 otherwise. */
|
||||
|
||||
int
|
||||
textconv_query (struct frame *f, struct textconv_callback_struct *query)
|
||||
{
|
||||
specpdl_ref count;
|
||||
ptrdiff_t pos, pos_byte, end, end_byte;
|
||||
ptrdiff_t temp, temp1;
|
||||
char *buffer;
|
||||
|
||||
/* Save the excursion, as there will be extensive changes to the
|
||||
selected window. */
|
||||
count = SPECPDL_INDEX ();
|
||||
record_unwind_protect_excursion ();
|
||||
|
||||
/* Inhibit quitting. */
|
||||
specbind (Qinhibit_quit, Qt);
|
||||
|
||||
/* Temporarily switch to F's selected window. */
|
||||
Fselect_window (f->selected_window, Qt);
|
||||
|
||||
/* Now find the appropriate text bounds for QUERY. First, move
|
||||
point QUERY->position steps forward or backwards. */
|
||||
|
||||
pos = PT;
|
||||
|
||||
/* 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. */
|
||||
|
||||
if (INT_ADD_WRAPV (pos, query->position, &pos))
|
||||
pos = PT;
|
||||
|
||||
if (pos < BEGV)
|
||||
pos = BEGV;
|
||||
|
||||
if (pos > ZV)
|
||||
pos = ZV;
|
||||
|
||||
/* Move to pos. */
|
||||
set_point (pos);
|
||||
pos = PT;
|
||||
pos_byte = PT_BYTE;
|
||||
|
||||
/* Now scan forward or backwards according to what is in QUERY. */
|
||||
|
||||
switch (query->direction)
|
||||
{
|
||||
case TEXTCONV_FORWARD_CHAR:
|
||||
/* Move forward by query->factor characters. */
|
||||
if (INT_ADD_WRAPV (pos, query->factor, &end) || end > ZV)
|
||||
end = ZV;
|
||||
|
||||
end_byte = CHAR_TO_BYTE (end);
|
||||
break;
|
||||
|
||||
case TEXTCONV_BACKWARD_CHAR:
|
||||
/* Move backward by query->factor characters. */
|
||||
if (INT_SUBTRACT_WRAPV (pos, query->factor, &end) || end < BEGV)
|
||||
end = BEGV;
|
||||
|
||||
end_byte = CHAR_TO_BYTE (end);
|
||||
break;
|
||||
|
||||
case TEXTCONV_FORWARD_WORD:
|
||||
/* Move forward by query->factor word. */
|
||||
end = scan_words (pos, (EMACS_INT) query->factor);
|
||||
|
||||
if (!end)
|
||||
{
|
||||
end = ZV;
|
||||
end_byte = ZV_BYTE;
|
||||
}
|
||||
else
|
||||
end_byte = CHAR_TO_BYTE (end);
|
||||
|
||||
break;
|
||||
|
||||
case TEXTCONV_BACKWARD_WORD:
|
||||
/* Move backwards by query->factor word. */
|
||||
end = scan_words (pos, 0 - (EMACS_INT) query->factor);
|
||||
|
||||
if (!end)
|
||||
{
|
||||
end = BEGV;
|
||||
end_byte = BEGV_BYTE;
|
||||
}
|
||||
else
|
||||
end_byte = CHAR_TO_BYTE (end);
|
||||
|
||||
break;
|
||||
|
||||
case TEXTCONV_CARET_UP:
|
||||
/* Move upwards one visual line, keeping the column intact. */
|
||||
Fvertical_motion (Fcons (Fcurrent_column (), make_fixnum (-1)),
|
||||
Qnil, Qnil);
|
||||
end = PT;
|
||||
end_byte = PT_BYTE;
|
||||
break;
|
||||
|
||||
case TEXTCONV_CARET_DOWN:
|
||||
/* Move downwards one visual line, keeping the column
|
||||
intact. */
|
||||
Fvertical_motion (Fcons (Fcurrent_column (), make_fixnum (1)),
|
||||
Qnil, Qnil);
|
||||
end = PT;
|
||||
end_byte = PT_BYTE;
|
||||
break;
|
||||
|
||||
case TEXTCONV_NEXT_LINE:
|
||||
/* Move one line forward. */
|
||||
scan_newline (pos, pos_byte, ZV, ZV_BYTE,
|
||||
query->factor, false);
|
||||
end = PT;
|
||||
end_byte = PT_BYTE;
|
||||
break;
|
||||
|
||||
case TEXTCONV_PREVIOUS_LINE:
|
||||
/* Move one line backwards. */
|
||||
scan_newline (pos, pos_byte, BEGV, BEGV_BYTE,
|
||||
0 - (EMACS_INT) query->factor, false);
|
||||
end = PT;
|
||||
end_byte = PT_BYTE;
|
||||
break;
|
||||
|
||||
case TEXTCONV_LINE_START:
|
||||
/* Move to the beginning of the line. */
|
||||
Fbeginning_of_line (Qnil);
|
||||
end = PT;
|
||||
end_byte = PT_BYTE;
|
||||
break;
|
||||
|
||||
case TEXTCONV_LINE_END:
|
||||
/* Move to the end of the line. */
|
||||
Fend_of_line (Qnil);
|
||||
end = PT;
|
||||
end_byte = PT_BYTE;
|
||||
break;
|
||||
|
||||
case TEXTCONV_ABSOLUTE_POSITION:
|
||||
/* How to implement this is unclear. */
|
||||
SET_PT (query->factor);
|
||||
end = PT;
|
||||
end_byte = PT_BYTE;
|
||||
break;
|
||||
|
||||
default:
|
||||
unbind_to (count, Qnil);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Sort end and pos. */
|
||||
|
||||
if (end < pos)
|
||||
{
|
||||
eassert (end_byte < pos_byte);
|
||||
temp = pos_byte;
|
||||
temp1 = pos;
|
||||
pos_byte = end_byte;
|
||||
pos = end;
|
||||
end = temp1;
|
||||
end_byte = temp;
|
||||
}
|
||||
|
||||
/* Return the string first. */
|
||||
buffer = xmalloc (end_byte - pos_byte);
|
||||
copy_buffer (pos, pos_byte, end, end_byte, buffer);
|
||||
query->text.text = buffer;
|
||||
query->text.length = end - pos;
|
||||
query->text.bytes = end_byte - pos_byte;
|
||||
|
||||
/* Next, perform any operation specified. */
|
||||
|
||||
switch (query->operation)
|
||||
{
|
||||
case TEXTCONV_SUBSTITUTION:
|
||||
if (safe_del_range (pos, end))
|
||||
{
|
||||
/* Undo any changes to the excursion. */
|
||||
unbind_to (count, Qnil);
|
||||
return 1;
|
||||
}
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
/* Undo any changes to the excursion. */
|
||||
unbind_to (count, Qnil);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Window system interface. These are called from the rest of
|
||||
Emacs. */
|
||||
|
||||
/* Notice that F's selected window has been set from redisplay.
|
||||
Reset F's input method state. */
|
||||
|
||||
void
|
||||
report_selected_window_change (struct frame *f)
|
||||
{
|
||||
if (!text_interface)
|
||||
return;
|
||||
|
||||
text_interface->reset (f);
|
||||
}
|
||||
|
||||
/* Register INTERFACE as the text conversion interface. */
|
||||
|
||||
void
|
||||
register_texconv_interface (struct textconv_interface *interface)
|
||||
{
|
||||
text_interface = interface;
|
||||
}
|
109
src/textconv.h
Normal file
109
src/textconv.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
/* String conversion support for graphics terminals.
|
||||
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _TEXTCONV_H_
|
||||
|
||||
#include "lisp.h"
|
||||
#include "frame.h"
|
||||
|
||||
/* The function pointers in this structure should be filled out by
|
||||
each GUI backend interested in supporting text conversion.
|
||||
|
||||
Finally, register_texconv_interface must be called at some point
|
||||
during terminal initialization. */
|
||||
|
||||
struct textconv_interface
|
||||
{
|
||||
/* Notice that the text conversion context has changed (which can
|
||||
happen if the window is deleted or switches buffers, or an
|
||||
unexpected buffer change occurs.) */
|
||||
void (*reset) (struct frame *);
|
||||
};
|
||||
|
||||
|
||||
|
||||
enum textconv_caret_direction
|
||||
{
|
||||
TEXTCONV_FORWARD_CHAR,
|
||||
TEXTCONV_BACKWARD_CHAR,
|
||||
TEXTCONV_FORWARD_WORD,
|
||||
TEXTCONV_BACKWARD_WORD,
|
||||
TEXTCONV_CARET_UP,
|
||||
TEXTCONV_CARET_DOWN,
|
||||
TEXTCONV_NEXT_LINE,
|
||||
TEXTCONV_PREVIOUS_LINE,
|
||||
TEXTCONV_LINE_START,
|
||||
TEXTCONV_LINE_END,
|
||||
TEXTCONV_ABSOLUTE_POSITION,
|
||||
};
|
||||
|
||||
enum textconv_operation
|
||||
{
|
||||
TEXTCONV_SUBSTITUTION,
|
||||
TEXTCONV_RETRIEVAL,
|
||||
};
|
||||
|
||||
/* Structure describing text in a buffer corresponding to a ``struct
|
||||
textconv_callback_struct''. */
|
||||
|
||||
struct textconv_conversion_text
|
||||
{
|
||||
/* Length of the text in characters and bytes. */
|
||||
size_t length, bytes;
|
||||
|
||||
/* Pointer to the text data. This must be deallocated by the
|
||||
caller. */
|
||||
char *text;
|
||||
};
|
||||
|
||||
/* Structure describing a single query submitted by the input
|
||||
method. */
|
||||
|
||||
struct textconv_callback_struct
|
||||
{
|
||||
/* Character position, relative to the current spot location, from
|
||||
where on text should be returned. */
|
||||
EMACS_INT position;
|
||||
|
||||
/* The type of scanning to perform to determine either the start or
|
||||
the end of the conversion. */
|
||||
enum textconv_caret_direction direction;
|
||||
|
||||
/* The the number of times for which to repeat the scanning in order
|
||||
to determine the starting position of the text to return. */
|
||||
unsigned short factor;
|
||||
|
||||
/* The operation to perform upon the current buffer contents.
|
||||
|
||||
If this is TEXTCONV_SUBSTITUTION, then the text that is returned
|
||||
will be deleted from the buffer itself.
|
||||
|
||||
Otherwise, the text is simply returned without modifying the
|
||||
buffer contents. */
|
||||
enum textconv_operation operation;
|
||||
|
||||
/* Structure that will be filled with a description of the resulting
|
||||
text. */
|
||||
struct textconv_conversion_text text;
|
||||
};
|
||||
|
||||
extern int textconv_query (struct frame *, struct textconv_callback_struct *);
|
||||
extern void register_texconv_interface (struct textconv_interface *);
|
||||
|
||||
#endif /* _TEXTCONV_H_ */
|
Loading…
Add table
Reference in a new issue