Description
We plan to use the extension API tracked by TypeScript issue #6508 to provide language services inside template strings of a @Component
decorator as well as reuse these services in loose HTML files.
Features
The features we plan to support are,
- Syntax and semantic code highlighting
- Syntactic and semantic error reporting
- Completion lists for HTML built-in elements and referenced directives
- Completion list for data-binding expression
- Parameter lists for data-binding function calls
- Go-to definition on attributes, elements and binding expressions
A prototype of these features was already implemented by Microsoft as part of the work to validate the their extensibility and the referenced issue has an animated GIF that shows the prototype in action.
This work is to use the Angular 2's template parser, instead of a custom parser, as well as using the Angular 2 offline-compiler metadata to describe the components allowing libraries (such as Material) to be self-describing.
Additional features (probably post-final):
- Source formatting and indentation support
- Rename of directive and attributes
- Quick fix to add missing directives
Component
decorator
Editor support
Editors that support configuring TypeScript extensions will be able to use the Angular 2 extensions for template strings with no additional changes.
Editors will need to be modified to integrate the Angular 2 language service for loose HTML files similar to how TypeScript is integrated for use in .ts, .d.ts and .tsx.
This work will extend the TypeScript node server and VS Code to support both the Angular 2 extension and loose HTML files.
Milestones
Initial release
Extending TypeScript
Tasks:
- Contribute to TS#6508 to help it land in TypeScript.
- Modify
tsserver
to load TypeScript Extensions. (see RFC: Extensibility model microsoft/TypeScript#9038) - Contribute to VS Code to enable it to load TypeScript Extensions. (RFC: Extensibility model microsoft/TypeScript#9038, no VS Code changes needed)
- Assist other editors in supporting TypeScript Extensions in general and Angular 2's extension in particular.
Problem: Angular 2 templates can be specified using a string literal or string template inside a TypeScript file. Providing language services for these strings requires TypeScript's language service to delegate calls to LanguageService
to the Angular 2 language service.
Soltuion: Use the proposed extensibility interface provided by TypeScript. Help evangelize the extension API to editors that support TypeScript.
Syntax Highlighting
Tasks:
-
Augment the result ofUsing .tmLanguage instead.getEncodedSemanticClassifications
with Angular 2 classifications usingHtmlLexer
.
Problem: Produce ClassifiedSpan[]
for ranges that intersect with a Angular 2 template. This requires determining if the span is in a template string which itself requires a semantic analysis of the TypeScript module to determine if the string is in a @Component
decorator.
Solution: Only return Angular 2 syntactic classification in getEncodedSemanticClassifications
because the semantic analysis of the TypeScript module is alre
8AAF
ady available. To produce the spans, scan the ClassifiedSpan
s returned by TypeScript and determine if any strings contained in the result are candidates. Use HtmlLexer
to produce lexical tokens from the string and generate ClassifiedSpan
s from the tokens.
Semantic Highlighting
Tasks:
-
Augment the result from Syntax Highlighting with semantic information from theUsing tmLanguage file instead.TemplateParser
AST.
Problem: Allow highlighting directives with a different color from built in HTML elements and attributes.
Solution: Use Angular 2's TemplateParser
to produce a template AST and augment the classifications produce during Syntax Highlighting using information from this AST.
Diagnostic Messages
Tasks:
- Invoke the
TemplateParser
on template strings and augment the result ofgetSemanticDiagnostics()
. - Generate the template code for the template AST and augment with the diagnostics from the generated modules.
Problem: Produce error and warning messages for Angular 2 templates in .ts
.
Solution: Invoke the Angular 2 TemplateParser
on template strings in TypeScript modules and augment the result of getSemanticDiagnostics()
with the result. Even syntax errors are delayed until getSemanticDiagnostics()
because determining if a string is a Angular 2 template requires TypeScript semantic analysis.
Problem: Produce errors produced by code generated from a template.
Solution: Invoke the offline compiler to produce the corresponding TypeScript for a template. Produce a secondary LanguageService
for a project that now includes the generated code and augment the result diagnostics from the generated module.
Alternative: The proposed solution might take significant time and resources because it requires instantiating an additional TypeScript language service which potentially could double the memory and time to respond to language service calls. Alternately the expression AST could be typed directly against the TypeScript symbols and error messages produced without invoking a secondary language service. The current proposal is simpler but the alternative should be considered if performance and memory are an issue for the proposed solution.
Template Completions
Tasks:
- Produce completion lists for element names.
- Produce completion lists for attribute names.
- Produce completion lists for attribute values from attribute type.
- Produce completion lists for binding expressions.
- Produce completion lists for interpolation expressions.
- Produce parameter help for data-binding function calls.
Notes: All solutions begin by producing a TemplateParser
AST and determine where in the AST the position requested corresponds to. All built-in HTML elements and attributes are treated as a built-in list of directives to avoid special casing HTML.
Problem: Produce completions lists for element names.
Solution: If the position is where an element is expected produce a list of available element selectors from the referenced directives.
Problem: Produce a list of attributes names.
Solution: If the position is where an attribute name is expected produce the list of available attribute selectors that could match the current element.
Problem: Produce list for attribute values from attribute type.
Solution: If the position is where an attribute value is expected determine the type of the attribute by requesting the type from TypeScript, produce the completion list from the types member symbols.
Problem: Produce completion lists for binding expressions.
Solution: If the position is where a binding expression is expected generate the corresponding template code and request a completion list from the corresponding location in the generated code from an internal TypeScript language service. See Alternatives in Diagnostic Messages for an alternative to the code generation approach and conditions for pursuing the alternative.
Problem: Produce completion lists for interpolation expressions.
Solution: If the position is where an interpolation expression is expected treat it like a binding expression above.
Problem: Produce parameter help for data-binding function calls.
Solution: If the position is where a binding expression or interpolation expression is expected use the technique in binding expressions above but requires a parameter list instead of a completion list from the language service.
Go to definition
- Go to definition of an element name.
- Go to definition of an attribute name.
- Go to definition of an attribute value.
- Go to definition of an binding expression.
- Go to definition of an interpolation expression.
Problem: Determine location that defines a template element.
Solution: Use the same technique as is used in Template Completions for the corresponding request but request references from TypeScript instead generating a completion list.
Sharable metadata
Tasks:
- Persist
@Component
and@Pipe
decorator information not available in.d.ts
files. (chore(build): Produce .d.ts files for build tools #7763) - Create a shareable metadata reader for extracting information from a TypeScript AST and/or persistent data. (chore(build): Produce .d.ts files for build tools #7763)
- Create a static reflector that allows the parser to use the persistent metadata. (feat(static-reflector): Added StaticReflector #7813)
Problem: Parsing an Angular 2 template relies on information provided using decorators. These decorators, however, are discarded when TypeScript produces a .d.ts
file. The language services need a way to retrieve this information for modules for which it only has .d.ts
files.
Solution: Write the decorator information discarded by TypeScript into a .metadata.json
file that can be read by the language services. This is will be produced for Angular by the Angular build and for other libraries, such as Angular Material, by the Angular CLI when packaging the module for deployment.
Alternative: The metadata could be derived by loading the referenced modules. However this requires executing the modules thus requiring the language service to execute arbitrary third-party code directly. In many contexts, this would be an unacceptable risk. Also, some modules might have dependencies that could not be loaded in all contexts the language service must run such as modules with a browser dependency running in the tsserver. The proposed solution does not rely on executing third-party code so does not expose the language service to running potentially unready, incompatible, and/or unreliable code with unintended side-effects just to extract metadata information. Also, .metadata.json
files can be written by a system that does load the modules and extracts the metadata information allowing the module to be loaded in a controlled environment outside the language service.