Skip to content

Commit

Permalink
Work around snippet edits doubling up extra indentation
Browse files Browse the repository at this point in the history
We can't tell vscode to not add in the extra indentation, so we instead opt to remove it from the edits themselves, and then let vscode add it back in.
  • Loading branch information
DropDemBits committed Feb 15, 2024
1 parent d846586 commit bcf14e2
Showing 1 changed file with 69 additions and 2 deletions.
71 changes: 69 additions & 2 deletions editors/code/src/snippets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand All @@ -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)];
}

0 comments on commit bcf14e2

Please sign in to comment.