Skip to content

Commit bcf14e2

Browse files
committed
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.
1 parent d846586 commit bcf14e2

1 file changed

Lines changed: 69 additions & 2 deletions

File tree

‎editors/code/src/snippets.ts‎

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export async function applySnippetWorkspaceEdit(
1313
const[uri,edits]=unwrapUndefinable(editEntries[0]);
1414
consteditor=awaiteditorFromUri(uri);
1515
if(editor){
16-
edit.set(uri,edits);
16+
edit.set(uri,removeLeadingWhitespace(editor,edits));
1717
awaitvscode.workspace.applyEdit(edit);
1818
}
1919
return;
@@ -48,7 +48,8 @@ async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undef
4848

4949
exportasyncfunctionapplySnippetTextEdits(editor: vscode.TextEditor,edits: vscode.TextEdit[]){
5050
constedit=newvscode.WorkspaceEdit();
51-
edit.set(editor.document.uri,toSnippetTextEdits(edits));
51+
constsnippetEdits=toSnippetTextEdits(edits);
52+
edit.set(editor.document.uri,removeLeadingWhitespace(editor,snippetEdits));
5253
awaitvscode.workspace.applyEdit(edit);
5354
}
5455

@@ -74,3 +75,69 @@ function toSnippetTextEdits(
7475
}
7576
});
7677
}
78+
79+
/**
80+
* Removes the leading whitespace from snippet edits, so as to not double up
81+
* on indentation.
82+
*
83+
* Snippet edits by default adjust any multi-line snippets to match the
84+
* indentation of the line to insert at. Unfortunately, we (the server) also
85+
* include the required indentation to match what we line insert at, so we end
86+
* up doubling up the indentation. Since there isn't any way to tell vscode to
87+
* not fixup indentation for us, we instead opt to remove the indentation and
88+
* then let vscode add it back in.
89+
*
90+
* This assumes that the source snippet text edits have the required
91+
* indentation, but that's okay as even without this workaround and the problem
92+
* to workaround, those snippet edits would already be inserting at the wrong
93+
* indentation.
94+
*/
95+
functionremoveLeadingWhitespace(
96+
editor: vscode.TextEditor,
97+
edits: (vscode.TextEdit|vscode.SnippetTextEdit)[],
98+
){
99+
returnedits.map((edit)=>{
100+
if(editinstanceofvscode.SnippetTextEdit){
101+
constsnippetEdit: vscode.SnippetTextEdit=edit;
102+
constfirstLineEnd=snippetEdit.snippet.value.indexOf("\n");
103+
104+
if(firstLineEnd!==-1){
105+
// Is a multi-line snippet, remove the indentation which
106+
// would be added back in by vscode.
107+
conststartLine=editor.document.lineAt(snippetEdit.range.start.line);
108+
constleadingWhitespace=getLeadingWhitespace(
109+
startLine.text,
110+
0,
111+
startLine.firstNonWhitespaceCharacterIndex,
112+
);
113+
114+
const[firstLine,rest]=splitAt(snippetEdit.snippet.value,firstLineEnd+1);
115+
constunindentedLines=rest
116+
.split("\n")
117+
.map((line)=>line.replace(leadingWhitespace,""))
118+
.join("\n");
119+
120+
snippetEdit.snippet.value=firstLine+unindentedLines;
121+
}
122+
123+
returnsnippetEdit;
124+
}else{
125+
returnedit;
126+
}
127+
});
128+
}
129+
130+
// based on https://github.com/microsoft/vscode/blob/main/src/vs/base/common/strings.ts#L284
131+
functiongetLeadingWhitespace(str: string,start: number=0,end: number=str.length): string{
132+
for(leti=start;i<end;i++){
133+
constchCode=str.charCodeAt(i);
134+
if(chCode!==" ".charCodeAt(0)&&chCode!==" ".charCodeAt(0)){
135+
returnstr.substring(start,i);
136+
}
137+
}
138+
returnstr.substring(start,end);
139+
}
140+
141+
functionsplitAt(str: string,index: number): [string,string]{
142+
return[str.substring(0,index),str.substring(index)];
143+
}

0 commit comments

Comments
 (0)