codemirror / dev

Development repository for the CodeMirror editor project
https://codemirror.net/
Other
5.94k stars 376 forks source link

HighlightStyle does not always respect `t.function` #1449

Closed jonatanklosko closed 1 month ago

jonatanklosko commented 1 month ago

Describe the issue

I noticed that sometimes fun in Mod.fun() does not get the expected highlighting. I narrowed it down to a small Elixir snippet:

Mod.fun()
# aaaaaaaaaaaaaaaaaaaaaa

When I add one more "a" to the comment, it changes the highlighting of fun. I believe it's not a parser/tags issue, because I checked that the tag is correct in both cases:

Script ```js // npm install @lezer/highlight @codemirror/language codemirror-lang-elixir import { getStyleTags, tags as t } from "@lezer/highlight"; import { elixirLanguage } from "codemirror-lang-elixir"; const code1 = ` Mod.fun() # aaaaaaaaaaaaaaaaaaaaaa `; const code2 = ` Mod.fun() # aaaaaaaaaaaaaaaaaaaaaaa `; const tags1 = getStyleTags(elixirLanguage.parser.parse(code1).resolve(6, 0)).tags; const tags2 = getStyleTags(elixirLanguage.parser.parse(code2).resolve(6, 0)).tags; console.log(tags1.length === 1 && tags1[0] === t.function(t.variableName)); //=> true console.log(tags2.length === 1 && tags2[0] === t.function(t.variableName)); //=> true ```

So I think the issue may be related to HighlightStyle.

Here's a single script that demonstrates the issue:


import { highlightCode, tags as t } from "@lezer/highlight";
import { HighlightStyle } from "@codemirror/language";
import { elixirLanguage } from "codemirror-lang-elixir";

const highlightStyle = HighlightStyle.define([
  { tag: t.function(t.variableName), color: "blue" },
]);

export function highlight(code) {
  const tree = elixirLanguage.parser.parse(code);

  let output = "";

  highlightCode(
    code,
    tree,
    highlightStyle,
    (code, classes) => {
      output += `[classes: ${classes}] ${code}\n`;
    },
    () => {},
  );

  return output;
}

console.log(
  highlight(`
Mod.fun()
# aaaaaaaaaaaaaaaaaaaaaa
`),
);
//=> [classes: ] Mod.
//=> [classes: ͼo] fun
//=> [classes: ] ()
//=> [classes: ] # aaaaaaaaaaaaaaaaaaaaaa

console.log(
  highlight(`
Mod.fun()
# aaaaaaaaaaaaaaaaaaaaaaa
`),
);
//=> [classes: ] Mod.fun()
//=> [classes: ] # aaaaaaaaaaaaaaaaaaaaaaa

We can see that in the first snippet foo with tag t.function(t.variableName) gets a style class, but in the second snippet it doesn't.

Interestingly, once we change the highlight style to { tag: t.variableName, color: "blue" }, it adds the same class in both cases.

Browser and platform

node v20

Reproduction link

No response

marijnh commented 1 month ago

Thanks for isolating that! This was a well-hidden bug in @lezer/common. Version 1.2.2 should fix it.

jonatanklosko commented 1 month ago

@marijnh beautiful, the highlighting looks good now, thanks for the quick fix! <3