Skip to content

Commit cc2fc95

Browse files
authored
(feat) show doc for props (hover/completion) (#529)
#306
1 parent a630dea commit cc2fc95

File tree

7 files changed

+136
-34
lines changed

7 files changed

+136
-34
lines changed

packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import ts from 'typescript';
2+
13
export interface IExportedNames {
24
has(name: string): boolean;
35
}
@@ -9,34 +11,84 @@ export class ExportedNames
911
type?: string;
1012
identifierText?: string;
1113
required?: boolean;
14+
doc?: string;
1215
}
1316
>
1417
implements IExportedNames {
18+
/**
19+
* Adds export to map
20+
*/
21+
addExport(
22+
name: ts.BindingName,
23+
target: ts.BindingName = null,
24+
type: ts.TypeNode = null,
25+
required = false,
26+
): void {
27+
if (name.kind != ts.SyntaxKind.Identifier) {
28+
throw Error('export source kind not supported ' + name);
29+
}
30+
if (target && target.kind != ts.SyntaxKind.Identifier) {
31+
throw Error('export target kind not supported ' + target);
32+
}
33+
34+
if (target) {
35+
this.set(name.text, {
36+
type: type?.getText(),
37+
identifierText: (target as ts.Identifier).text,
38+
required,
39+
doc: this.getDoc(target),
40+
});
41+
} else {
42+
this.set(name.text, {});
43+
}
44+
}
45+
46+
private getDoc(target: ts.BindingName) {
47+
let doc = undefined;
48+
// Traverse `a` up to `export let a`
49+
const exportExpr = target?.parent?.parent?.parent;
50+
51+
if (exportExpr) {
52+
const fileText = exportExpr.getSourceFile().getFullText();
53+
const comment = ts.getLeadingCommentRanges(fileText, exportExpr.getFullStart());
54+
55+
if (comment) {
56+
doc = fileText.substring(comment[0].pos, comment[0].end);
57+
}
58+
}
59+
60+
return doc;
61+
}
62+
1563
/**
1664
* Creates a string from the collected props
1765
*
1866
* @param isTsFile Whether this is a TypeScript file or not.
1967
*/
2068
createPropsStr(isTsFile: boolean) {
2169
const names = Array.from(this.entries());
70+
const dontAddTypeDef =
71+
!isTsFile ||
72+
names.length === 0 ||
73+
names.every(([_, value]) => !value.type && value.required);
2274

2375
const returnElements = names.map(([key, value]) => {
2476
// Important to not use shorthand props for rename functionality
25-
return `${value.identifierText || key}: ${key}`;
77+
return `${dontAddTypeDef && value.doc ? `\n${value.doc}` : ''}${
78+
value.identifierText || key
79+
}: ${key}`;
2680
});
2781

28-
if (
29-
!isTsFile ||
30-
names.length === 0 ||
31-
names.every(([_, value]) => !value.type && value.required)
32-
) {
82+
if (dontAddTypeDef) {
3383
// No exports or only `typeof` exports -> omit the `as {...}` completely.
3484
// If not TS, omit the types to not have a "cannot use types in jsx" error.
3585
return `{${returnElements.join(' , ')}}`;
3686
}
3787

3888
const returnElementsType = names.map(([key, value]) => {
39-
const identifier = `${value.identifierText || key}${value.required ? '' : '?'}`;
89+
const identifier = `${value.doc ? `\n${value.doc}` : ''}${value.identifierText || key}${
90+
value.required ? '' : '?'
91+
}`;
4092
if (!value.type) {
4193
return `${identifier}: typeof ${key}`;
4294
}

packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -58,28 +58,6 @@ export function processInstanceScriptContent(
5858
const pushScope = () => (scope = new Scope(scope));
5959
const popScope = () => (scope = scope.parent);
6060

61-
const addExport = (
62-
name: ts.BindingName,
63-
target: ts.BindingName = null,
64-
type: ts.TypeNode = null,
65-
required = false,
66-
) => {
67-
if (name.kind != ts.SyntaxKind.Identifier) {
68-
throw Error('export source kind not supported ' + name);
69-
}
70-
if (target && target.kind != ts.SyntaxKind.Identifier) {
71-
throw Error('export target kind not supported ' + target);
72-
}
73-
if (target) {
74-
exportedNames.set(name.text, {
75-
type: type?.getText(),
76-
identifierText: (target as ts.Identifier).text,
77-
required,
78-
});
79-
} else {
80-
exportedNames.set(name.text, {});
81-
}
82-
};
8361
const addGetter = (node: ts.Identifier) => {
8462
if (!node) {
8563
return;
@@ -277,14 +255,14 @@ export function processInstanceScriptContent(
277255
ts.forEachChild(list, (node) => {
278256
if (ts.isVariableDeclaration(node)) {
279257
if (ts.isIdentifier(node.name)) {
280-
addExport(node.name, node.name, node.type, !node.initializer);
258+
exportedNames.addExport(node.name, node.name, node.type, !node.initializer);
281259
} else if (
282260
ts.isObjectBindingPattern(node.name) ||
283261
ts.isArrayBindingPattern(node.name)
284262
) {
285263
ts.forEachChild(node.name, (element) => {
286264
if (ts.isBindingElement(element)) {
287-
addExport(element.name);
265+
exportedNames.addExport(element.name);
288266
}
289267
});
290268
}
@@ -376,9 +354,9 @@ export function processInstanceScriptContent(
376354
if (ts.isNamedExports(exportClause)) {
377355
for (const ne of exportClause.elements) {
378356
if (ne.propertyName) {
379-
addExport(ne.propertyName, ne.name);
357+
exportedNames.addExport(ne.propertyName, ne.name);
380358
} else {
381-
addExport(ne.name);
359+
exportedNames.addExport(ne.name);
382360
}
383361
}
384362
//we can remove entire statement
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
///<reference types="svelte" />
2+
<></>;function render() {
3+
4+
/**
5+
* DOCS!
6+
*/
7+
let a;
8+
/**
9+
* MORE DOCS!
10+
*/
11+
let b;
12+
let c;
13+
;
14+
() => (<></>);
15+
return { props: {
16+
/**
17+
* DOCS!
18+
*/a: a ,
19+
/**
20+
* MORE DOCS!
21+
*/b: b , c: c}, slots: {}, getters: {}, events: {} }}
22+
23+
export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) {
24+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
/**
3+
* DOCS!
4+
*/
5+
export let a;
6+
/**
7+
* MORE DOCS!
8+
*/
9+
export let b;
10+
export let c;
11+
</script>

packages/svelte2tsx/test/svelte2tsx/samples/export-with-default-multi/expected.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
world = '';
77
;
88
() => (<></>);
9-
return { props: {name: name , world: world}, slots: {}, getters: {}, events: {} }}
9+
return { props: {
10+
/**@type { string | number }*/name: name ,
11+
/**@type { string | number }*/world: world}, slots: {}, getters: {}, events: {} }}
1012

1113
export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) {
1214
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
///<reference types="svelte" />
2+
<></>;function render() {
3+
4+
/**
5+
* DOCS!
6+
*/
7+
let a: string;
8+
/**
9+
* MORE DOCS!
10+
*/
11+
let b = 1;
12+
let c;
13+
;
14+
() => (<></>);
15+
return { props: {a: a , b: b , c: c} as {
16+
/**
17+
* DOCS!
18+
*/a: string,
19+
/**
20+
* MORE DOCS!
21+
*/b?: typeof b, c: typeof c}, slots: {}, getters: {}, events: {} }}
22+
23+
export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) {
24+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
/**
3+
* DOCS!
4+
*/
5+
export let a: string;
6+
/**
7+
* MORE DOCS!
8+
*/
9+
export let b = 1;
10+
export let c;
11+
</script>

0 commit comments

Comments
 (0)