Codemirror 6 and Typescript LSP

Did you look at https://www.npmjs.com/package/@typescript/vfs? Its README has full instructions on how to get document highlights for a file.

They create a file called index.ts in the virtual file system with some content, then initialize a virtualTypeScriptEnvironment with that file, that’s all you have to do as well.

Then, instead of getting document highlights, you can call getCompletionsAtPosition (also listed in the README). I would recommend playing around with @typescript/vfs outside CodeMirror just to get the hang of it first.

how exactly are you replacing the normal autocomplete function

You cannot just override: [env.languageService.getCompletionsAtPosition()], because the completion source’s signature won’t match up this way…

This is what my autocompletion config roughly looks like:

autocompletion({
  override: [(ctx) => {
    const { pos } = ctx;
    
    // tsserver is initialized using @typescript/vfs
    const completions = tsserver.languageService.getCompletionsAtPosition(
      "index.ts",
      pos,
      {}
    );
    if (!completions) {
      log("Unable to get completions", { pos });
      return null;
    }

    return completeFromList(
      completions.entries.map(c => ({
        type: c.kind,
        label: c.name,
      }))
    )(ctx);
  }]
})

could you explain what you meant by the 8th bullet in the original reply, which is talking about ts.updateFile?

Since you initialize tsserver with some file contents, you have to realize that tsserver has its own representation / view of the “file” you’re working with. When you change/type things in your editor, you have to tell tsserver about these changes as well so that the next time you ask something from it (like autocompletions, or type errors), it gives you the correct response. You have to keep your editor’s view in sync with tsserver’s view of the file. That’s what the updateFile method on the tsserver instance does.
In my case, I override the dispatch function in my new EditorView params this way:

const view = new EditorView({
  // ...snip
  dispatch: transaction => {
        // Update view first
        this.view.update([transaction]);

        // Then tell tsserver about new file. Debounce this, I'm not doing that here for sake of clarity
        if (transaction.docChanged) {
          const content = doc.sliceString(0);
          tsserver.updateFile("index.ts", content);
        }
      },
})
1 Like