Search code examples
vscode-extensionslanguage-server-protocol

Creating IntelliSense based on compiled JavaScript for other language?


I thought about creating an extension that offers IntelliSense (i.e. code completion) for CoffeeScript, a popular language that compiles to JavaScript. By IntelliSense I primarly mean autocompletion: Properties, function parameters, automatic imports. If possible, also Go-To-Definitions. Despite its huge success and age, nothing like it exists in any IDE for CS, as far as I know. This is arguably because integrating the language server, import scanning, function signature analysis etc. are complex and this is not an easy task per se.

However, CoffeeScript natively and efficiently compiles into JavaScript, and JS Intellisense in VSCode is splendid. For a very basic yet mostly functional extension, the following approach sounds feasible:

  • Register completion item provider for type CoffeeScript files
  • When providing a completion item, take the compiled JavaScript code, get profuse IntelliSense information from the JS/TS language server (how? do I need my own instance? I don't think VSC even uses an actual JS LSP internally?) and show it in the CoffeeScript file cursor position

It does not sound difficult, but I don't know how to proceed. Here is a pseudo code snippet of how I imagine it to work:

class Provider {
    provideCompletionItems(document, position, token) {
        const word = document.getText(document.getWordRangeAtPosition(position))
        const text = document.getText().split('\n')
        const currentLineno = position.line
        const textWithoutCurrentLine = text.filter((line, lineno) => lineno != currentLineno)
        const compiled = CoffeeScript.compile(textWithoutCurrentLine.join('\n'), { sourceMap: true })
        const currentLinenoJS = compiled.sourceMap[currentLine].lineno // don't know the syntax yet
        const compiledWithWord = compiled.js.split('\n').splice(currentLinenoJS, 0, word)

        // These methods don't exist, what do I do?
        const JSProvider = vscode.languages.simulateCompletionItemProvider('javascript')
        const completionItems: CompletionItem[] = JSProvider.getCompletionItemsForTextAndLine(textWithoutCurrentLine, currentLinenoJS)

        return completionItems
    }
}
vscode.languages.registerCompletionItemProvider('coffeescript', Provider));

This might be more confusing than helpful, but it's what the code could look like. I would love a hint on how to access the JavaScript intellisense features for dynamically generated code, from within the completion item provider handler of a CoffeeScript file. If this is doable, it would allow for simple extensions for similar compile-to-JS languages too.

In other words: JS has all these amazing autocompletion features and CS compiles to JS, including source maps. How can I leverage this to get autocompletion for CS?


Solution

  • Register completion item provider for type CoffeeScript files

    This would be following programmatic language features. For a middleware kind of extension there are two main architectures possible, outlined here: Language services or request forwarding.

    I eventually went with implementing a language service extension, outlined here.

    This approach was significantly easier than writing a native analyzer.

    When providing a completion item, take the compiled JavaScript code, get profuse IntelliSense information from the JS/TS language server (how? do I need my own instance?

    Yes

    I don't think VSC even uses an actual JS LSP internally?)

    Yes, tsserver does not implement LSP, but offers its own APIs, but there are other usable implementations. So you need to interact with either of those.