mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 06:03:58 +00:00
Work around snippet edits doubling up extra indentation
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.
This commit is contained in:
parent
d846586bc9
commit
bcf14e27ce
1 changed files with 69 additions and 2 deletions
|
@ -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)];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue