55 * ------------------------------------------------------------------------------------------ */
66
77import * as ng from '@angular/language-service' ;
8-
9- import {
10- createConnection , IConnection , InitializeResult , TextDocumentPositionParams ,
11- CompletionItem , CompletionItemKind , Definition , Location , TextDocumentIdentifier ,
12- Position , Range , TextEdit , Hover
13- } from 'vscode-languageserver' ;
8+ import * as ts from 'typescript' ;
9+ import * as lsp from 'vscode-languageserver' ;
1410
1511import { TextDocuments , TextDocumentEvent , fileNameToUri } from './documents' ;
1612import { ErrorCollector } from './errors' ;
1713
1814import { Completion } from '@angular/language-service' ;
1915
2016// Create a connection for the server. The connection uses Node's IPC as a transport
21- let connection : IConnection = createConnection ( ) ;
17+ const connection : lsp . IConnection = lsp . createConnection ( ) ;
2218
2319// Create a simple text document manager. The text document manager
2420// supports full document sync only
25- let documents : TextDocuments = new TextDocuments ( handleTextEvent ) ;
21+ const documents : TextDocuments = new TextDocuments ( handleTextEvent ) ;
2622
2723
2824// Setup the error collector that watches for document events and requests errors
@@ -45,7 +41,7 @@ documents.listen(connection);
4541// After the server has started the client sends an initilize request. The server receives
4642// in the passed params the rootPath of the workspace plus the client capabilites.
4743let workspaceRoot : string ;
48- connection . onInitialize ( ( params ) : InitializeResult => {
44+ connection . onInitialize ( ( params ) : lsp . InitializeResult => {
4945 workspaceRoot = params . rootPath ;
5046 return {
5147 capabilities : {
@@ -62,7 +58,8 @@ connection.onInitialize((params): InitializeResult => {
6258 }
6359} ) ;
6460
65- function compiletionKindToCompletionItemKind ( kind : string ) : CompletionItemKind {
61+ function compiletionKindToCompletionItemKind ( kind : string ) : lsp . CompletionItemKind {
62+ const { CompletionItemKind} = lsp ;
6663 switch ( kind ) {
6764 case 'attribute' : return CompletionItemKind . Property ;
6865 case 'html attribute' : return CompletionItemKind . Property ;
@@ -84,13 +81,13 @@ const wordRe = /(\w|\(|\)|\[|\]|\*|\-|\_|\.)+/g;
8481const special = / \( | \) | \[ | \] | \* | \- | \_ | \. / ;
8582
8683// Convert attribute names with non-\w chracters into a text edit.
87- function insertionToEdit ( range : Range , insertText : string ) : TextEdit {
84+ function insertionToEdit ( range : lsp . Range , insertText : string ) : lsp . TextEdit {
8885 if ( insertText . match ( special ) && range ) {
89- return TextEdit . replace ( range , insertText ) ;
86+ return lsp . TextEdit . replace ( range , insertText ) ;
9087 }
9188}
9289
93- function getReplaceRange ( document : TextDocumentIdentifier , offset : number ) : Range {
90+ function getReplaceRange ( document : lsp . TextDocumentIdentifier , offset : number ) : lsp . Range {
9491 const line = documents . getDocumentLine ( document , offset ) ;
9592 if ( line && line . text && line . start <= offset && line . start + line . text . length >= offset ) {
9693 const lineOffset = offset - line . start - 1 ;
@@ -104,7 +101,7 @@ function getReplaceRange(document: TextDocumentIdentifier, offset: number): Rang
104101 }
105102 } ) ) ;
106103 if ( found != null ) {
107- return Range . create ( Position . create ( line . line - 1 , found ) , Position . create ( line . line - 1 , found + len ) ) ;
104+ return lsp . Range . create ( line . line - 1 , found , line . line - 1 , found + len ) ;
108105 }
109106 }
110107}
@@ -119,7 +116,7 @@ function insertTextOf(completion: Completion): string {
119116}
120117
121118// This handler provides the initial list of the completion items.
122- connection . onCompletion ( ( textDocumentPosition : TextDocumentPositionParams ) : CompletionItem [ ] => {
119+ connection . onCompletion ( ( textDocumentPosition : lsp . TextDocumentPositionParams ) : lsp . CompletionItem [ ] => {
123120 const { fileName, service, offset, languageId} = documents . getServiceInfo ( textDocumentPosition . textDocument ,
124121 textDocumentPosition . position )
125122 if ( fileName && service && offset != null ) {
@@ -142,14 +139,14 @@ connection.onCompletion((textDocumentPosition: TextDocumentPositionParams): Comp
142139 }
143140} ) ;
144141
145- function ngDefintionToDefintion ( definition : ng . Definition ) : Definition {
142+ function ngDefinitionToDefinition ( definition : ng . Definition ) : lsp . Definition {
146143 const locations = definition . map ( d => {
147- const document = TextDocumentIdentifier . create ( fileNameToUri ( d . fileName ) ) ;
144+ const document = lsp . TextDocumentIdentifier . create ( fileNameToUri ( d . fileName ) ) ;
148145 const positions = documents . offsetsToPositions ( document , [ d . span . start , d . span . end ] ) ;
149146 return { document, positions}
150147 } ) . filter ( d => d . positions . length > 0 ) . map ( d => {
151- const range = Range . create ( d . positions [ 0 ] , d . positions [ 1 ] ) ;
152- return Location . create ( d . document . uri , range ) ;
148+ const range = lsp . Range . create ( d . positions [ 0 ] , d . positions [ 1 ] ) ;
149+ return lsp . Location . create ( d . document . uri , range ) ;
153150 } ) ;
154151 if ( locations && locations . length ) {
155152 return locations ;
@@ -165,22 +162,51 @@ function logErrors<T>(f: () => T): T {
165162 }
166163}
167164
168- connection . onDefinition ( ( textDocumentPosition : TextDocumentPositionParams ) : Definition => logErrors ( ( ) => {
169- const { fileName , service , offset , languageId } = documents . getServiceInfo ( textDocumentPosition . textDocument ,
170- textDocumentPosition . position )
165+ connection . onDefinition ( ( params : lsp . TextDocumentPositionParams ) => logErrors ( ( ) => {
166+ const { textDocument , position } = params ;
167+ const { fileName , service , offset } = documents . getServiceInfo ( textDocument , position ) ;
171168 if ( fileName && service && offset != null ) {
172169 let result = service . getDefinitionAt ( fileName , offset ) ;
173- if ( result ) {
174- return ngDefintionToDefintion ( result ) ;
170+ if ( ! result ) {
171+ return ;
172+ }
173+ if ( Array . isArray ( result ) ) {
174+ // Backwards compatibility with old ng.Location[] (ng.Definition)
175+ return ngDefinitionToDefinition ( result ) ;
175176 }
177+ const { textSpan, definitions} = result as ts . DefinitionInfoAndBoundSpan ;
178+ if ( ! definitions || ! definitions . length ) {
179+ return ;
180+ }
181+ const [ start , end ] = documents . offsetsToPositions ( textDocument , [
182+ textSpan . start ,
183+ textSpan . start + textSpan . length ,
184+ ] ) ;
185+ const originSelectionRange = lsp . Range . create (
186+ start . line - 1 , start . character - 1 , end . line - 1 , end . character - 1 ) ;
187+ return definitions . map ( ( d ) => {
188+ const targetUri = lsp . TextDocumentIdentifier . create ( fileNameToUri ( d . fileName ) ) ;
189+ const [ start , end ] = documents . offsetsToPositions ( targetUri , [
190+ d . textSpan . start ,
191+ d . textSpan . start + d . textSpan . length ,
192+ ] ) ;
193+ const targetRange = lsp . Range . create (
194+ start . line - 1 , start . character - 1 , end . line - 1 , end . character - 1 ) ;
195+ return {
196+ originSelectionRange,
197+ targetUri : targetUri . uri ,
198+ targetRange,
199+ targetSelectionRange : targetRange ,
200+ } ;
201+ } ) ;
176202 }
177203} ) ) ;
178204
179- function ngHoverToHover ( hover : ng . Hover , document : TextDocumentIdentifier ) : Hover {
205+ function ngHoverToHover ( hover : ng . Hover , document : lsp . TextDocumentIdentifier ) : lsp . Hover {
180206 if ( hover ) {
181207 const positions = documents . offsetsToPositions ( document , [ hover . span . start , hover . span . end ] ) ;
182208 if ( positions ) {
183- const range = Range . create ( positions [ 0 ] , positions [ 1 ] ) ;
209+ const range = lsp . Range . create ( positions [ 0 ] , positions [ 1 ] ) ;
184210 return {
185211 contents : { language : 'typescript' , value : hover . text . map ( t => t . text ) . join ( '' ) } ,
186212 range
@@ -189,16 +215,55 @@ function ngHoverToHover(hover: ng.Hover, document: TextDocumentIdentifier): Hove
189215 }
190216}
191217
192- connection . onHover ( ( textDocumentPosition : TextDocumentPositionParams ) : Hover => logErrors ( ( ) => {
193- const { fileName , service , offset , languageId } = documents . getServiceInfo ( textDocumentPosition . textDocument ,
194- textDocumentPosition . position )
218+ connection . onHover ( ( params : lsp . TextDocumentPositionParams ) : lsp . Hover => logErrors ( ( ) => {
219+ const { position , textDocument } = params ;
220+ const { fileName , service , offset } = documents . getServiceInfo ( textDocument , position ) ;
195221 if ( fileName && service && offset != null ) {
196- let result = service . getHoverAt ( fileName , offset ) ;
197- if ( result ) {
198- return ngHoverToHover ( result , textDocumentPosition . textDocument ) ;
222+ const result = service . getHoverAt ( fileName , offset ) ;
223+ if ( ! result ) {
224+ return ;
225+ }
226+ if ( isNgHover ( result ) ) {
227+ // Backwards compatibility with old ng.Hover
228+ return ngHoverToHover ( result , params . textDocument ) ;
229+ }
230+ const { kind, kindModifiers, textSpan, displayParts, documentation} = result as ts . QuickInfo ;
231+ let desc = kindModifiers ? kindModifiers + ' ' : '' ;
232+ if ( displayParts ) {
233+ // displayParts does not contain info about kindModifiers
234+ // but displayParts does contain info about kind
235+ desc += displayParts . map ( dp => dp . text ) . join ( '' ) ;
199236 }
237+ else {
238+ desc += kind ;
239+ }
240+ const contents : lsp . MarkedString [ ] = [ {
241+ language : 'typescript' ,
242+ value : desc ,
243+ } ] ;
244+ if ( documentation ) {
245+ for ( const d of documentation ) {
246+ contents . push ( d . text ) ;
247+ }
248+ }
249+ const [ start , end ] = documents . offsetsToPositions ( textDocument , [
250+ textSpan . start ,
251+ textSpan . start + textSpan . length ,
252+ ] ) ;
253+ return {
254+ contents,
255+ range : lsp . Range . create (
256+ start . line - 1 , start . character - 1 , end . line - 1 , end . character - 1 ) ,
257+ } ;
200258 }
201259} ) ) ;
202260
261+ function isNgHover ( result : ng . Hover ) : result is ng . Hover {
262+ if ( result . span && Array . isArray ( result . text ) ) {
263+ return true ;
264+ }
265+ return false ;
266+ }
267+
203268// Listen on the connection
204269connection . listen ( ) ;
0 commit comments