Autocomplete not working in mixed language parsing

I have a custom language that is built on top of JSON that should give auto complete suggestions when inside a JSON String node.

This is the current code I have. I am not getting any suggestions and ctrl+space shows no dialog.

Can anyone see where I have gone wrong or what I’m missing?

const expressionLanguage = LRLanguage.define({
  parser: expressionParser,
  name: "expressionLang",
});

const expressionLanguageSupport = () =>
  new LanguageSupport(expressionLanguage, [
    expressionLanguage.data.of({
      autocomplete: completeFromList([{ label: "pls work" }]),
    })
  ]);

const mixedLanguage = LRLanguage.define({
  parser: jsonLanguage.parser.configure({
    wrap: parseMixed((node) => {
      if (node.name === "String") {
        return {
          parser: expressionParser,
        };
      }
      return null;
    }),
  }),
  name: "jsonExpression",
});

const mixedLanguageSupport = () =>
  new LanguageSupport(mixedLanguage, [expressionLanguageSupport().support, json().support]);

It works in JavaScript code in this example. Are you including autocompletion in your list of extensions? What does state.languageDataAt("autocomplete", pos) return when called with a position inside such an expression?

Are you including autocompletion in your list of extensions?

I’m using react codemirror which loads it in a basicSetup function.

autocomplete seems to be working as if I make the following change I get the auto complete dialog everywhere apart from inside the String node.

export const mixedLanguageSupport = () =>
  new LanguageSupport(mixedLanguage, [
    mixedLanguage.data.of({
      autocomplete: completeFromList([{ label: "testy" }]),
    }),
    expressionLanguageSupport().support,
    json().support,
  ]);

What does state.languageDataAt("autocomplete", pos) return

An empty array.

That’s very odd. Any chance you can reduce it to a minimal example script (possibly using parsers from language packages instead of your custom one to keep it small)?

This seems to behave the same. Its using the lang-javascript and lang-json packages.

You’ll get autocomplete anywhere in the document apart from inside a string node.

const mixedLanguage = LRLanguage.define({
  parser: jsonLanguage.parser.configure({
    wrap: parseMixed((node) => {
      if (node.name === "String") {
        return {
          parser: javascriptLanguage.parser,
        };
      }
      return null;
    }),
  }),
  name: "jsonJavascript",
});

const customJavascriptLanguage = LRLanguage.define({
  parser: javascriptLanguage.parser,
  name: "customJavascript",
});

const customJavascriptSupport = () =>
  new LanguageSupport(customJavascriptLanguage, [
    customJavascriptLanguage.data.of({
      autocomplete: completeFromList([{ label: "inside json string node" }]),
    }),
  ]);

const mixedLanguageSupport = () =>
  new LanguageSupport(mixedLanguage, [
    mixedLanguage.data.of({
      autocomplete: completeFromList([{ label: "outside json string node" }]),
    }),
    customJavascriptSupport().support,
    json().support,
  ]);

const extensions = [autocompletion(), mixedLanguageSupport()];

You’re returning javascriptLanguage.parser, not customJavascriptLanguage.parser, from your mixed parsing function. So you’ll get the javascript language data for the syntax trees it produces, not your custom language data.

Thanks for your help. It’s working as expected now.