Repository: kovidgoyal/kitty

Issue:
Disabling ligatures disables other OpenType features (which are enabled by default) too
I discovered this while thinking about ctrlcctrlv/TT2020#3

The problem is this:

https://github.com/kovidgoyal/kitty/blob/70071fe1f679f8fbd514fb061df02b55628e7c9f/kitty/fonts.c#L779

It turns off all OpenType shaping if `disable_ligatures` is true, not only ligatures. `disable_shaping` is a more accurate name for this option.

It would be better if I could have more fine tuning, e.g.&mdash; 

```yaml
disable_ligatures always # Internally appends to below option "liga" 0, "clig" 0, "dlig" 0 
font_shaping_tags "calt" 1, "ss02" 1
```

> [`hb_feature_from_string()`](http://www.manpagez.com/html/harfbuzz/harfbuzz-1.0.1/harfbuzz-Shaping.php#hb-feature-from-string) now accepts CSS font-feature-settings format.

I am working on this right now so would appreciate being given time to submit a PR. If I fail to figure it out I'll let you know, but the code seems well done, and the problem simple (for me, since I know my way around OpenType fonts and also HarfBuzz).


Discussion:

Resolving patch:
diff --git a/kitty/config.py b/kitty/config.py
--- a/kitty/config.py
+++ b/kitty/config.py
@@ -410,6 +410,19 @@ def handle_symbol_map(key, val, ans):
     ans['symbol_map'].update(parse_symbol_map(val))
 
 
+@special_handler
+def handle_font_feature_settings(key, val, ans):
+    parts = val.split(':', maxsplit=2)
+    if len(parts) != 2:
+        if parts[0] == "none":
+            return
+        else:
+            log_error("Ignoring invalid font_feature_settings for font {}".format(parts[0]))
+    else:
+        font, features = parts
+        ans['font_feature_settings'].update({font.strip(): features.strip().split()})
+
+
 @special_handler
 def handle_kitten_alias(key, val, ans):
     parts = val.split(maxsplit=2)
@@ -495,7 +508,7 @@ def option_names_for_completion():
 
 
 def parse_config(lines, check_keys=True, accumulate_bad_lines=None):
-    ans = {'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': [], 'env': {}, 'kitten_aliases': {}}
+    ans = {'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': [], 'env': {}, 'kitten_aliases': {}, 'font_feature_settings': {}}
     parse_config_base(
         lines,
         defaults,
diff --git a/kitty/config_data.py b/kitty/config_data.py
--- a/kitty/config_data.py
+++ b/kitty/config_data.py
@@ -276,6 +276,45 @@ def disable_ligatures(x):
     map alt+1 disable_ligatures_in active always
     map alt+2 disable_ligatures_in all never
     map alt+3 disable_ligatures_in tab cursor
+
+Note: This function is equivalent to setting :code:`font_feature_settings` to
+:code:`-liga -dlig -calt`.
+'''))
+
+o('font_feature_settings', 'none', long_text=_('''
+Choose exactly which OpenType features to enable or disable. This is useful as
+some fonts might have many features worthwhile in a terminal—for example, Fira
+Code Retina includes a discretionary feature, :code:`zero`, which in that font
+changes the appearance of the zero (0), to make it more easily distinguishable
+from Ø. Fira Code Retina also includes other discretionary features known as
+Stylistic Sets which have the tags :code:`ss01` through :code:`ss20`.
+
+Note that this code is indexed by PostScript name, and not TTF name or font
+family; this allows you to define very precise feature settings; e.g. you can
+disable a feature in the italic font but not in the regular font.
+
+To get the PostScript name for a font, ask Fontconfig for it, using your family
+name::
+
+    $ fc-match "Fira Code" postscriptname
+    :postscriptname=FiraCode-Regular
+    $ fc-match "Fira Code Retina" postscriptname
+    :postscriptname=FiraCode-Retina
+    $ fc-match "TT2020Base:style=italic" postscriptname
+    :postscriptname=TT2020Base-Italic
+
+Enable alternate zero and oldstyle numerals::
+
+    font_feature_settings FiraCode-Retina: +zero +onum
+
+Enable only alternate zero::
+
+    font_feature_settings FiraCode-Retina: +zero
+
+Disable the normal ligatures, but keep the :code:`calt` feature which (in this
+font) breaks up monotony::
+
+    font_feature_settings TT2020StyleB-Regular: -liga +calt
 '''))
 
 
diff --git a/kitty/fonts/render.py b/kitty/fonts/render.py
--- a/kitty/fonts/render.py
+++ b/kitty/fonts/render.py
@@ -103,7 +103,7 @@ def set_font_family(opts=None, override_font_size=None, debug_font_matching=Fals
     set_font_data(
         render_box_drawing, prerender_function, descriptor_for_idx,
         indices['bold'], indices['italic'], indices['bi'], num_symbol_fonts,
-        sm, sz
+        sm, sz, opts['font_feature_settings']
     )
 
 


Test patch: