99import * as ts from 'typescript' ;
1010
1111import { createLanguageService } from './language_service' ;
12- import { LanguageService , LanguageServiceHost } from './types' ;
12+ import { Completion , Diagnostic , LanguageService , LanguageServiceHost } from './types' ;
1313import { TypeScriptServiceHost } from './typescript_host' ;
1414
15+ export function create ( info : any /* ts.server.PluginCreateInfo */ ) : ts . LanguageService {
16+ // Create the proxy
17+ const proxy : ts . LanguageService = Object . create ( null ) ;
18+ const oldLS : ts . LanguageService = info . languageService ;
19+ for ( const k in oldLS ) {
20+ ( < any > proxy ) [ k ] = function ( ) { return ( oldLS as any ) [ k ] . apply ( oldLS , arguments ) ; } ;
21+ }
1522
16- /** A plugin to TypeScript's langauge service that provide language services for
17- * templates in string literals.
18- *
19- * @experimental
20- */
21- export class LanguageServicePlugin {
22- private serviceHost : TypeScriptServiceHost ;
23- private service : LanguageService ;
24- private host : ts . LanguageServiceHost ;
23+ function completionToEntry ( c : Completion ) : ts . CompletionEntry {
24+ return { kind : c . kind , name : c . name , sortText : c . sort , kindModifiers : '' } ;
25+ }
2526
26- static 'extension-kind' = 'language-service' ;
27+ function diagnosticToDiagnostic ( d : Diagnostic , file : ts . SourceFile ) : ts . Diagnostic {
28+ return {
29+ file,
30+ start : d . span . start ,
31+ length : d . span . end - d . span . start ,
32+ messageText : d . message ,
33+ category : ts . DiagnosticCategory . Error ,
34+ code : 0
35+ } ;
36+ }
2737
28- constructor ( config : {
29- host : ts . LanguageServiceHost ; service : ts . LanguageService ;
30- registry ?: ts . DocumentRegistry , args ?: any
31- } ) {
32- this . host = config . host ;
33- this . serviceHost = new TypeScriptServiceHost ( config . host , config . service ) ;
34- this . service = createLanguageService ( this . serviceHost ) ;
35- this . serviceHost . setSite ( this . service ) ;
38+ function tryOperation ( attempting : string , callback : ( ) => void ) {
39+ try {
40+ callback ( ) ;
41+ } catch ( e ) {
42+ info . project . projectService . logger . info ( `Failed to ${ attempting } : ${ e . toString ( ) } ` ) ;
43+ info . project . projectService . logger . info ( `Stack trace: ${ e . stack } ` ) ;
44+ }
3645 }
3746
38- /**
39- * Augment the diagnostics reported by TypeScript with errors from the templates in string
40- * literals.
41- */
42- getSemanticDiagnosticsFilter ( fileName : string , previous : ts . Diagnostic [ ] ) : ts . Diagnostic [ ] {
43- let errors = this . service . getDiagnostics ( fileName ) ;
44- if ( errors && errors . length ) {
45- let file = this . serviceHost . getSourceFile ( fileName ) ;
46- for ( const error of errors ) {
47- previous . push ( {
48- file,
49- start : error . span . start ,
50- length : error . span . end - error . span . start ,
51- messageText : error . message ,
52- category : ts . DiagnosticCategory . Error ,
53- code : 0
54- } ) ;
47+ const serviceHost = new TypeScriptServiceHost ( info . languageServiceHost , info . languageService ) ;
48+ const ls = createLanguageService ( serviceHost ) ;
49+ serviceHost . setSite ( ls ) ;
50+
51+ proxy . getCompletionsAtPosition = function ( fileName : string , position : number ) {
52+ let base = oldLS . getCompletionsAtPosition ( fileName , position ) ;
53+ tryOperation ( 'get completions' , ( ) => {
54+ const results = ls . getCompletionsAt ( fileName , position ) ;
55+ if ( results && results . length ) {
56+ if ( base === undefined ) {
57+ base = { isMemberCompletion : false , isNewIdentifierLocation : false , entries : [ ] } ;
58+ }
59+ for ( const entry of results ) {
60+ base . entries . push ( completionToEntry ( entry ) ) ;
61+ }
62+ }
63+ } ) ;
64+ return base ;
65+ } ;
66+
67+ proxy . getQuickInfoAtPosition = function ( fileName : string , position : number ) : ts . QuickInfo {
68+ let base = oldLS . getQuickInfoAtPosition ( fileName , position ) ;
69+ tryOperation ( 'get quick info' , ( ) => {
70+ const ours = ls . getHoverAt ( fileName , position ) ;
71+ if ( ours ) {
72+ const displayParts : typeof base . displayParts = [ ] ;
73+ for ( const part of ours . text ) {
74+ displayParts . push ( { kind : part . language , text : part . text } ) ;
75+ }
76+ base = {
77+ displayParts,
78+ documentation : [ ] ,
79+ kind : 'angular' ,
80+ kindModifiers : 'what does this do?' ,
81+ textSpan : { start : ours . span . start , length : ours . span . end - ours . span . start }
82+ } ;
5583 }
84+ } ) ;
85+
86+ return base ;
87+ } ;
88+
89+ proxy . getSemanticDiagnostics = function ( fileName : string ) {
90+ let base = oldLS . getSemanticDiagnostics ( fileName ) ;
91+ if ( base === undefined ) {
92+ base = [ ] ;
5693 }
57- return previous ;
58- }
94+ tryOperation ( 'get diagnostics' , ( ) => {
95+ info . project . projectService . logger . info ( `Computing Angular semantic diagnostics...` ) ;
96+ const ours = ls . getDiagnostics ( fileName ) ;
97+ if ( ours && ours . length ) {
98+ const file = oldLS . getProgram ( ) . getSourceFile ( fileName ) ;
99+ base . push . apply ( base , ours . map ( d => diagnosticToDiagnostic ( d , file ) ) ) ;
100+ }
101+ } ) ;
102+
103+ return base ;
104+ } ;
59105
60- /**
61- * Get completions for angular templates if one is at the given position.
62- */
63- getCompletionsAtPosition ( fileName : string , position : number ) : ts . CompletionInfo {
64- let result = this . service . getCompletionsAt ( fileName , position ) ;
65- if ( result ) {
66- return {
67- isMemberCompletion : false ,
68- isNewIdentifierLocation : false ,
69- entries : result . map < ts . CompletionEntry > (
70- entry =>
71- ( { name : entry . name , kind : entry . kind , kindModifiers : '' , sortText : entry . sort } ) )
72- } ;
106+ proxy . getDefinitionAtPosition = function (
107+ fileName : string , position : number ) : ts . DefinitionInfo [ ] {
108+ let base = oldLS . getDefinitionAtPosition ( fileName , position ) ;
109+ if ( base && base . length ) {
110+ return base ;
73111 }
74- }
75- }
112+
113+ tryOperation ( 'get definition' , ( ) => {
114+ const ours = ls . getDefinitionAt ( fileName , position ) ;
115+ if ( ours && ours . length ) {
116+ base = base || [ ] ;
117+ for ( const loc of ours ) {
118+ base . push ( {
119+ fileName : loc . fileName ,
120+ textSpan : { start : loc . span . start , length : loc . span . end - loc . span . start } ,
121+ name : '' ,
122+ kind : 'definition' ,
123+ containerName : loc . fileName ,
124+ containerKind : 'file'
125+ } ) ;
126+ }
127+ }
128+ } ) ;
129+ return base ;
130+ } ;
131+
132+ return proxy ;
133+ }
0 commit comments