typescriptlanguageservice

Implementing a host of a language service


After reading the src\services code, it seems this is the interface any host of a language service must satisfy:

//
// Public interface of the host of a language service instance.
//
export interface ILanguageServiceHost extends TypeScript.ILogger {
    getCompilationSettings(): TypeScript.CompilationSettings;
    getScriptCount(): number;
    getScriptId(scriptIndex: number): string;
    getScriptSourceText(scriptIndex: number, start: number, end: number): string;
    getScriptSourceLength(scriptIndex: number): number;
    getScriptIsResident(scriptIndex: number): bool;
    getScriptVersion(scriptIndex: number): number;
    getScriptEditRangeSinceVersion(scriptIndex: number, scriptVersion: number): TypeScript.ScriptEditRange;
}

I haven't been able to find any documentation or samples, and while some methods are self-explanatory, others are not, notably:

getScriptId()
getScriptIsResident()
getScriptVersion()
getScriptEditRangeSinceVersion()

Is the language service API ready for use? Could someone explain briefly the purpose of the methods above?


Solution

  • Disclaimer: The language service hosting API will be changing in future versions. I'm not sure what the full extent of the changes will be -- I expect things will be mostly the same, but there will almost certainly be breaking changes.

    Also, there's a full TypeScript implementation of the hosting API in src\harness\harness.ts used for the language service unit tests you can refer to. Here's a conceptual breakdown of the functions you listed:

    getScriptId()

    You need to return a string that is unique for each file (script), but doesn't change from invocation from invocation. Returning the filename of the script would work nicely.

    getScriptIsResident()

    The compiler has a notion of a 'resident' file that isn't mutable (for example, lib.d.ts). Resident status is used for performance reasons -- for example, types that came out of a resident file are considered immutable (this is why you see oddness in Visual Studio when you try to extend a type defined in lib.d.ts). You can safely return false for all files here, or if you know a file is immutable, you can return true. The concept of a 'resident' file will go away in some future version of the compiler once the improved type-checker comes online.

    getScriptVersion()

    Here, you need to return a monotonically increasing number that is incremented whenever the source text of the script changes. The language service uses this number to determine whether or not it should do reparsing / retypechecking of the files.

    getScriptEditRangeSinceVersion()

    This function should return a list of edit ranges (hopefully self-explanatory) that have occurred between now and the specified prior version number (see above getScriptVersion). Obviously this is a bit of a pain to implement, but it is allowable here to return TypeScript.ScriptEditRange.unknown(), at which point the language service will do a full reparse of the file (significant perf impact, so try to do this sparingly in interactive contexts).