mirror of
https://github.com/vim/vim.git
synced 2025-09-27 04:14:06 -04:00
patch 8.2.3524: GUI: ligatures are not used
Problem: GUI: ligatures are not used. Solution: Add the 'guiligatures' option. (Dusan Popovic, closes #8933)
This commit is contained in:
committed by
Bram Moolenaar
parent
c89c91cafd
commit
4eeedc09fe
@@ -3790,6 +3790,18 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
screen. Set it to a negative value to allow windows taller than the
|
screen. Set it to a negative value to allow windows taller than the
|
||||||
screen.
|
screen.
|
||||||
|
|
||||||
|
*'guiligatures'* *'gli'* *E1243*
|
||||||
|
'guiligatures' 'gli' string (default "")
|
||||||
|
global
|
||||||
|
{only for GTK GUI}
|
||||||
|
List of ASCII characters that, when combined together, can create more
|
||||||
|
complex shapes. Each character must be a printable ASCII character
|
||||||
|
with a value in the 32-127 range.
|
||||||
|
Example: >
|
||||||
|
:set guiligatures=!\"#$%&()*+-./:<=>?@[]^_{\|~
|
||||||
|
< Changing this option updates screen output immediately. Set it to an
|
||||||
|
empty string to disable ligatures.
|
||||||
|
|
||||||
*'guioptions'* *'go'*
|
*'guioptions'* *'go'*
|
||||||
'guioptions' 'go' string (default "egmrLtT" (MS-Windows,
|
'guioptions' 'go' string (default "egmrLtT" (MS-Windows,
|
||||||
"t" is removed in |defaults.vim|),
|
"t" is removed in |defaults.vim|),
|
||||||
|
@@ -670,3 +670,5 @@ EXTERN char e_separator_not_supported_str[]
|
|||||||
INIT(= N_("E1241: Separator not supported: %s"));
|
INIT(= N_("E1241: Separator not supported: %s"));
|
||||||
EXTERN char e_no_white_space_allowed_before_separator_str[]
|
EXTERN char e_no_white_space_allowed_before_separator_str[]
|
||||||
INIT(= N_("E1242: No white space allowed before separator: %s"));
|
INIT(= N_("E1242: No white space allowed before separator: %s"));
|
||||||
|
EXTERN char e_ascii_code_not_in_range[]
|
||||||
|
INIT(= N_("E1243: ASCII code not in 32-127 range"));
|
||||||
|
34
src/gui.c
34
src/gui.c
@@ -460,6 +460,10 @@ gui_init_check(void)
|
|||||||
gui.scrollbar_width = gui.scrollbar_height = SB_DEFAULT_WIDTH;
|
gui.scrollbar_width = gui.scrollbar_height = SB_DEFAULT_WIDTH;
|
||||||
gui.prev_wrap = -1;
|
gui.prev_wrap = -1;
|
||||||
|
|
||||||
|
# ifdef FEAT_GUI_GTK
|
||||||
|
CLEAR_FIELD(gui.ligatures_map);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(ALWAYS_USE_GUI) || defined(VIMDLL)
|
#if defined(ALWAYS_USE_GUI) || defined(VIMDLL)
|
||||||
result = OK;
|
result = OK;
|
||||||
#else
|
#else
|
||||||
@@ -1065,6 +1069,36 @@ gui_get_wide_font(void)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(FEAT_GUI_GTK) || defined(PROTO)
|
||||||
|
/*
|
||||||
|
* Set list of ascii characters that combined can create ligature.
|
||||||
|
* Store them in char map for quick access from gui_gtk2_draw_string.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gui_set_ligatures(void)
|
||||||
|
{
|
||||||
|
char_u *p;
|
||||||
|
|
||||||
|
if (*p_guiligatures != NUL)
|
||||||
|
{
|
||||||
|
// check for invalid characters
|
||||||
|
for (p = p_guiligatures; *p != NUL; ++p)
|
||||||
|
if (*p < 32 || *p > 127)
|
||||||
|
{
|
||||||
|
emsg(_(e_ascii_code_not_in_range));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store valid setting into ligatures_map
|
||||||
|
CLEAR_FIELD(gui.ligatures_map);
|
||||||
|
for (p = p_guiligatures; *p != NUL; ++p)
|
||||||
|
gui.ligatures_map[*p] = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
CLEAR_FIELD(gui.ligatures_map);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gui_set_cursor(int row, int col)
|
gui_set_cursor(int row, int col)
|
||||||
{
|
{
|
||||||
|
@@ -409,6 +409,9 @@ typedef struct Gui
|
|||||||
char_u *browse_fname; // file name from filedlg
|
char_u *browse_fname; // file name from filedlg
|
||||||
|
|
||||||
guint32 event_time;
|
guint32 event_time;
|
||||||
|
|
||||||
|
char_u ligatures_map[256]; // ascii map for characters 0-255, value is
|
||||||
|
// 1 if in 'guiligatures'
|
||||||
#endif // FEAT_GUI_GTK
|
#endif // FEAT_GUI_GTK
|
||||||
|
|
||||||
#if defined(FEAT_GUI_TABLINE) \
|
#if defined(FEAT_GUI_TABLINE) \
|
||||||
|
@@ -5595,18 +5595,22 @@ draw_under(int flags, int row, int col, int cells)
|
|||||||
int
|
int
|
||||||
gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags)
|
gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags)
|
||||||
{
|
{
|
||||||
GdkRectangle area; // area for clip mask
|
char_u *conv_buf = NULL; // result of UTF-8 conversion
|
||||||
PangoGlyphString *glyphs; // glyphs of current item
|
char_u *new_conv_buf;
|
||||||
int column_offset = 0; // column offset in cells
|
int convlen;
|
||||||
int i;
|
char_u *sp, *bp;
|
||||||
char_u *conv_buf = NULL; // result of UTF-8 conversion
|
int plen;
|
||||||
char_u *new_conv_buf;
|
int len_sum; // return value needs to add up since we are
|
||||||
int convlen;
|
// printing substrings
|
||||||
char_u *sp, *bp;
|
int byte_sum; // byte position in string
|
||||||
int plen;
|
char_u *cs; // current *s pointer
|
||||||
#if GTK_CHECK_VERSION(3,0,0)
|
int needs_pango; // look ahead, 0=ascii 1=unicode/ligatures
|
||||||
cairo_t *cr;
|
int should_need_pango;
|
||||||
#endif
|
int slen;
|
||||||
|
int is_ligature;
|
||||||
|
int next_is_ligature;
|
||||||
|
int is_utf8;
|
||||||
|
char_u backup_ch;
|
||||||
|
|
||||||
if (gui.text_context == NULL || gtk_widget_get_window(gui.drawarea) == NULL)
|
if (gui.text_context == NULL || gtk_widget_get_window(gui.drawarea) == NULL)
|
||||||
return len;
|
return len;
|
||||||
@@ -5652,6 +5656,124 @@ gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags)
|
|||||||
len = convlen;
|
len = convlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ligature support and complex utf-8 char optimization:
|
||||||
|
* String received to output to screen can print using pre-cached glyphs
|
||||||
|
* (fast) or Pango (slow). Ligatures and multibype utf-8 must use Pango.
|
||||||
|
* Since we receive mixed content string, split it into logical segments
|
||||||
|
* that are guaranteed to go trough glyphs as much as possible. Since
|
||||||
|
* single ligature char prints as ascii, print it that way.
|
||||||
|
*/
|
||||||
|
len_sum = 0; // return value needs to add up since we are printing
|
||||||
|
// substrings
|
||||||
|
byte_sum = 0;
|
||||||
|
cs = s;
|
||||||
|
// look ahead, 0=ascii 1=unicode/ligatures
|
||||||
|
needs_pango = ((*cs & 0x80) || gui.ligatures_map[*cs]);
|
||||||
|
|
||||||
|
// split string into ascii and non-ascii (ligatures + utf-8) substrings,
|
||||||
|
// print glyphs or use Pango
|
||||||
|
while (cs < s + len)
|
||||||
|
{
|
||||||
|
slen = 0;
|
||||||
|
while (slen < (len - byte_sum))
|
||||||
|
{
|
||||||
|
is_ligature = gui.ligatures_map[*(cs + slen)];
|
||||||
|
// look ahead, single ligature char between ascii is ascii
|
||||||
|
if (is_ligature && !needs_pango)
|
||||||
|
{
|
||||||
|
if ((slen + 1) < (len - byte_sum))
|
||||||
|
{
|
||||||
|
next_is_ligature = gui.ligatures_map[*(cs + slen + 1)];
|
||||||
|
if (!next_is_ligature)
|
||||||
|
is_ligature = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
is_ligature = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is_utf8 = *(cs + slen) & 0x80;
|
||||||
|
should_need_pango = (is_ligature || is_utf8);
|
||||||
|
if (needs_pango != should_need_pango) // mode switch
|
||||||
|
break;
|
||||||
|
if (needs_pango)
|
||||||
|
{
|
||||||
|
if (is_ligature)
|
||||||
|
{
|
||||||
|
slen++; // ligature char by char
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((*(cs + slen) & 0xC0) == 0x80)
|
||||||
|
{
|
||||||
|
// a continuation, find next 0xC0 != 0x80 but don't
|
||||||
|
// include it
|
||||||
|
while ((slen < (len - byte_sum))
|
||||||
|
&& ((*(cs + slen) & 0xC0) == 0x80))
|
||||||
|
{
|
||||||
|
slen++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((*(cs + slen) & 0xE0) == 0xC0)
|
||||||
|
{
|
||||||
|
// + one byte utf8
|
||||||
|
slen++;
|
||||||
|
}
|
||||||
|
else if ((*(cs + slen) & 0xF0) == 0xE0)
|
||||||
|
{
|
||||||
|
// + two bytes utf8
|
||||||
|
slen += 2;
|
||||||
|
}
|
||||||
|
else if ((*(cs + slen) & 0xF8) == 0xF0)
|
||||||
|
{
|
||||||
|
// + three bytes utf8
|
||||||
|
slen += 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// this should not happen, try moving forward, Pango
|
||||||
|
// will catch it
|
||||||
|
slen++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slen++; // ascii
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// temporarily zero terminate substring, print, restore char, wrap
|
||||||
|
backup_ch = *(cs + slen);
|
||||||
|
*(cs + slen) = 0;
|
||||||
|
len_sum += gui_gtk2_draw_string_ext(row, col + len_sum,
|
||||||
|
cs, slen, flags, needs_pango);
|
||||||
|
*(cs + slen) = backup_ch;
|
||||||
|
cs += slen;
|
||||||
|
byte_sum += slen;
|
||||||
|
needs_pango = should_need_pango;
|
||||||
|
}
|
||||||
|
vim_free(conv_buf);
|
||||||
|
return len_sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
gui_gtk2_draw_string_ext(
|
||||||
|
int row,
|
||||||
|
int col,
|
||||||
|
char_u *s,
|
||||||
|
int len,
|
||||||
|
int flags,
|
||||||
|
int force_pango)
|
||||||
|
{
|
||||||
|
GdkRectangle area; // area for clip mask
|
||||||
|
PangoGlyphString *glyphs; // glyphs of current item
|
||||||
|
int column_offset = 0; // column offset in cells
|
||||||
|
int i;
|
||||||
|
#if GTK_CHECK_VERSION(3,0,0)
|
||||||
|
cairo_t *cr;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Restrict all drawing to the current screen line in order to prevent
|
* Restrict all drawing to the current screen line in order to prevent
|
||||||
* fuzzy font lookups from messing up the screen.
|
* fuzzy font lookups from messing up the screen.
|
||||||
@@ -5679,7 +5801,8 @@ gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags)
|
|||||||
*/
|
*/
|
||||||
if (!(flags & DRAW_ITALIC)
|
if (!(flags & DRAW_ITALIC)
|
||||||
&& !((flags & DRAW_BOLD) && gui.font_can_bold)
|
&& !((flags & DRAW_BOLD) && gui.font_can_bold)
|
||||||
&& gui.ascii_glyphs != NULL)
|
&& gui.ascii_glyphs != NULL
|
||||||
|
&& !force_pango)
|
||||||
{
|
{
|
||||||
char_u *p;
|
char_u *p;
|
||||||
|
|
||||||
@@ -5883,7 +6006,6 @@ skipitall:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
pango_glyph_string_free(glyphs);
|
pango_glyph_string_free(glyphs);
|
||||||
vim_free(conv_buf);
|
|
||||||
|
|
||||||
#if GTK_CHECK_VERSION(3,0,0)
|
#if GTK_CHECK_VERSION(3,0,0)
|
||||||
cairo_destroy(cr);
|
cairo_destroy(cr);
|
||||||
|
@@ -622,6 +622,9 @@ EXTERN char_u *p_guifontset; // 'guifontset'
|
|||||||
EXTERN char_u *p_guifontwide; // 'guifontwide'
|
EXTERN char_u *p_guifontwide; // 'guifontwide'
|
||||||
EXTERN int p_guipty; // 'guipty'
|
EXTERN int p_guipty; // 'guipty'
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef FEAT_GUI_GTK
|
||||||
|
EXTERN char_u *p_guiligatures; // 'guiligatures'
|
||||||
|
# endif
|
||||||
#if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11)
|
#if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11)
|
||||||
EXTERN long p_ghr; // 'guiheadroom'
|
EXTERN long p_ghr; // 'guiheadroom'
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1208,6 +1208,19 @@ static struct vimoption options[] =
|
|||||||
{(char_u *)NULL, (char_u *)0L}
|
{(char_u *)NULL, (char_u *)0L}
|
||||||
#endif
|
#endif
|
||||||
SCTX_INIT},
|
SCTX_INIT},
|
||||||
|
|
||||||
|
|
||||||
|
{"guiligatures", "gli", P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP,
|
||||||
|
#if defined(FEAT_GUI_GTK)
|
||||||
|
(char_u *)&p_guiligatures, PV_NONE,
|
||||||
|
{(char_u *)"", (char_u *)0L}
|
||||||
|
#else
|
||||||
|
(char_u *)NULL, PV_NONE,
|
||||||
|
{(char_u *)NULL, (char_u *)0L}
|
||||||
|
#endif
|
||||||
|
SCTX_INIT},
|
||||||
|
|
||||||
|
|
||||||
{"guiheadroom", "ghr", P_NUM|P_VI_DEF,
|
{"guiheadroom", "ghr", P_NUM|P_VI_DEF,
|
||||||
#if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11)
|
#if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11)
|
||||||
(char_u *)&p_ghr, PV_NONE,
|
(char_u *)&p_ghr, PV_NONE,
|
||||||
|
@@ -1560,6 +1560,13 @@ ambw_end:
|
|||||||
redraw_gui_only = TRUE;
|
redraw_gui_only = TRUE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
# if defined(FEAT_GUI_GTK)
|
||||||
|
else if (varp == &p_guiligatures)
|
||||||
|
{
|
||||||
|
gui_set_ligatures();
|
||||||
|
redraw_gui_only = TRUE;
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
#ifdef CURSOR_SHAPE
|
#ifdef CURSOR_SHAPE
|
||||||
// 'guicursor'
|
// 'guicursor'
|
||||||
|
@@ -7,6 +7,7 @@ void gui_exit(int rc);
|
|||||||
void gui_shell_closed(void);
|
void gui_shell_closed(void);
|
||||||
int gui_init_font(char_u *font_list, int fontset);
|
int gui_init_font(char_u *font_list, int fontset);
|
||||||
int gui_get_wide_font(void);
|
int gui_get_wide_font(void);
|
||||||
|
void gui_set_ligatures(void);
|
||||||
void gui_update_cursor(int force, int clear_selection);
|
void gui_update_cursor(int force, int clear_selection);
|
||||||
void gui_position_menu(void);
|
void gui_position_menu(void);
|
||||||
int gui_get_base_width(void);
|
int gui_get_base_width(void);
|
||||||
|
@@ -43,6 +43,7 @@ void gui_mch_set_fg_color(guicolor_T color);
|
|||||||
void gui_mch_set_bg_color(guicolor_T color);
|
void gui_mch_set_bg_color(guicolor_T color);
|
||||||
void gui_mch_set_sp_color(guicolor_T color);
|
void gui_mch_set_sp_color(guicolor_T color);
|
||||||
int gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags);
|
int gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags);
|
||||||
|
int gui_gtk2_draw_string_ext(int row, int col, char_u *s, int len, int flags, int force_pango);
|
||||||
int gui_mch_haskey(char_u *name);
|
int gui_mch_haskey(char_u *name);
|
||||||
int gui_get_x11_windis(Window *win, Display **dis);
|
int gui_get_x11_windis(Window *win, Display **dis);
|
||||||
Display *gui_mch_get_display(void);
|
Display *gui_mch_get_display(void);
|
||||||
|
@@ -567,6 +567,31 @@ func Test_set_guifontwide()
|
|||||||
endif
|
endif
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_set_guiligatures()
|
||||||
|
let skipped = ''
|
||||||
|
|
||||||
|
if !g:x11_based_gui
|
||||||
|
let skipped = g:not_supported . 'guiligatures'
|
||||||
|
else
|
||||||
|
if has('gui_gtk') || has('gui_gtk2') || has('gui_gnome') || has('gui_gtk3')
|
||||||
|
" Try correct value
|
||||||
|
set guiligatures=<>=ab
|
||||||
|
call assert_equal("<>=ab", &guiligatures)
|
||||||
|
" Try to throw error
|
||||||
|
try
|
||||||
|
set guiligatures=<>=šab
|
||||||
|
call assert_report("'set guiligatures=<>=šab should have failed")
|
||||||
|
catch
|
||||||
|
call assert_exception('E1243:')
|
||||||
|
endtry
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !empty(skipped)
|
||||||
|
throw skipped
|
||||||
|
endif
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_set_guiheadroom()
|
func Test_set_guiheadroom()
|
||||||
let skipped = ''
|
let skipped = ''
|
||||||
|
|
||||||
|
@@ -757,6 +757,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
3524,
|
||||||
/**/
|
/**/
|
||||||
3523,
|
3523,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user