Skip to content
Keen Yee Liau edited this page Aug 26, 2019 · 3 revisions

Angular Language Service

This document outlines the goals and implementation of the Angular language service (LS).

Goals

  1. User: Provide useful editor features to Angular users for a better developer experience
  2. Performance: Fast and efficient real-time editing capabilities
  3. Compatibility: Primarily with VSCode, but must support other editors

Components

  1. Client
  2. Server

The client is a VSCode-specific extension that runs in the VSCode extension host. When activated, the client would spawn a server process and synchronizes documents with the server as they are opened and edited by user.

The server is a proper implementation of the Language Service Protocol (LSP). It is a standalone module, and could be deployed independently of the client. The server is very similar to tsserver in functionality, except for the way it handles non-TS files. In essence, the server is a "bridge" between tsserver and LSP, but it only answers queries relevant to Angular.

Constraints

  1. Efficiency (TypeScript parsing)
  2. No version skew for both TypeScript and Angular
  3. Support for external template

Angular LS inherently works hand-in-hand with TypeScript compiler due to the nature of the code. Angular compiler is effectively an extension of the TypeScript compiler.

Ideally, it is desirable that TypeScript code not be parsed twice, once by the native TypeScript extension, and again by the Angular LS. However, this forces the Angular LS to be a strict tsserver plugin, and such move causes multiple drawbacks.

  1. Support for external files in tsserver is not feature complete.
    There has been much discussion around this, but many edge cases must be taken into account before it could be implemented.
  2. Compatibility with editors other than VSCode.
    In order for tsserver to support non-TS files, there ought to be client side changes, but these changes are different for each editor.

For the reasons above, the Angular LS remains an implementation of Language Service Protocol (LSP). This does not mean that tsserver plugin is abandoned completely. Angular LS aims to be fully compatible with tsserver, sans support for external template. This does not preclude the plugin from supporting external templates in the future. If such capability is baked into tsserver one day, Angular LS is in a good position to adopt the change quickly. Internally, the new Angular LS loads @angular/language-service as a tsserver plugin.

tsserver

tsserver is a standalone "binary" that contains TypeScript's language server. It works very much like any language server, but it does not adhere to the Language Service Protocol (LSP). Angular augments the behavior of tsserver through its plugin architecture.

There are two kinds of tsserver plugins:

  1. Global plugin
    The plugin gets installed when the server starts up, and it is specified through the command line flag --globalPlugins. This means that the plugin is active for every project that the server handles.

  2. Local plugin
    The plugin gets installed for a specific project only, and it is specified through the tsconfig.json for the particular project.

Angular supports both global and local plugin mode. In the server implementation here, Angular is only concerned about answering Angular-specific queries, so it installs the plugin as a global plugin, then configure the plugin to only return Angular results through angularOnly flag.

The other use case is when users install the plugin through the tsconfig.json of their project and wants Angular language features in addition to TypeScript's. There is no support for external template in this mode (yet). In this case, Angular should not disable TypeScript's queries, and must answer both Angular and TypeScript queries.

It is important to note that tsserver plugin does not complement the default TypeScript behavior, but rather completely takes over the results that would be returned. It is the responsibility of the plugin to return the intended results.

Breaking Changes

There are a few breaking changes in the new design of the language service.

  1. Before this, the extension comes bundled with its own @angular/language-service, but it is used only as a fallback if the same package is not found in the user's project. This increases the size and dependencies of the extension. In the new design, the extension would become a no-op program if it can't find @angular/language-service in the project. This is to ensure version compatibility with the actual compiler used to compile the project.

Glossary

It is very important to establish the right nomenclatures for discussion purposes.

  1. Extension
    A VSCode extension that users install via the editor.
  2. Plugin
    A tsserver plugin that augments the behavior of TypeScript LS.
  3. ProjectService
    A singleton instance that handles multiple projects inside tsserver.
  4. Project
    A "server" concept of a collection of files that belong to the same tsconfig. The collection could be any files, not necessarily TS only.
  5. ScriptInfo
    A "server" concept of a document, i.e. a file. This is language agnostic.
  6. SourceFile
    Specifically refers to a TypeScript file, and contains the AST for the source.
  7. Program
    A "compiler" concept of a collection of TS files, along with its transitive dependencies.
Clone this wiki locally