Skip to content

Commit

Permalink
Added Jsx completion feature and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
armanio123 committed Sep 16, 2021
1 parent cdd9314 commit 318b59f
Show file tree
Hide file tree
Showing 10 changed files with 368 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8547,6 +8547,7 @@ namespace ts {
readonly providePrefixAndSuffixTextForRename?: boolean;
readonly includePackageJsonAutoImports?: "auto" | "on" | "off";
readonly provideRefactorNotApplicableReason?: boolean;
readonly jsxSnippetCompletion?: "auto" | "braces" | "none";
}

/** Represents a bigint literal value without requiring bigint support */
Expand Down
1 change: 1 addition & 0 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3391,6 +3391,7 @@ namespace ts.server.protocol {
readonly provideRefactorNotApplicableReason?: boolean;
readonly allowRenameOfImportPath?: boolean;
readonly includePackageJsonAutoImports?: "auto" | "on" | "off";
readonly jsxSnippetCompletion?: "auto" | "braces" | "none";

readonly displayPartsForJSDoc?: boolean;
readonly generateReturnInDocTemplate?: boolean;
Expand Down
33 changes: 32 additions & 1 deletion src/services/completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,37 @@ namespace ts.Completions {
hasAction = !importCompletionNode;
}

const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, location); // TODO: GH#18217
if (kind === ScriptElementKind.jsxAttribute && preferences.jsxSnippetCompletion && preferences.jsxSnippetCompletion !== "none") {
let useBraces = preferences.jsxSnippetCompletion === "braces";
const type = typeChecker.getTypeOfSymbolAtLocation(symbol, location);

// If is boolean like or undefined, don't return a snippet we want just to return the completion.
if (preferences.jsxSnippetCompletion === "auto"
&& !(type.flags & TypeFlags.BooleanLike)
&& !(type.flags & TypeFlags.Union && every((type as UnionType).types, type => !!(type.flags & (TypeFlags.BooleanLike | TypeFlags.Undefined))))
) {
if (type.flags & TypeFlags.StringLike || (type.flags & TypeFlags.Union && every((type as UnionType).types, type => !!(type.flags & (TypeFlags.StringLike | TypeFlags.Undefined))))) {
// If is string like or undefined use quotes
insertText = `${name}=${quote(sourceFile, preferences, "$1")}`;
isSnippet = true;
}
else {
// Use braces for everything else
useBraces = true;
}
}

if (useBraces) {
insertText = `${name}={$1}`;
isSnippet = true;
}

if (isSnippet) {
replacementSpan = createTextSpanFromNode(location, sourceFile);
}
}

