@@ -3,20 +3,28 @@ import * as vscode from "vscode";
3
3
import { assert } from "./util" ;
4
4
import { unwrapUndefinable } from "./undefinable" ;
5
5
6
- export async function applySnippetWorkspaceEdit ( edit : vscode . WorkspaceEdit ) {
7
- if ( edit . entries ( ) . length === 1 ) {
8
- const [ uri , edits ] = unwrapUndefinable ( edit . entries ( ) [ 0 ] ) ;
6
+ export type SnippetTextDocumentEdit = [ vscode . Uri , ( vscode . TextEdit | vscode . SnippetTextEdit ) [ ] ] ;
7
+
8
+ export async function applySnippetWorkspaceEdit (
9
+ edit : vscode . WorkspaceEdit ,
10
+ editEntries : SnippetTextDocumentEdit [ ] ,
11
+ ) {
12
+ if ( editEntries . length === 1 ) {
13
+ const [ uri , edits ] = unwrapUndefinable ( editEntries [ 0 ] ) ;
9
14
const editor = await editorFromUri ( uri ) ;
10
- if ( editor ) await applySnippetTextEdits ( editor , edits ) ;
15
+ if ( editor ) {
16
+ edit . set ( uri , edits ) ;
17
+ await vscode . workspace . applyEdit ( edit ) ;
18
+ }
11
19
return ;
12
20
}
13
- for ( const [ uri , edits ] of edit . entries ( ) ) {
21
+ for ( const [ uri , edits ] of editEntries ) {
14
22
const editor = await editorFromUri ( uri ) ;
15
23
if ( editor ) {
16
24
await editor . edit ( ( builder ) => {
17
25
for ( const indel of edits ) {
18
26
assert (
19
- ! parseSnippet ( indel . newText ) ,
27
+ ! ( indel instanceof vscode . SnippetTextEdit ) ,
20
28
`bad ws edit: snippet received with multiple edits: ${ JSON . stringify (
21
29
edit ,
22
30
) } `,
@@ -39,53 +47,30 @@ async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undef
39
47
}
40
48
41
49
export async function applySnippetTextEdits ( editor : vscode . TextEditor , edits : vscode . TextEdit [ ] ) {
42
- const selections : vscode . Selection [ ] = [ ] ;
43
- let lineDelta = 0 ;
44
- await editor . edit ( ( builder ) => {
45
- for ( const indel of edits ) {
46
- const parsed = parseSnippet ( indel . newText ) ;
47
- if ( parsed ) {
48
- const [ newText , [ placeholderStart , placeholderLength ] ] = parsed ;
49
- const prefix = newText . substr ( 0 , placeholderStart ) ;
50
- const lastNewline = prefix . lastIndexOf ( "\n" ) ;
51
-
52
- const startLine = indel . range . start . line + lineDelta + countLines ( prefix ) ;
53
- const startColumn =
54
- lastNewline === - 1
55
- ? indel . range . start . character + placeholderStart
56
- : prefix . length - lastNewline - 1 ;
57
- const endColumn = startColumn + placeholderLength ;
58
- selections . push (
59
- new vscode . Selection (
60
- new vscode . Position ( startLine , startColumn ) ,
61
- new vscode . Position ( startLine , endColumn ) ,
62
- ) ,
63
- ) ;
64
- builder . replace ( indel . range , newText ) ;
65
- } else {
66
- builder . replace ( indel . range , indel . newText ) ;
67
- }
68
- lineDelta +=
69
- countLines ( indel . newText ) - ( indel . range . end . line - indel . range . start . line ) ;
70
- }
71
- } ) ;
72
- if ( selections . length > 0 ) editor . selections = selections ;
73
- if ( selections . length === 1 ) {
74
- const selection = unwrapUndefinable ( selections [ 0 ] ) ;
75
- editor . revealRange ( selection , vscode . TextEditorRevealType . InCenterIfOutsideViewport ) ;
76
- }
50
+ const edit = new vscode . WorkspaceEdit ( ) ;
51
+ edit . set ( editor . document . uri , toSnippetTextEdits ( edits ) ) ;
52
+ await vscode . workspace . applyEdit ( edit ) ;
77
53
}
78
54
79
- function parseSnippet ( snip : string ) : [ string , [ number , number ] ] | undefined {
80
- const m = snip . match ( / \$ ( 0 | \{ 0 : ( [ ^ } ] * ) \} ) / ) ;
81
- if ( ! m ) return undefined ;
82
- const placeholder = m [ 2 ] ?? "" ;
83
- if ( m . index == null ) return undefined ;
84
- const range : [ number , number ] = [ m . index , placeholder . length ] ;
85
- const insert = snip . replace ( m [ 0 ] , placeholder ) ;
86
- return [ insert , range ] ;
55
+ function hasSnippet ( snip : string ) : boolean {
56
+ const m = snip . match ( / \$ \d + | \{ \d + : [ ^ } ] * \} / ) ;
57
+ return m != null ;
87
58
}
88
59
89
- function countLines ( text : string ) : number {
90
- return ( text . match ( / \n / g) || [ ] ) . length ;
60
+ function toSnippetTextEdits (
61
+ edits : vscode . TextEdit [ ] ,
62
+ ) : ( vscode . TextEdit | vscode . SnippetTextEdit ) [ ] {
63
+ return edits . map ( ( textEdit ) => {
64
+ // Note: text edits without any snippets are returned as-is instead of
65
+ // being wrapped in a SnippetTextEdit, as otherwise it would be
66
+ // treated as if it had a tab stop at the end.
67
+ if ( hasSnippet ( textEdit . newText ) ) {
68
+ return new vscode . SnippetTextEdit (
69
+ textEdit . range ,
70
+ new vscode . SnippetString ( textEdit . newText ) ,
71
+ ) ;
72
+ } else {
73
+ return textEdit ;
74
+ }
75
+ } ) ;
91
76
}
0 commit comments