forked from sveltejs/language-tools
/
HoverProvider.ts
80 lines (68 loc) · 2.78 KB
/
HoverProvider.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import ts from 'typescript';
import { Hover, Position } from 'vscode-languageserver';
import { Document, getWordAt, mapObjWithRangeToOriginal } from '../../../lib/documents';
import { HoverProvider } from '../../interfaces';
import { SvelteDocumentSnapshot } from '../DocumentSnapshot';
import { LSAndTSDocResolver } from '../LSAndTSDocResolver';
import { getMarkdownDocumentation } from '../previewer';
import { convertRange } from '../utils';
import { getComponentAtPosition } from './utils';
export class HoverProviderImpl implements HoverProvider {
constructor(private readonly lsAndTsDocResolver: LSAndTSDocResolver) {}
async doHover(document: Document, position: Position): Promise<Hover | null> {
const { lang, tsDoc } = await this.getLSAndTSDoc(document);
const eventHoverInfo = this.getEventHoverInfo(lang, document, tsDoc, position);
if (eventHoverInfo) {
return eventHoverInfo;
}
const offset = tsDoc.offsetAt(tsDoc.getGeneratedPosition(position));
const info = lang.getQuickInfoAtPosition(tsDoc.filePath, offset);
if (!info) {
return null;
}
const declaration = ts.displayPartsToString(info.displayParts);
const documentation = getMarkdownDocumentation(info.documentation, info.tags);
// https://microsoft.github.io/language-server-protocol/specification#textDocument_hover
const contents = ['```typescript', declaration, '```']
.concat(documentation ? ['---', documentation] : [])
.join('\n');
return mapObjWithRangeToOriginal(tsDoc, {
range: convertRange(tsDoc, info.textSpan),
contents
});
}
private getEventHoverInfo(
lang: ts.LanguageService,
doc: Document,
tsDoc: SvelteDocumentSnapshot,
originalPosition: Position
): Hover | null {
const possibleEventName = getWordAt(doc.getText(), doc.offsetAt(originalPosition), {
left: /\S+$/,
right: /[\s=]/
});
if (!possibleEventName.startsWith('on:')) {
return null;
}
const component = getComponentAtPosition(lang, doc, tsDoc, originalPosition);
if (!component) {
return null;
}
const eventName = possibleEventName.substr('on:'.length);
const event = component.getEvents().find((event) => event.name === eventName);
if (!event) {
return null;
}
return {
contents: [
'```typescript',
`${event.name}: ${event.type}`,
'```',
event.doc || ''
].join('\n')
};
}
private async getLSAndTSDoc(document: Document) {
return this.lsAndTsDocResolver.getLSAndTSDoc(document);
}
}