mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-04-17 01:49:41 -05:00
Format completion snippet text before escaping (#48793)
* Format snippet text before escaping * Reset `escapes` before printing so printer can be reused
This commit is contained in:
@@ -1199,18 +1199,19 @@ namespace ts.Completions {
|
||||
function createSnippetPrinter(
|
||||
printerOptions: PrinterOptions,
|
||||
) {
|
||||
let escapes: TextChange[] | undefined;
|
||||
const baseWriter = textChanges.createWriter(getNewLineCharacter(printerOptions));
|
||||
const printer = createPrinter(printerOptions, baseWriter);
|
||||
const writer: EmitTextWriter = {
|
||||
...baseWriter,
|
||||
write: s => baseWriter.write(escapeSnippetText(s)),
|
||||
write: s => escapingWrite(s, () => baseWriter.write(s)),
|
||||
nonEscapingWrite: baseWriter.write,
|
||||
writeLiteral: s => baseWriter.writeLiteral(escapeSnippetText(s)),
|
||||
writeStringLiteral: s => baseWriter.writeStringLiteral(escapeSnippetText(s)),
|
||||
writeSymbol: (s, symbol) => baseWriter.writeSymbol(escapeSnippetText(s), symbol),
|
||||
writeParameter: s => baseWriter.writeParameter(escapeSnippetText(s)),
|
||||
writeComment: s => baseWriter.writeComment(escapeSnippetText(s)),
|
||||
writeProperty: s => baseWriter.writeProperty(escapeSnippetText(s)),
|
||||
writeLiteral: s => escapingWrite(s, () => baseWriter.writeLiteral(s)),
|
||||
writeStringLiteral: s => escapingWrite(s, () => baseWriter.writeStringLiteral(s)),
|
||||
writeSymbol: (s, symbol) => escapingWrite(s, () => baseWriter.writeSymbol(s, symbol)),
|
||||
writeParameter: s => escapingWrite(s, () => baseWriter.writeParameter(s)),
|
||||
writeComment: s => escapingWrite(s, () => baseWriter.writeComment(s)),
|
||||
writeProperty: s => escapingWrite(s, () => baseWriter.writeProperty(s)),
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -1218,12 +1219,39 @@ namespace ts.Completions {
|
||||
printAndFormatSnippetList,
|
||||
};
|
||||
|
||||
// The formatter/scanner will have issues with snippet-escaped text,
|
||||
// so instead of writing the escaped text directly to the writer,
|
||||
// generate a set of changes that can be applied to the unescaped text
|
||||
// to escape it post-formatting.
|
||||
function escapingWrite(s: string, write: () => void) {
|
||||
const escaped = escapeSnippetText(s);
|
||||
if (escaped !== s) {
|
||||
const start = baseWriter.getTextPos();
|
||||
write();
|
||||
const end = baseWriter.getTextPos();
|
||||
escapes = append(escapes ||= [], { newText: escaped, span: { start, length: end - start } });
|
||||
}
|
||||
else {
|
||||
write();
|
||||
}
|
||||
}
|
||||
|
||||
/* Snippet-escaping version of `printer.printList`. */
|
||||
function printSnippetList(
|
||||
format: ListFormat,
|
||||
list: NodeArray<Node>,
|
||||
sourceFile: SourceFile | undefined,
|
||||
): string {
|
||||
const unescaped = printUnescapedSnippetList(format, list, sourceFile);
|
||||
return escapes ? textChanges.applyChanges(unescaped, escapes) : unescaped;
|
||||
}
|
||||
|
||||
function printUnescapedSnippetList(
|
||||
format: ListFormat,
|
||||
list: NodeArray<Node>,
|
||||
sourceFile: SourceFile | undefined,
|
||||
): string {
|
||||
escapes = undefined;
|
||||
writer.clear();
|
||||
printer.writeList(format, list, sourceFile, writer);
|
||||
return writer.getText();
|
||||
@@ -1236,7 +1264,7 @@ namespace ts.Completions {
|
||||
formatContext: formatting.FormatContext,
|
||||
): string {
|
||||
const syntheticFile = {
|
||||
text: printSnippetList(
|
||||
text: printUnescapedSnippetList(
|
||||
format,
|
||||
list,
|
||||
sourceFile),
|
||||
@@ -1256,7 +1284,11 @@ namespace ts.Completions {
|
||||
/* delta */ 0,
|
||||
{ ...formatContext, options: formatOptions });
|
||||
});
|
||||
return textChanges.applyChanges(syntheticFile.text, changes);
|
||||
|
||||
const allChanges = escapes
|
||||
? stableSort(concatenate(changes, escapes), (a, b) => compareTextSpans(a.span, b.span))
|
||||
: changes;
|
||||
return textChanges.applyChanges(syntheticFile.text, allChanges);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
// Case: Snippet text needs escaping
|
||||
////interface DollarSign {
|
||||
//// "$usd"(a: number): number;
|
||||
//// $cad(b: number): number;
|
||||
//// cla$$y(c: number): number;
|
||||
//// isDollarAmountString(s: string): s is `$${number}`
|
||||
////}
|
||||
////class USD implements DollarSign {
|
||||
//// /*a*/
|
||||
@@ -25,6 +28,24 @@ verify.completions({
|
||||
sortText: completion.SortText.ClassMemberSnippets,
|
||||
isSnippet: true,
|
||||
insertText: "\"\\$usd\"(a: number): number {\n $0\n}",
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "$cad",
|
||||
sortText: completion.SortText.ClassMemberSnippets,
|
||||
isSnippet: true,
|
||||
insertText: "\\$cad(b: number): number {\n $0\n}",
|
||||
},
|
||||
{
|
||||
name: "cla$$y",
|
||||
sortText: completion.SortText.ClassMemberSnippets,
|
||||
isSnippet: true,
|
||||
insertText: "cla\\$\\$y(c: number): number {\n $0\n}",
|
||||
},
|
||||
{
|
||||
name: "isDollarAmountString",
|
||||
sortText: completion.SortText.ClassMemberSnippets,
|
||||
isSnippet: true,
|
||||
insertText: "isDollarAmountString(s: string): s is `\\$\\${number}` {\n $0\n}"
|
||||
},
|
||||
],
|
||||
});
|
||||
Reference in New Issue
Block a user