htmlreactjsfontscodemirror

Ligatures does not show up in codemirror editor using font that supports them


I'm using the latest version of Codemirror. From my package.json:

"codemirror": "^6.0.1",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-sql": "^6.8.0",

And I'm setting a font installed on my system locally:

    cfg.push(EditorView.theme({
      '& .cm-content': {
        fontSize: fontSize,
        fontFamily: fontFamily, // <-- This gets set to "CaskaydiaCove Nerd Font Mono"
      },
      '.cm-gutters': {
        fontSize: fontSize,
      },
    }));

In the editor, I can confirm my font gets loaded. However, when I enter the text:

=== != ?= <- >=

I don't see any of the coding font ligatures. I can run the same code on JSFiddle:

<html lang="en">
<head>
  <style>
    .lig-support {
      font-family: "CaskaydiaCove Nerd Font Mono";
      font-size: 19pt;
    }
  </style>
</head>
<body bgcolor="white">
  <div class="lig-support" contenteditable="true">
    <span>
      === != ?= <- >=
    </span>
  </div>
</body>
</html>

And I see all the ligatures correctly. This means something in Codemirror is disabling the ligatures, however I cannot see anything in the Chrome tools or computed styles that would indicate this nor can I find anything in the Codemirror source code that appears it would do this. I've tried on both Brave and Chrome browsers. Any ideas? Thanks!

Update: For those of you who don't happen to have this font installed, here's what it looks like on my machine with the StackOverflow preview:

enter image description here

Once again, the ligatures work in the StackOverflow preview and various HTML playgrounds, but do not work in Codemirror.


Solution

  • By default codemirror should enable – or at least not disable – these code specific ligatures like in "CaskaydiaCove Nerd Font Mono".

    On the other hand it's safe to say that codemirror doesn't really prioritize legacy compatibility and may use different CSS class name schemes depending on the used version.

    Recommendations

    1. test your codemirror setup with the most minimal settings - e.g using a default theme with a manually defined font-family in CSS. If a this basic setup shows the ligatures correctly - there might be something wrong with a custom template
    2. Inspect your rendering in the browser via devtools and check the computed styles: These will tell you whether the rendered font is the defined family or a stytem default. Ideally, the codemirror elements have some explicit CSS rules enabling ligatures such as: font-variant-ligatures: contextual; or font-variant-ligatures: normal;
    3. Don't rely on locally installed fonts and prefer font loading via CSS @font-face. Local/system fonts can introduce weird cross-browser inconsistencies as some browser may block local fonts completely or choose another style/weight with the same internal font name
    4. letter-spacing will also disable any ligatures so make sure letter-spacing is set to zero or normal: letter-spacing: 0

    const { basicSetup } = CM["codemirror"];
    const { EditorView, keymap, lineNumbers } = CM["@codemirror/view"];
    const { EditorState } = CM["@codemirror/state"];
    const { javascript } = CM["@codemirror/lang-javascript"];
    //const { defaultKeymap } = CM["@codemirror/commands"];
    
    //let mode = legacyMode({ mode: javascript({ indentUnit: 4 }, {}) });
    
    let state = EditorState.create({
        doc: `const test = (e)=>{
        let test = 1
        if(test===0 || test <=-2){
            console.log('test')
            }
        }`,
        extensions: [
            lineNumbers(),
            javascript(),
            basicSetup
        ]
    });
    
    let view = new EditorView({state});
    document.querySelector("#editor").append(view.dom);
    
    let view2 = new EditorView({state});
    document.querySelector("#editor2").append(view2.dom);
    
    let view3 = new EditorView({state});
    document.querySelector("#editor3").append(view3.dom);
    
    let view4 = new EditorView({state});
    document.querySelector("#editor4").append(view4.dom);
    @import 'https://fonts.googleapis.com/css2?family=Fira+Code:wght@300..700';
    
    
    @font-face {
        font-family: "CaskaydiaCove Nerd Font Mono";
        font-weight: 400;
        src: url("https://www.programmingfonts.org/fonts/resources/cascadia-code/cascadia-code.woff2")
            format("woff2");
    }
    
    .cm-content {
        font-family: "CaskaydiaCove Nerd Font Mono";
        font-variant-ligatures: contextual;
    }
    
    .fira *{
       font-family: 'Fira Code',monospace
    }
    
    .letter-spacing {
        letter-spacing: 0.001em;
    }
    
    .no-lig *{
        /*
        font-variant-ligatures: contextual;
        font-variant-ligatures: normal;
        */
        font-variant-ligatures: none;
    
    }
    <script src="https://codemirror.net/codemirror.js"></script>
    
    <h3>Default: ligatures enabled</h3>
    <div id="editor"></div>
    
    <h3>Default: ligatures disabled due to custom letter spacing</h3>
    <div id="editor2" class="letter-spacing"></div>
    
    <h3>Default: ligatures disabled due to custom CSS rule</h3>
    <div id="editor3" class="no-lig"></div>
    
    <h3>Default: ligatures enabled - fira code</h3>
    <div id="editor4" class="fira"></div>

    You should also try to use a CDN hosted font like "Fira Code" from google fonts to make sure it's not some bundling relative path issue.