Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import * as vscode from "vscode";

import { assert } from "./util";
import { unwrapUndefinable } from "./undefinable";

export type SnippetTextDocumentEdit = [vscode.Uri, (vscode.TextEdit | vscode.SnippetTextEdit)[]];

export async function applySnippetWorkspaceEdit(
    edit: vscode.WorkspaceEdit,
    editEntries: SnippetTextDocumentEdit[],
) {
    if (editEntries.length === 1) {
        const [uri, edits] = unwrapUndefinable(editEntries[0]);
        const editor = await editorFromUri(uri);
        if (editor) {
            edit.set(uri, edits);
            await vscode.workspace.applyEdit(edit);
        }
        return;
    }
    for (const [uri, edits] of editEntries) {
        const editor = await editorFromUri(uri);
        if (editor) {
            await editor.edit((builder) => {
                for (const indel of edits) {
                    assert(
                        !(indel instanceof vscode.SnippetTextEdit),
                        `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) {
        // `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed
        await vscode.window.showTextDocument(uri, {});
    }
    return vscode.window.visibleTextEditors.find(
        (it) => it.document.uri.toString() === uri.toString(),
    );
}

export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) {
    const edit = new vscode.WorkspaceEdit();
    edit.set(editor.document.uri, toSnippetTextEdits(edits));
    await vscode.workspace.applyEdit(edit);
}

function hasSnippet(snip: string): boolean {
    const m = snip.match(/\$\d+|\{\d+:[^}]*\}/);
    return m != null;
}

function toSnippetTextEdits(
    edits: vscode.TextEdit[],
): (vscode.TextEdit | vscode.SnippetTextEdit)[] {
    return edits.map((textEdit) => {
        // Note: text edits without any snippets are returned as-is instead of
        // being wrapped in a SnippetTextEdit, as otherwise it would be
        // treated as if it had a tab stop at the end.
        if (hasSnippet(textEdit.newText)) {
            return new vscode.SnippetTextEdit(
                textEdit.range,
                new vscode.SnippetString(textEdit.newText),
            );
        } else {
            return textEdit;
        }
    });
}