// TODO(drosen): Right now we just permit *all* semantic meanings when calling
// 'getSymbolKind' which is permissible given that it is backwards compatible; but
// really we should consider passing the meaning for the node so that we don't report
Expand All @@ -685,7 +716,7 @@ namespace ts.Completions {
// entries (like JavaScript identifier entries).
return {
name,
kind: SymbolDisplay.getSymbolKind(typeChecker, symbol, location), // TODO: GH#18217
kind,
kindModifiers: SymbolDisplay.getSymbolModifiers(typeChecker, symbol),
sortText,
source: getSourceFromOrigin(origin),
Expand Down
2 changes: 2 additions & 0 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3995,6 +3995,7 @@ declare namespace ts {
readonly providePrefixAndSuffixTextForRename?: boolean;
readonly includePackageJsonAutoImports?: "auto" | "on" | "off";
readonly provideRefactorNotApplicableReason?: boolean;
readonly jsxSnippetCompletion?: "auto" | "braces" | "none";
}
/** Represents a bigint literal value without requiring bigint support */
export interface PseudoBigInt {
Expand Down Expand Up @@ -9458,6 +9459,7 @@ declare namespace ts.server.protocol {
readonly provideRefactorNotApplicableReason?: boolean;
readonly allowRenameOfImportPath?: boolean;
readonly includePackageJsonAutoImports?: "auto" | "on" | "off";
readonly jsxSnippetCompletion?: "auto" | "braces" | "none";
readonly displayPartsForJSDoc?: boolean;
readonly generateReturnInDocTemplate?: boolean;
}
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3995,6 +3995,7 @@ declare namespace ts {
readonly providePrefixAndSuffixTextForRename?: boolean;
readonly includePackageJsonAutoImports?: "auto" | "on" | "off";
readonly provideRefactorNotApplicableReason?: boolean;
readonly jsxSnippetCompletion?: "auto" | "braces" | "none";
}
/** Represents a bigint literal value without requiring bigint support */
export interface PseudoBigInt {
Expand Down
1 change: 1 addition & 0 deletions tests/cases/fourslash/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,7 @@ declare namespace FourSlashInterface {
readonly includeAutomaticOptionalChainCompletions?: boolean;
readonly importModuleSpecifierPreference?: "shortest" | "project-relative" | "relative" | "non-relative";
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
readonly jsxSnippetCompletion?: "auto" | "braces" | "none";
}
interface InlayHintsOptions extends UserPreferences {
readonly includeInlayParameterNameHints?: "none" | "literals" | "all";
Expand Down
88 changes: 88 additions & 0 deletions tests/cases/fourslash/jsxAttributeSnippetCompletionAuto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/// <reference path="fourslash.ts" />

// @Filename: foo.tsx
//// declare namespace JSX {
//// interface Element { }
//// interface IntrinsicElements {
//// foo: {
//// prop_a: boolean;
//// prop_b: string;
//// prop_c: any;
//// prop_d: { p1: string; }
//// prop_e: string | undefined;
//// prop_f: boolean | undefined;
//// prop_g: { p1: string; } | undefined;
//// prop_h?: string;
//// prop_i?: boolean;
//// prop_j?: { p1: string; };
//// }
//// }
//// }
////
//// <foo [|prop_/**/|] />

verify.completions({
marker: "",
exact: [
{
name: "prop_a",
isSnippet: undefined,
},
{
name: "prop_b",
insertText: "prop_b=\"$1\"",
replacementSpan: test.ranges()[0],
isSnippet: true,
},
{
name: "prop_c",
insertText: "prop_c={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
},
{
name: "prop_d",
insertText: "prop_d={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
},
{
name: "prop_e",
insertText: "prop_e=\"$1\"",
replacementSpan: test.ranges()[0],
isSnippet: true,
},
{
name: "prop_f",
isSnippet: undefined,
},
{
name: "prop_g",
insertText: "prop_g={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
},
{
name: "prop_h",
insertText: "prop_h=\"$1\"",
replacementSpan: test.ranges()[0],
isSnippet: true,
sortText: completion.SortText.OptionalMember,
},
{
name: "prop_i",
isSnippet: undefined,
sortText: completion.SortText.OptionalMember,
},
{
name: "prop_j",
insertText: "prop_j={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
sortText: completion.SortText.OptionalMember,
}
],
preferences: {
jsxSnippetCompletion: "auto"
}
});
94 changes: 94 additions & 0 deletions tests/cases/fourslash/jsxAttributeSnippetCompletionBraces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/// <reference path="fourslash.ts" />

// @Filename: foo.tsx
//// declare namespace JSX {
//// interface Element { }
//// interface IntrinsicElements {
//// foo: {
//// prop_a: boolean;
//// prop_b: string;
//// prop_c: any;
//// prop_d: { p1: string; }
//// prop_e: string | undefined;
//// prop_f: boolean | undefined;
//// prop_g: { p1: string; } | undefined;
//// prop_h?: string;
//// prop_i?: boolean;
//// prop_j?: { p1: string; };
//// }
//// }
//// }
////
//// <foo [|prop_/**/|] />

verify.completions({
marker: "",
exact: [
{
name: "prop_a",
insertText: "prop_a={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
},
{
name: "prop_b",
insertText: "prop_b={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
},
{
name: "prop_c",
insertText: "prop_c={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
},
{
name: "prop_d",
insertText: "prop_d={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
},
{
name: "prop_e",
insertText: "prop_e={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
},
{
name: "prop_f",
insertText: "prop_f={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
},
{
name: "prop_g",
insertText: "prop_g={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
},
{
name: "prop_h",
insertText: "prop_h={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
sortText: completion.SortText.OptionalMember,
},
{
name: "prop_i",
insertText: "prop_i={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
sortText: completion.SortText.OptionalMember,
},
{
name: "prop_j",
insertText: "prop_j={$1}",
replacementSpan: test.ranges()[0],
isSnippet: true,
sortText: completion.SortText.OptionalMember,
}
],
preferences: {
jsxSnippetCompletion: "braces"
}
});
74 changes: 74 additions & 0 deletions tests/cases/fourslash/jsxAttributeSnippetCompletionDefault.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/// <reference path="fourslash.ts" />

// @Filename: foo.tsx
//// declare namespace JSX {
//// interface Element { }
//// interface IntrinsicElements {
//// foo: {
//// prop_a: boolean;
//// prop_b: string;
//// prop_c: any;
//// prop_d: { p1: string; }
//// prop_e: string | undefined;
//// prop_f: boolean | undefined;
//// prop_g: { p1: string; } | undefined;
//// prop_h?: string;
//// prop_i?: boolean;
//// prop_j?: { p1: string; };
//// }
//// }
//// }
////
//// <foo [|prop_/**/|] />

verify.completions({
marker: "",
exact: [
{
name: "prop_a",
isSnippet: undefined,
},
{
name: "prop_b",
isSnippet: undefined,
},
{
name: "prop_c",
isSnippet: undefined,
},
{
name: "prop_d",
isSnippet: undefined,
},
{
name: "prop_e",
isSnippet: undefined,
},
{
name: "prop_f",
isSnippet: undefined,
},
{
name: "prop_g",
isSnippet: undefined,
},
{
name: "prop_h",
isSnippet: undefined,
sortText: completion.SortText.OptionalMember,
},
{
name: "prop_i",
isSnippet: undefined,
sortText: completion.SortText.OptionalMember,
},
{
name: "prop_j",
isSnippet: undefined,
sortText: completion.SortText.OptionalMember,
}
],
preferences: {
jsxSnippetCompletion: undefined
}
});
Loading

0 comments on commit 318b59f

Please sign in to comment.