Patch 8.2.3524
Problem:    GUI: ligatures are not used.
Solution:   Add the 'guiligatures' option. (Dusan Popovic, closes #8933)
Files:      runtime/doc/options.txt, src/gui.c, src/gui.h, src/gui_gtk_x11.c,
            src/option.h, src/optiondefs.h, src/optionstr.c, src/errors.h,
            src/proto/gui.pro, src/proto/gui_gtk_x11.pro,
            src/testdir/test_gui.vim


*** ../vim-8.2.3523/runtime/doc/options.txt     2021-10-16 17:51:08.047842118 
+0100
--- runtime/doc/options.txt     2021-10-16 20:45:34.367702659 +0100
***************
*** 3779,3784 ****
--- 3790,3807 ----
        screen.  Set it to a negative value to allow windows taller than the
        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'     string  (default "egmrLtT"   (MS-Windows,
                                           "t" is removed in |defaults.vim|),
*** ../vim-8.2.3523/src/gui.c   2021-07-20 20:07:32.964058857 +0100
--- src/gui.c   2021-10-16 20:34:32.858636291 +0100
***************
*** 460,465 ****
--- 460,469 ----
      gui.scrollbar_width = gui.scrollbar_height = SB_DEFAULT_WIDTH;
      gui.prev_wrap = -1;
  
+ # ifdef FEAT_GUI_GTK
+     CLEAR_FIELD(gui.ligatures_map);
+ #endif
+ 
  #if defined(ALWAYS_USE_GUI) || defined(VIMDLL)
      result = OK;
  #else
***************
*** 1065,1070 ****
--- 1069,1104 ----
      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
  gui_set_cursor(int row, int col)
  {
*** ../vim-8.2.3523/src/gui.h   2020-12-30 12:14:41.950441890 +0000
--- src/gui.h   2021-10-16 20:33:58.110140304 +0100
***************
*** 409,414 ****
--- 409,417 ----
      char_u    *browse_fname;      // file name from filedlg
  
      guint32   event_time;
+ 
+     char_u ligatures_map[256];            // ascii map for characters 0-255, 
value is
+                                   // 1 if in 'guiligatures'
  #endif        // FEAT_GUI_GTK
  
  #if defined(FEAT_GUI_TABLINE) \
*** ../vim-8.2.3523/src/gui_gtk_x11.c   2021-06-02 12:28:11.427120469 +0100
--- src/gui_gtk_x11.c   2021-10-16 20:51:16.056225178 +0100
***************
*** 5595,5612 ****
      int
  gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags)
  {
!     GdkRectangle      area;               // area for clip mask
!     PangoGlyphString  *glyphs;            // glyphs of current item
!     int                       column_offset = 0;  // column offset in cells
!     int                       i;
!     char_u            *conv_buf = NULL;   // result of UTF-8 conversion
!     char_u            *new_conv_buf;
!     int                       convlen;
!     char_u            *sp, *bp;
!     int                       plen;
! #if GTK_CHECK_VERSION(3,0,0)
!     cairo_t           *cr;
! #endif
  
      if (gui.text_context == NULL || gtk_widget_get_window(gui.drawarea) == 
NULL)
        return len;
--- 5595,5616 ----
      int
  gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags)
  {
!     char_u    *conv_buf = NULL;   // result of UTF-8 conversion
!     char_u    *new_conv_buf;
!     int               convlen;
!     char_u    *sp, *bp;
!     int               plen;
!     int               len_sum;        // return value needs to add up since 
we are
!                               // printing substrings
!     int               byte_sum;       // byte position in string
!     char_u    *cs;            // current *s pointer
!     int               needs_pango;    // look ahead, 0=ascii 
1=unicode/ligatures
!     int               should_need_pango;
!     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)
        return len;
***************
*** 5653,5658 ****
--- 5657,5780 ----
      }
  
      /*
+      * 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
       * fuzzy font lookups from messing up the screen.
       */
