summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYee Cheng Chin <ychin.git@gmail.com>2023-10-05 20:54:21 +0200
committerChristian Brabandt <cb@256bit.org>2023-10-05 20:54:21 +0200
commit290b887e8cc2c0d3dfc7f315b2052472c7c589cc (patch)
tree1b5285384cc6ed9e3a0bce9051d1e4b201196161
parentea746f9e862092aef3d4e95c64d116759b9fabe0 (diff)
patch 9.0.1991: no cmdline completion for setting the fontv9.0.1991
Problem: no cmdline completion for setting the font Solution: enable it on Win32 and GTK builds Add guifont cmdline completion (for Windows and GTK) For Windows, auto-complete will only suggest monospace fonts as that's the only types allowed. Will also suggest font options after the colon, including suggesting the current font size for convenience, and misc charset and quality options like `cANSI` and `qCLEARTYPE`. For GTK, auto-complete will suggest only monospace fonts for `guifont` but will include all fonts for `guifontwide`. The completion code doesn't currently suggest the current font size, as the GTK guifont format does not have a clear delimiter (':' for other platforms). closes: #13264 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
-rw-r--r--src/gui_gtk_x11.c56
-rw-r--r--src/optiondefs.h16
-rw-r--r--src/optionstr.c123
-rw-r--r--src/os_mswin.c121
-rw-r--r--src/proto/gui_gtk_x11.pro1
-rw-r--r--src/proto/optionstr.pro1
-rw-r--r--src/proto/os_mswin.pro1
-rw-r--r--src/testdir/test_gui.vim50
-rw-r--r--src/version.c2
9 files changed, 362 insertions, 9 deletions
diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c
index 3fd6f3a904..4b3f53ef91 100644
--- a/src/gui_gtk_x11.c
+++ b/src/gui_gtk_x11.c
@@ -5316,6 +5316,62 @@ gui_mch_free_font(GuiFont font)
}
/*
+ * Cmdline expansion for setting 'guifont' / 'guifontwide'. Will enumerate
+ * through all fonts for completion. When setting 'guifont' it will only show
+ * monospace fonts as it's unlikely other fonts would be useful.
+ */
+ void
+gui_mch_expand_font(optexpand_T *args, void *param, int (*add_match)(char_u *val))
+{
+ PangoFontFamily **font_families = NULL;
+ int n_families = 0;
+ int wide = *(int *)param;
+
+ if (args->oe_include_orig_val && *args->oe_opt_value == NUL && !wide)
+ {
+ // If guifont is empty, and we want to fill in the orig value, suggest
+ // the default so the user can modify it.
+ if (add_match((char_u *)DEFAULT_FONT) != OK)
+ return;
+ }
+
+ pango_context_list_families(
+ gui.text_context,
+ &font_families,
+ &n_families);
+
+ for (int i = 0; i < n_families; i++)
+ {
+ if (!wide && !pango_font_family_is_monospace(font_families[i]))
+ continue;
+
+ const char* fam_name = pango_font_family_get_name(font_families[i]);
+ if (input_conv.vc_type != CONV_NONE)
+ {
+ char_u *buf = string_convert(&input_conv, (char_u*)fam_name, NULL);
+ if (buf != NULL)
+ {
+ if (add_match(buf) != OK)
+ {
+ vim_free(buf);
+ break;
+ }
+ vim_free(buf);
+ }
+ else
+ break;
+ }
+ else
+ {
+ if (add_match((char_u *)fam_name) != OK)
+ break;
+ }
+ }
+
+ g_free(font_families);
+}
+
+/*
* Return the Pixel value (color) for the given color name.
*
* Return INVALCOLOR for error.
diff --git a/src/optiondefs.h b/src/optiondefs.h
index f850d19b1c..c4810e7d5d 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -1164,9 +1164,13 @@ static struct vimoption options[] =
{(char_u *)NULL, (char_u *)0L}
#endif
SCTX_INIT},
- {"guifont", "gfn", P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP,
+ {"guifont", "gfn", P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP
+#if !defined(FEAT_GUI_GTK)
+ |P_COLON
+#endif
+ ,
#ifdef FEAT_GUI
- (char_u *)&p_guifont, PV_NONE, did_set_guifont, NULL,
+ (char_u *)&p_guifont, PV_NONE, did_set_guifont, expand_set_guifont,
{(char_u *)"", (char_u *)0L}
#else
(char_u *)NULL, PV_NONE, NULL, NULL,
@@ -1183,10 +1187,14 @@ static struct vimoption options[] =
{(char_u *)NULL, (char_u *)0L}
#endif
SCTX_INIT},
- {"guifontwide", "gfw", P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP,
+ {"guifontwide", "gfw", P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP
+#if !defined(FEAT_GUI_GTK)
+ |P_COLON
+#endif
+ ,
#if defined(FEAT_GUI)
(char_u *)&p_guifontwide, PV_NONE,
- did_set_guifontwide, NULL,
+ did_set_guifontwide, expand_set_guifont,
{(char_u *)"", (char_u *)0L}
#else
(char_u *)NULL, PV_NONE, NULL, NULL,
diff --git a/src/optionstr.c b/src/optionstr.c
index 135cb9af68..6b40dcd706 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -731,7 +731,8 @@ did_set_option_listflag(char_u *val, char_u *flags, char *errbuf)
}
/*
- * Expand an option that accepts a list of string values.
+ * Expand an option that accepts a list of fixed string values with known
+ * number of items.
*/
static int
expand_set_opt_string(
@@ -801,10 +802,11 @@ static char_u *set_opt_callback_orig_option = NULL;
static char_u *((*set_opt_callback_func)(expand_T *, int));
/*
- * Callback used by expand_set_opt_generic to also include the original value.
+ * Callback used by expand_set_opt_generic to also include the original value
+ * as the first item.
*/
static char_u *
-expand_set_opt_callback(expand_T *xp, int idx)
+expand_set_opt_generic_cb(expand_T *xp, int idx)
{
if (idx == 0)
{
@@ -817,7 +819,8 @@ expand_set_opt_callback(expand_T *xp, int idx)
}
/*
- * Expand an option with a callback that iterates through a list of possible names.
+ * Expand an option with a callback that iterates through a list of possible
+ * names using an index.
*/
static int
expand_set_opt_generic(
@@ -838,7 +841,7 @@ expand_set_opt_generic(
args->oe_regmatch,
matches,
numMatches,
- expand_set_opt_callback,
+ expand_set_opt_generic_cb,
FALSE);
set_opt_callback_orig_option = NULL;
@@ -846,6 +849,95 @@ expand_set_opt_generic(
return ret;
}
+static garray_T *expand_cb_ga;
+static optexpand_T *expand_cb_args;
+
+/*
+ * Callback provided to a function in expand_set_opt_callback. Will perform
+ * regex matching against the value and add to the list.
+ *
+ * Returns OK usually. Returns FAIL if it failed to allocate memory, and the
+ * caller should terminate the enumeration.
+ */
+ static int
+expand_set_opt_callback_cb(char_u *val)
+{
+ regmatch_T *regmatch = expand_cb_args->oe_regmatch;
+ expand_T *xp = expand_cb_args->oe_xp;
+ garray_T *ga = expand_cb_ga;
+ char_u *str;
+
+ if (val == NULL || *val == NUL)
+ return OK;
+
+ if (xp->xp_pattern[0] != NUL &&
+ !vim_regexec(regmatch, val, (colnr_T)0))
+ return OK;
+
+ str = vim_strsave_escaped(val, (char_u *)" \t\\");
+
+ if (str == NULL)
+ return FAIL;
+
+ if (ga_grow(ga, 1) == FAIL)
+ {
+ vim_free(str);
+ return FAIL;
+ }
+
+ ((char_u **)ga->ga_data)[ga->ga_len] = str;
+ ++ga->ga_len;
+ return OK;
+}
+
+/*
+ * Expand an option with a provided function that takes a callback. The
+ * function will enumerate through all options and call the callback to add it
+ * to the list.
+ *
+ * "func" is the enumerator function that will generate the list of options.
+ * "func_params" is a single parameter that will be passed to func.
+ */
+ static int
+expand_set_opt_callback(
+ optexpand_T *args,
+ void (*func)(optexpand_T *, void* params, int (*cb)(char_u *val)),
+ void *func_params,
+ int *numMatches,
+ char_u ***matches)
+{
+ garray_T ga;
+ int include_orig_val = args->oe_include_orig_val;
+ char_u *option_val = args->oe_opt_value;
+
+ ga_init2(&ga, sizeof(char *), 30);
+
+ if (include_orig_val && *option_val != NUL)
+ {
+ char_u *p = vim_strsave(option_val);
+ if (p == NULL)
+ return FAIL;
+ if (ga_grow(&ga, 1) == FAIL)
+ {
+ vim_free(p);
+ return FAIL;
+ }
+ ((char_u **)ga.ga_data)[ga.ga_len] = p;
+ ++ga.ga_len;
+ }
+
+ expand_cb_ga = &ga;
+ expand_cb_args = args;
+
+ func(args, func_params, expand_set_opt_callback_cb);
+
+ expand_cb_ga = NULL;
+ expand_cb_args = NULL;
+
+ *matches = ga.ga_data;
+ *numMatches = ga.ga_len;
+ return OK;
+}
/*
* Expand an option which is a list of flags.
@@ -2237,6 +2329,27 @@ did_set_guifont(optset_T *args UNUSED)
return errmsg;
}
+/*
+ * Expand the 'guifont' option. Only when GUI is being used. Each platform has
+ * specific behaviors.
+ */
+ int
+expand_set_guifont(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+ if (!gui.in_use)
+ return FAIL;
+
+# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK)
+ char_u **varp = (char_u **)args->oe_varp;
+ int wide = (varp == &p_guifontwide);
+
+ return expand_set_opt_callback(
+ args, gui_mch_expand_font, &wide, numMatches, matches);
+# else
+ return FAIL;
+# endif
+}
+
# if defined(FEAT_XFONTSET) || defined(PROTO)
/*
* The 'guifontset' option is changed.
diff --git a/src/os_mswin.c b/src/os_mswin.c
index aa5fe5243e..46f73752f9 100644
--- a/src/os_mswin.c
+++ b/src/os_mswin.c
@@ -2819,6 +2819,32 @@ points_to_pixels(WCHAR *str, WCHAR **end, int vertical, long_i pprinter_dc)
return pixels;
}
+/*
+ * Convert pixel into point size. This is a reverse of points_to_pixels.
+ */
+ static double
+pixels_to_points(int pixels, int vertical, long_i pprinter_dc)
+{
+ double points = 0;
+ HWND hwnd = (HWND)0;
+ HDC hdc;
+ HDC printer_dc = (HDC)pprinter_dc;
+
+ if (printer_dc == NULL)
+ {
+ hwnd = GetDesktopWindow();
+ hdc = GetWindowDC(hwnd);
+ }
+ else
+ hdc = printer_dc;
+
+ points = pixels * 72.0 / GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX);
+ if (printer_dc == NULL)
+ ReleaseDC(hwnd, hdc);
+
+ return points;
+}
+
static int CALLBACK
font_enumproc(
ENUMLOGFONTW *elf,
@@ -2890,6 +2916,100 @@ init_logfont(LOGFONTW *lf)
}
/*
+ * Call back for EnumFontFamiliesW in expand_font_enumproc.
+ *
+ */
+ static int CALLBACK
+expand_font_enumproc(
+ ENUMLOGFONTW *elf,
+ NEWTEXTMETRICW *ntm UNUSED,
+ DWORD type UNUSED,
+ LPARAM lparam)
+{
+ LOGFONTW *lf = (LOGFONTW*)elf;
+
+# ifndef FEAT_PROPORTIONAL_FONTS
+ // Ignore non-monospace fonts without further ado
+ if ((ntm->tmPitchAndFamily & 1) != 0)
+ return 1;
+# endif
+
+ // Filter only on ANSI. Otherwise will see a lot of random fonts that we
+ // usually don't want.
+ if (lf->lfCharSet != ANSI_CHARSET)
+ return 1;
+
+ int (*add_match)(char_u *) = (int (*)(char_u *))lparam;
+
+ WCHAR *faceNameW = lf->lfFaceName;
+ char_u *faceName = utf16_to_enc(faceNameW, NULL);
+ if (!faceName)
+ return 0;
+
+ add_match(faceName);
+ vim_free(faceName);
+
+ return 1;
+}
+
+/*
+ * Cmdline expansion for setting 'guifont'. Will enumerate through all
+ * monospace fonts for completion. If used after ':', will expand to possible
+ * font configuration options like font sizes.
+ *
+ * This function has "gui" in its name because in some platforms (GTK) font
+ * handling is done by the GUI code, whereas in Windows it's part of the
+ * platform code.
+ */
+ void
+gui_mch_expand_font(optexpand_T *args, void *param UNUSED, int (*add_match)(char_u *val))
+{
+ expand_T *xp = args->oe_xp;
+ if (xp->xp_pattern > args->oe_set_arg && *(xp->xp_pattern-1) == ':')
+ {
+ char buf[30];
+
+ // Always fill in with the current font size as first option for
+ // convenience. We simply round to the closest integer for simplicity.
+ int font_height = (int)round(
+ pixels_to_points(-current_font_height, TRUE, (long_i)NULL));
+ vim_snprintf(buf, ARRAY_LENGTH(buf), "h%d", font_height);
+ add_match((char_u *)buf);
+
+ // Note: Keep this in sync with get_logfont(). Don't include 'c' and
+ // 'q' as we fill in all the values below.
+ static char *(p_gfn_win_opt_values[]) = {
+ "h" , "w" , "W" , "b" , "i" , "u" , "s"};
+ for (size_t i = 0; i < ARRAY_LENGTH(p_gfn_win_opt_values); i++)
+ add_match((char_u *)p_gfn_win_opt_values[i]);
+
+ struct charset_pair *cp;
+ for (cp = charset_pairs; cp->name != NULL; ++cp)
+ {
+ vim_snprintf(buf, ARRAY_LENGTH(buf), "c%s", cp->name);
+ add_match((char_u *)buf);
+ }
+ struct quality_pair *qp;
+ for (qp = quality_pairs; qp->name != NULL; ++qp)
+ {
+ vim_snprintf(buf, ARRAY_LENGTH(buf), "q%s", qp->name);
+ add_match((char_u *)buf);
+ }
+ return;
+ }
+
+ HWND hwnd = GetDesktopWindow();
+ HDC hdc = GetWindowDC(hwnd);
+
+ EnumFontFamiliesW(hdc,
+ NULL,
+ (FONTENUMPROCW)expand_font_enumproc,
+ (LPARAM)add_match);
+
+ ReleaseDC(hwnd, hdc);
+}
+
+/*
* Compare a UTF-16 string and an ASCII string literally.
* Only works all the code points are inside ASCII range.
*/
@@ -2995,6 +3115,7 @@ get_logfont(
{
switch (*p++)
{
+ // Note: Keep this in sync with gui_mch_expand_font().
case L'h':
lf->lfHeight = - points_to_pixels(p, &p, TRUE, (long_i)printer_dc);
break;
diff --git a/src/proto/gui_gtk_x11.pro b/src/proto/gui_gtk_x11.pro
index 3fa2ac96d6..459cdb9532 100644
--- a/src/proto/gui_gtk_x11.pro
+++ b/src/proto/gui_gtk_x11.pro
@@ -37,6 +37,7 @@ int gui_mch_init_font(char_u *font_name, int fontset);
GuiFont gui_mch_get_font(char_u *name, int report_error);
char_u *gui_mch_get_fontname(GuiFont font, char_u *name);
void gui_mch_free_font(GuiFont font);
+void gui_mch_expand_font(optexpand_T *args, void *param, int (*cb)(char_u *val));
guicolor_T gui_mch_get_color(char_u *name);
guicolor_T gui_mch_get_rgb_color(int r, int g, int b);
void gui_mch_set_fg_color(guicolor_T color);
diff --git a/src/proto/optionstr.pro b/src/proto/optionstr.pro
index 48cccea8cc..88034fcec5 100644
--- a/src/proto/optionstr.pro
+++ b/src/proto/optionstr.pro
@@ -152,6 +152,7 @@ int expand_set_foldclose(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_foldmethod(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_foldopen(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_formatoptions(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_guifont(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_guioptions(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_highlight(optexpand_T *args, int *numMatches, char_u ***matches);
int expand_set_jumpoptions(optexpand_T *args, int *numMatches, char_u ***matches);
diff --git a/src/proto/os_mswin.pro b/src/proto/os_mswin.pro
index d9172ee01b..9f534e09d3 100644
--- a/src/proto/os_mswin.pro
+++ b/src/proto/os_mswin.pro
@@ -51,5 +51,6 @@ void serverProcessPendingMessages(void);
char *charset_id2name(int id);
char *quality_id2name(DWORD id);
int get_logfont(LOGFONTW *lf, char_u *name, HDC printer_dc, int verbose);
+void gui_mch_expand_font(optexpand_T *args, void *param, int (*cb)(char_u *val));
void channel_init_winsock(void);
/* vim: set ft=c : */
diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim
index acc72f8395..e7f7c6d830 100644
--- a/src/testdir/test_gui.vim
+++ b/src/testdir/test_gui.vim
@@ -580,6 +580,56 @@ func Test_set_guifontwide()
endif
endfunc
+func Test_expand_guifont()
+ if has('gui_win32')
+ let guifont_saved = &guifont
+ let guifontwide_saved = &guifontwide
+
+ " Test recalling existing option, and suggesting current font size
+ set guifont=Courier\ New:h11:cANSI
+ call assert_equal('Courier\ New:h11:cANSI', getcompletion('set guifont=', 'cmdline')[0])
+ call assert_equal('h11', getcompletion('set guifont=Lucida\ Console:', 'cmdline')[0])
+
+ " Test auto-completion working for font names
+ call assert_equal(['Courier\ New'], getcompletion('set guifont=Couri*ew$', 'cmdline'))
+ call assert_equal(['Courier\ New'], getcompletion('set guifontwide=Couri*ew$', 'cmdline'))
+
+ " Make sure non-monospace fonts are filtered out
+ call assert_equal([], getcompletion('set guifont=Arial', 'cmdline'))
+ call assert_equal([], getcompletion('set guifontwide=Arial', 'cmdline'))
+
+ " Test auto-completion working for font options
+ call assert_notequal(-1, index(getcompletion('set guifont=Courier\ New:', 'cmdline'), 'b'))
+ call assert_equal(['cDEFAULT'], getcompletion('set guifont=Courier\ New:cD*T', 'cmdline'))
+ call assert_equal(['qCLEARTYPE'], getcompletion('set guifont=Courier\ New:qC*TYPE', 'cmdline'))
+
+ let &guifontwide = guifontwide_saved
+ let &guifont = guifont_saved
+ elseif has('gui_gtk')
+ let guifont_saved = &guifont
+ let guifontwide_saved = &guifontwide
+
+ " Test recalling default and existing option
+ set guifont=
+ call assert_equal('Monospace\ 10', getcompletion('set guifont=', 'cmdline')[0])
+ set guifont=Monospace\ 9
+ call assert_equal('Monospace\ 9', getcompletion('set guifont=', 'cmdline')[0])
+
+ " Test auto-completion working for font names
+ call assert_equal(['Monospace'], getcompletion('set guifont=Mono*pace$', 'cmdline'))
+ call assert_equal(['Monospace'], getcompletion('set guifontwide=Mono*pace$', 'cmdline'))
+
+ " Make sure non-monospace fonts are filtered out only in 'guifont'
+ call assert_equal([], getcompletion('set guifont=Sans$', 'cmdline'))
+ call assert_equal(['Sans'], getcompletion('set guifontwide=Sans$', 'cmdline'))
+
+ let &guifontwide = guifontwide_saved
+ let &guifont = guifont_saved
+ else
+ call assert_equal([], getcompletion('set guifont=', 'cmdline'))
+ endif
+endfunc
+
func Test_set_guiligatures()
CheckX11BasedGui
diff --git a/src/version.c b/src/version.c
index 0f73dc25ae..9b1c0b4e92 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1991,
+/**/
1990,
/**/
1989,