6465: Support multiple file edits in AssistBuilder r=matklad a=Veykril

Fixes #6459

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2020-11-09 11:54:42 +00:00 committed by GitHub
commit 2d3b0571bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 14 deletions

View file

@ -208,7 +208,7 @@ pub(crate) struct AssistBuilder {
edit: TextEditBuilder, edit: TextEditBuilder,
file_id: FileId, file_id: FileId,
is_snippet: bool, is_snippet: bool,
change: SourceChange, source_file_edits: Vec<SourceFileEdit>,
} }
impl AssistBuilder { impl AssistBuilder {
@ -217,20 +217,27 @@ impl AssistBuilder {
edit: TextEdit::builder(), edit: TextEdit::builder(),
file_id, file_id,
is_snippet: false, is_snippet: false,
change: SourceChange::default(), source_file_edits: Vec::default(),
} }
} }
pub(crate) fn edit_file(&mut self, file_id: FileId) { pub(crate) fn edit_file(&mut self, file_id: FileId) {
self.commit();
self.file_id = file_id; self.file_id = file_id;
} }
fn commit(&mut self) { fn commit(&mut self) {
let edit = mem::take(&mut self.edit).finish(); let edit = mem::take(&mut self.edit).finish();
if !edit.is_empty() { if !edit.is_empty() {
let new_edit = SourceFileEdit { file_id: self.file_id, edit }; match self.source_file_edits.binary_search_by_key(&self.file_id, |edit| edit.file_id) {
assert!(!self.change.source_file_edits.iter().any(|it| it.file_id == new_edit.file_id)); Ok(idx) => self.source_file_edits[idx]
self.change.source_file_edits.push(new_edit); .edit
.union(edit)
.expect("overlapping edits for same file"),
Err(idx) => self
.source_file_edits
.insert(idx, SourceFileEdit { file_id: self.file_id, edit }),
}
} }
} }
@ -277,10 +284,10 @@ impl AssistBuilder {
fn finish(mut self) -> SourceChange { fn finish(mut self) -> SourceChange {
self.commit(); self.commit();
let mut change = mem::take(&mut self.change); SourceChange {
if self.is_snippet { source_file_edits: mem::take(&mut self.source_file_edits),
change.is_snippet = true; file_system_edits: Default::default(),
is_snippet: self.is_snippet,
} }
change
} }
} }

View file

@ -3,16 +3,29 @@ import * as vscode from 'vscode';
import { assert } from './util'; import { assert } from './util';
export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) { export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`); if (edit.entries().length === 1) {
const [uri, edits] = edit.entries()[0]; const [uri, edits] = edit.entries()[0];
const editor = await editorFromUri(uri);
if (editor) await applySnippetTextEdits(editor, edits);
return;
}
for (const [uri, edits] of edit.entries()) {
const editor = await editorFromUri(uri);
if (editor) await editor.edit((builder) => {
for (const indel of edits) {
assert(!parseSnippet(indel.newText), `bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`);
builder.replace(indel.range, indel.newText);
}
});
}
}
async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undefined> {
if (vscode.window.activeTextEditor?.document.uri !== uri) { if (vscode.window.activeTextEditor?.document.uri !== uri) {
// `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed // `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed
await vscode.window.showTextDocument(uri, {}); await vscode.window.showTextDocument(uri, {});
} }
const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); return vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
if (!editor) return;
await applySnippetTextEdits(editor, edits);
} }
export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) { export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) {