***************
*** 5679,5685 ****
       */
      if (!(flags & DRAW_ITALIC)
            && !((flags & DRAW_BOLD) && gui.font_can_bold)
!           && gui.ascii_glyphs != NULL)
      {
        char_u *p;
  
--- 5801,5808 ----
       */
      if (!(flags & DRAW_ITALIC)
            && !((flags & DRAW_BOLD) && gui.font_can_bold)
!           && gui.ascii_glyphs != NULL
!           && !force_pango)
      {
        char_u *p;
  
***************
*** 5883,5889 ****
  #endif
  
      pango_glyph_string_free(glyphs);
-     vim_free(conv_buf);
  
  #if GTK_CHECK_VERSION(3,0,0)
      cairo_destroy(cr);
--- 6006,6011 ----
*** ../vim-8.2.3523/src/option.h        2021-10-16 15:41:25.378336694 +0100
--- src/option.h        2021-10-16 20:40:54.896084995 +0100
***************
*** 622,627 ****
--- 622,630 ----
  EXTERN char_u *p_guifontwide; // 'guifontwide'
  EXTERN int    p_guipty;       // 'guipty'
  #endif
+ #ifdef FEAT_GUI_GTK
+ EXTERN char_u *p_guiligatures;  // 'guiligatures'
+ # endif
  #if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11)
  EXTERN long   p_ghr;          // 'guiheadroom'
  #endif
*** ../vim-8.2.3523/src/optiondefs.h    2021-10-16 15:41:25.378336694 +0100
--- src/optiondefs.h    2021-10-16 20:25:49.587154629 +0100
***************
*** 1208,1213 ****
--- 1208,1226 ----
                            {(char_u *)NULL, (char_u *)0L}
  #endif
                            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,
  #if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11)
                            (char_u *)&p_ghr, PV_NONE,
*** ../vim-8.2.3523/src/optionstr.c     2021-10-16 15:41:25.378336694 +0100
--- src/optionstr.c     2021-10-16 20:25:49.587154629 +0100
***************
*** 1560,1565 ****
--- 1560,1572 ----
        redraw_gui_only = TRUE;
      }
  #endif
+ # if defined(FEAT_GUI_GTK)
+     else if (varp == &p_guiligatures)
+     {
+       gui_set_ligatures();
+       redraw_gui_only = TRUE;
+     }
+ # endif
  
  #ifdef CURSOR_SHAPE
      // 'guicursor'
*** ../vim-8.2.3523/src/errors.h        2021-10-13 15:04:28.859631740 +0100
--- src/errors.h        2021-10-16 20:31:57.952424519 +0100
***************
*** 670,672 ****
--- 670,674 ----
        INIT(= N_("E1241: Separator not supported: %s"));
  EXTERN char e_no_white_space_allowed_before_separator_str[]
        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"));
*** ../vim-8.2.3523/src/proto/gui.pro   2020-08-01 12:10:04.582539553 +0100
--- src/proto/gui.pro   2021-10-16 20:42:09.709104774 +0100
***************
*** 7,12 ****
--- 7,13 ----
  void gui_shell_closed(void);
  int gui_init_font(char_u *font_list, int fontset);
  int gui_get_wide_font(void);
+ void gui_set_ligatures(void);
  void gui_update_cursor(int force, int clear_selection);
  void gui_position_menu(void);
  int gui_get_base_width(void);
*** ../vim-8.2.3523/src/proto/gui_gtk_x11.pro   2021-03-18 21:28:53.563835682 
+0000
--- src/proto/gui_gtk_x11.pro   2021-10-16 20:25:49.587154629 +0100
***************
*** 43,48 ****
--- 43,49 ----
  void gui_mch_set_bg_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_ext(int row, int col, char_u *s, int len, int flags, 
int force_pango);
  int gui_mch_haskey(char_u *name);
  int gui_get_x11_windis(Window *win, Display **dis);
  Display *gui_mch_get_display(void);
*** ../vim-8.2.3523/src/testdir/test_gui.vim    2021-10-16 13:00:10.940165406 
+0100
--- src/testdir/test_gui.vim    2021-10-16 20:45:23.643563983 +0100
***************
*** 567,572 ****
--- 567,597 ----
    endif
  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()
    let skipped = ''
  
*** ../vim-8.2.3523/src/version.c       2021-10-16 19:07:40.459867578 +0100
--- src/version.c       2021-10-16 20:25:37.918987372 +0100
***************
*** 759,760 ****
--- 759,762 ----
  {   /* Add new patch number below this line */
+ /**/
+     3524,
  /**/

-- 
Living in Hollywood is like living in a bowl of granola.  What ain't
fruits and nuts is flakes.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20211016195239.DF18FC80053%40moolenaar.net.

Raspunde prin e-mail lui