Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'editors/code/src/snippets.ts')
-rw-r--r--editors/code/src/snippets.ts71
1 files changed, 69 insertions, 2 deletions
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts
index fb12125bcd..b3982bdf2b 100644
--- a/editors/code/src/snippets.ts
+++ b/editors/code/src/snippets.ts
@@ -13,7 +13,7 @@ export async function applySnippetWorkspaceEdit(
const [uri, edits] = unwrapUndefinable(editEntries[0]);
const editor = await editorFromUri(uri);
if (editor) {
- edit.set(uri, edits);
+ edit.set(uri, removeLeadingWhitespace(editor, edits));
await vscode.workspace.applyEdit(edit);
}
return;
@@ -48,7 +48,8 @@ async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undef
export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) {
const edit = new vscode.WorkspaceEdit();
- edit.set(editor.document.uri, toSnippetTextEdits(edits));
+ const snippetEdits = toSnippetTextEdits(edits);
+ edit.set(editor.document.uri, removeLeadingWhitespace(editor, snippetEdits));
await vscode.workspace.applyEdit(edit);
}
@@ -74,3 +75,69 @@ function toSnippetTextEdits(
}
});
}
+
+/**
+ * Removes the leading whitespace from snippet edits, so as to not double up
+ * on indentation.
+ *
+ * Snippet edits by default adjust any multi-line snippets to match the
+ * indentation of the line to insert at. Unfortunately, we (the server) also
+ * include the required indentation to match what we line insert at, so we end
+ * up doubling up the indentation. Since there isn't any way to tell vscode to
+ * not fixup indentation for us, we instead opt to remove the indentation and
+ * then let vscode add it back in.
+ *
+ * This assumes that the source snippet text edits have the required
+ * indentation, but that's okay as even without this workaround and the problem
+ * to workaround, those snippet edits would already be inserting at the wrong
+ * indentation.
+ */
+function removeLeadingWhitespace(
+ editor: vscode.TextEditor,
+ edits: (vscode.TextEdit | vscode.SnippetTextEdit)[],
+) {
+ return edits.map((edit) => {
+ if (edit instanceof vscode.SnippetTextEdit) {
+ const snippetEdit: vscode.SnippetTextEdit = edit;
+ const firstLineEnd = snippetEdit.snippet.value.indexOf("\n");
+
+ if (firstLineEnd !== -1) {
+ // Is a multi-line snippet, remove the indentation which
+ // would be added back in by vscode.
+ const startLine = editor.document.lineAt(snippetEdit.range.start.line);
+ const leadingWhitespace = getLeadingWhitespace(
+ startLine.text,
+ 0,
+ startLine.firstNonWhitespaceCharacterIndex,
+ );
+
+ const [firstLine, rest] = splitAt(snippetEdit.snippet.value, firstLineEnd + 1);
+ const unindentedLines = rest
+ .split("\n")
+ .map((line) => line.replace(leadingWhitespace, ""))
+ .join("\n");
+
+ snippetEdit.snippet.value = firstLine + unindentedLines;
+ }
+
+ return snippetEdit;
+ } else {
+ return edit;
+ }
+ });
+}
+
+// based on https://github.com/microsoft/vscode/blob/main/src/vs/base/common/strings.ts#L284
+function getLeadingWhitespace(str: string, start: number = 0, end: number = str.length): string {
+ for (let i = start; i < end; i++) {
+ const chCode = str.charCodeAt(i);
+ if (chCode !== " ".charCodeAt(0) && chCode !== " ".charCodeAt(0)) {
+ return str.substring(start, i);
+ }
+ }
+ return str.substring(start, end);
+}
+
+function splitAt(str: string, index: number): [string, string] {
+ return [str.substring(0, index), str.substring(index)];
+}