WIP: snippet support on emitter, adding snippets in completions

This commit is contained in:
Gabriela Araujo Britto
2021-10-04 18:13:16 -07:00
parent d20eb6852a
commit c81f3856be
8 changed files with 112 additions and 10 deletions
+4
View File
@@ -351,6 +351,10 @@ namespace ts {
return formatEnum(kind, (ts as any).SyntaxKind, /*isFlags*/ false);
}
export function formatSnippetKind(kind: SnippetKind | undefined): string {
return formatEnum(kind, (ts as any).SnippetKind, /*isFlags*/ false);
}
export function formatNodeFlags(flags: NodeFlags | undefined): string {
return formatEnum(flags, (ts as any).NodeFlags, /*isFlags*/ true);
}
+31 -1
View File
@@ -1302,7 +1302,13 @@ namespace ts {
currentParenthesizerRule = undefined;
}
function pipelineEmitWithHintWorker(hint: EmitHint, node: Node): void {
function pipelineEmitWithHintWorker(hint: EmitHint, node: Node, allowSnippets = true): void {
if (allowSnippets) {
const snippet = getSnippetElement(node);
if (snippet) {
return emitSnippetNode(hint, node, snippet);
}
}
if (hint === EmitHint.SourceFile) return emitSourceFile(cast(node, isSourceFile));
if (hint === EmitHint.IdentifierName) return emitIdentifier(cast(node, isIdentifier));
if (hint === EmitHint.JsxAttributeValue) return emitLiteral(cast(node, isStringLiteral), /*jsxAttributeEscape*/ true);
@@ -1937,6 +1943,30 @@ namespace ts {
}
}
//
// Snippet Elements
//
function emitSnippetNode(hint: EmitHint, node: Node, snippet: SnippetElement) {
switch (snippet.kind) {
case SnippetKind.Placeholder:
return emitPlaceholder(hint, node, snippet);
case SnippetKind.TabStop:
return emitTabStop(snippet);
}
}
function emitPlaceholder(hint: EmitHint, node: Node, snippet: Placeholder) {
// write(`\$\{${order}:${defaultText}\}`);
write(`\$\{${snippet.order}:`);
pipelineEmitWithHintWorker(hint, node, /*allowSnippets*/ false);
write(`\}`);
}
function emitTabStop(snippet: TabStop) {
write(`\$${snippet.order}`);
}
//
// Identifiers
//
+16
View File
@@ -256,6 +256,22 @@ namespace ts {
}
}
/**
* Gets the SnippetElement of a node.
*/
export function getSnippetElement(node: Node): SnippetElement | undefined {
return node.emitNode?.snippetElement;
}
/**
* Sets the SnippetElement of a node.
*/
export function setSnippetElement<T extends Node>(node: T, snippet: SnippetElement): T {
const emitNode = getOrCreateEmitNode(node);
emitNode.snippetElement = snippet;
return node;
}
/* @internal */
export function ignoreSourceNewlines<T extends Node>(node: T): T {
getOrCreateEmitNode(node).flags |= EmitFlags.IgnoreSourceNewlines;
+3 -5
View File
@@ -6740,16 +6740,14 @@ namespace ts {
snippetElement?: SnippetElement; // Snippet element of the node
}
export interface SnippetElement {
kind: SnippetKind;
}
export type SnippetElement = TabStop | Placeholder;
export interface TabStop extends SnippetElement {
export interface TabStop {
kind: SnippetKind.TabStop;
order: number;
}
export interface PlaceHolder extends SnippetElement {
export interface Placeholder {
kind: SnippetKind.Placeholder;
order: number;
}
+2 -2
View File
@@ -3142,7 +3142,7 @@ namespace ts {
return undefined;
}
export function isKeyword(token: SyntaxKind): boolean {
export function isKeyword(token: SyntaxKind): token is KeywordSyntaxKind {
return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword;
}
@@ -3327,7 +3327,7 @@ namespace ts {
return node.escapedText === "push" || node.escapedText === "unshift";
}
export function isParameterDeclaration(node: VariableLikeDeclaration) {
export function isParameterDeclaration(node: VariableLikeDeclaration): node is ParameterDeclaration {
const root = getRootDeclaration(node);
return root.kind === SyntaxKind.Parameter;
}
+32 -2
View File
@@ -760,8 +760,11 @@ namespace ts.Completions {
let body;
if (preferences.includeCompletionsWithSnippetText) {
isSnippet = true;
const tabStopStatement = factory.createExpressionStatement(factory.createIdentifier("$1"));
body = factory.createBlock([tabStopStatement], /* multiline */ true);
// const tabStopStatement = factory.createExpressionStatement(factory.createIdentifier("$1"));
// body = factory.createBlock([tabStopStatement], /* multiline */ true);
const emptyStatement = factory.createExpressionStatement(factory.createIdentifier(""));
setSnippetElement(emptyStatement, { kind: SnippetKind.TabStop, order: 0 });
body = factory.createBlock([emptyStatement], /* multiline */ true);
}
else {
body = factory.createBlock([], /* multiline */ true);
@@ -813,6 +816,7 @@ namespace ts.Completions {
concatenate([factory.createModifier(SyntaxKind.OverrideKeyword)], node.modifiers),
);
}
addSnippets(node);
completionNodes.push(node);
},
body);
@@ -823,6 +827,32 @@ namespace ts.Completions {
return { insertText, isSnippet };
}
function addSnippets(node: Node): void {
let order = 1;
addSnippetsWorker(node);
function addSnippetsWorker(node: Node) {
if (isVariableLike(node) && isParameterDeclaration(node)) {
// Placeholder
setSnippetElement(node.name, { kind: SnippetKind.Placeholder, order });
order += 1;
if (node.type) {
setSnippetElement(node.type, { kind: SnippetKind.Placeholder, order });
order += 1;
}
}
else if (isFunctionLikeDeclaration(node)) {
if (node.type) {
setSnippetElement(node.type, { kind: SnippetKind.Placeholder, order });
order += 1;
}
}
forEachChild(node, addSnippetsWorker);
}
}
function originToCompletionEntryData(origin: SymbolOriginInfoExport): CompletionEntryData | undefined {
return {
exportName: origin.exportName,
+23
View File
@@ -0,0 +1,23 @@
/* @internal */
namespace ts.snippets {
// interface SnippetWriter extends EmitTextWriter, PrintHandlers {}
// function createSnippetWriter(newLine: string): SnippetWriter {
// const substituteNode = (hint: EmitHint, node: Node) => {
// switch (hint) {
// case EmitHint.IdentifierName:
// // >> TODO: do escaping here
// return node;
// }
// return node;
// };
// const writer = createTextWriter(newLine);
// return {
// ...writer,
// substituteNode,
// };
// }
}
+1
View File
@@ -33,6 +33,7 @@
"preProcess.ts",
"rename.ts",
"smartSelection.ts",
"snippets.ts",
"signatureHelp.ts",
"inlayHints.ts",
"sourcemaps.ts",