Skip to content

Commit 91dbe86

Browse files
committed
feat(ls): hook-up hover added to @angular/language-service
1 parent 63db792 commit 91dbe86

File tree

3 files changed

+89
-22
lines changed

3 files changed

+89
-22
lines changed

server/src/documents.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ function uriToFileName(uri: string): string {
113113
}
114114

115115
const fileProtocol = "file://";
116-
function fileNameToUri(fileName: string): string {
116+
export function fileNameToUri(fileName: string): string {
117117
return encodeURI(fileProtocol + fileName);
118118
}
119119

@@ -215,7 +215,10 @@ export class TextDocuments {
215215
public offsetsToPositions(document: TextDocumentIdentifier, offsets: number[]): Position[] {
216216
const file = uriToFileName(document.uri);
217217
if (file) {
218-
return this.projectService.positionsToLineOffsets(file, offsets).map(lineOffset => Position.create(lineOffset.line - 1, lineOffset.col - 1));
218+
const lineOffsets = this.projectService.positionsToLineOffsets(file, offsets)
219+
if (lineOffsets) {
220+
return lineOffsets.map(lineOffset => Position.create(lineOffset.line - 1, lineOffset.col - 1));
221+
}
219222
}
220223
return [];
221224
}

server/src/editorServices.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,31 +1677,38 @@ export class ProjectService {
16771677
this.report("change", file, project);
16781678
}
16791679

1680-
/**
1681-
*
1682-
*/
1683-
lineOffsetsToPositions(fileName: string, positions: {line: number, col: number}[]): number[] {
1684-
const file = normalizePath(fileName);
1685-
const project = this.getProjectForFile(file);
1686-
if (project && !project.languageServiceDiabled) {
1687-
const compilerService = project.compilerService;
1688-
return positions.map(position => compilerService.host.lineOffsetToPosition(file, position.line, position.col));
1680+
forcedGetProjectForFile(fileName: string): Project {
1681+
const file = normalizePath(fileName);
1682+
const info = this.filenameToScriptInfo[file];
1683+
if (info) {
1684+
let project = info.defaultProject;
1685+
if (!project) {
1686+
// Force the association of the info with a default project.
1687+
this.findReferencingProjects(info);
1688+
project = info.defaultProject;
16891689
}
1690-
return undefined;
1690+
return project;
1691+
}
1692+
}
1693+
1694+
lineOffsetsToPositions(fileName: string, positions: {line: number, col: number}[]): number[] {
1695+
const project = this.forcedGetProjectForFile(fileName);
1696+
if (project && !project.languageServiceDiabled) {
1697+
const compilerService = project.compilerService;
1698+
return positions.map(position => compilerService.host.lineOffsetToPosition(fileName, position.line, position.col));
1699+
}
16911700
}
16921701

16931702
positionsToLineOffsets(fileName: string, offsets: number[]): {line: number, col: number}[] {
1694-
const file = normalizePath(fileName);
1695-
const project = this.getProjectForFile(file);
1703+
const project = this.forcedGetProjectForFile(fileName);
16961704
if (project && !project.languageServiceDiabled) {
16971705
const compilerService = project.compilerService;
16981706
return offsets.map(offset => compilerService.host.positionToLineOffset(fileName, offset)).map(pos => ({line: pos.line, col: pos.offset}));
16991707
}
17001708
}
17011709

17021710
positionToLineOffset(fileName: string, offset: number) {
1703-
const file = normalizePath(fileName);
1704-
const project = this.getProjectForFile(file);
1711+
const project = this.forcedGetProjectForFile(fileName);
17051712
if (project && !project.languageServiceDiabled) {
17061713
const compilerService = project.compilerService;
17071714
return compilerService.host.positionToLineOffset(fileName, offset);
@@ -3037,7 +3044,6 @@ export class LineLeaf implements LineCollection {
30373044
}
30383045
}
30393046

3040-
30413047
function logServiceTimes(logger: Logger, service: ng.LanguageService): ng.LanguageService {
30423048
function time<T>(name: string, cb: () => T): T {
30433049
const start = Date.now();
@@ -3055,6 +3061,12 @@ function logServiceTimes(logger: Logger, service: ng.LanguageService): ng.Langua
30553061
getTemplateReferences() {
30563062
return time("getTemplateRefrences", () => service.getTemplateReferences());
30573063
},
3064+
getDefinitionAt(fileName, position) {
3065+
return time("getDefinitionAt", () => service.getDefinitionAt(fileName, position));
3066+
},
3067+
getHoverAt(fileName, position) {
3068+
return time("getHoverAt", () => service.getHoverAt(fileName, position));
3069+
},
30583070
getPipesAt(fileName, position) {
30593071
return service.getPipesAt(fileName, position);
30603072
}

server/src/server.ts

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@
77
/// <reference path="../typings/promise.d.ts" />
88
/// <reference path="../node_modules/@types/node/index.d.ts" />
99

10+
import * as ng from '@angular/language-service';
11+
1012
import 'reflect-metadata';
1113
import {
1214
IPCMessageReader, IPCMessageWriter,
1315
createConnection, IConnection, TextDocumentSyncKind,
1416
TextDocument, Diagnostic, DiagnosticSeverity,
1517
InitializeParams, InitializeResult, TextDocumentPositionParams,
16-
CompletionItem, CompletionItemKind, Definition, TextDocumentIdentifier,
17-
Position, Range, TextEdit
18+
CompletionItem, CompletionItemKind, Definition, Location, TextDocumentIdentifier,
19+
Position, Range, TextEdit, Hover
1820
} from 'vscode-languageserver';
1921

20-
import {TextDocuments, TextDocumentEvent} from './documents';
22+
import {TextDocuments, TextDocumentEvent, fileNameToUri} from './documents';
2123
import {ErrorCollector} from './errors';
2224

2325
import {Completion, Span} from '@angular/language-service';
@@ -60,7 +62,9 @@ connection.onInitialize((params): InitializeResult => {
6062
completionProvider: {
6163
resolveProvider: false,
6264
triggerCharacters: ['<', '.', '*', '[', '(']
63-
}
65+
},
66+
definitionProvider: true,
67+
hoverProvider: true
6468
}
6569
}
6670
});
@@ -93,7 +97,6 @@ function insertionToEdit(range: Range, insertText: string): TextEdit {
9397
}
9498
}
9599

96-
97100
function getReplaceRange(document: TextDocumentIdentifier, offset: number): Range {
98101
const line = documents.getDocumentLine(document, offset);
99102
if (line && line.text && line.start <= offset && line.start + line.text.length >= offset) {
@@ -146,5 +149,54 @@ connection.onCompletion((textDocumentPosition: TextDocumentPositionParams): Comp
146149
}
147150
});
148151

152+
function ngDefintionToDefintion(definition: ng.Definition): Definition {
153+
const locations = definition.map(d => {
154+
const document = TextDocumentIdentifier.create(fileNameToUri(d.fileName));
155+
const positions = documents.offsetsToPositions(document, [d.span.start, d.span.end]);
156+
return {document, positions}
157+
}).filter(d => d.positions.length > 0).map(d => {
158+
const range = Range.create(d.positions[0], d.positions[1]);
159+
return Location.create(d.document.uri, range);
160+
});
161+
if (locations && locations.length) {
162+
return locations;
163+
}
164+
}
165+
166+
connection.onDefinition((textDocumentPosition: TextDocumentPositionParams): Definition => {
167+
const {fileName, service, offset, languageId} = documents.getServiceInfo(textDocumentPosition.textDocument,
168+
textDocumentPosition.position)
169+
if (fileName && service && offset != null) {
170+
let result = service.getDefinitionAt(fileName, offset);
171+
if (result) {
172+
return ngDefintionToDefintion(result);
173+
}
174+
}
175+
});
176+
177+
function ngHoverToHover(hover: ng.Hover, document: TextDocumentIdentifier): Hover {
178+
if (hover) {
179+
const positions = documents.offsetsToPositions(document, [hover.span.start, hover.span.end]);
180+
if (positions) {
181+
const range = Range.create(positions[0], positions[1]);
182+
return {
183+
contents: {language: 'typescript', value: hover.text.map(t => t.text).join('')},
184+
range
185+
};
186+
}
187+
}
188+
}
189+
190+
connection.onHover((textDocumentPosition: TextDocumentPositionParams): Hover => {
191+
const {fileName, service, offset, languageId} = documents.getServiceInfo(textDocumentPosition.textDocument,
192+
textDocumentPosition.position)
193+
if (fileName && service && offset != null) {
194+
let result = service.getHoverAt(fileName, offset);
195+
if (result) {
196+
return ngHoverToHover(result, textDocumentPosition.textDocument);
197+
}
198+
}
199+
});
200+
149201
// Listen on the connection
150202
connection.listen();

0 commit comments

Comments
 (0)