@@ -3,23 +3,29 @@ 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 ) ,
20
- `bad ws edit: snippet received with multiple edits: ${ JSON . stringify (
21
- edit ,
22
- ) } `,
27
+ ! ( indel instanceof vscode . SnippetTextEdit ) ,
28
+ `bad ws edit: snippet received with multiple edits: ${ JSON . stringify ( edit ) } `
23
29
) ;
24
30
builder . replace ( indel . range , indel . newText ) ;
25
31
}
@@ -39,53 +45,30 @@ async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undef
39
45
}
40
46
41
47
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
- }
48
+ const edit = new vscode . WorkspaceEdit ( ) ;
49
+ edit . set ( editor . document . uri , toSnippetTextEdits ( edits ) ) ;
50
+ await vscode . workspace . applyEdit ( edit ) ;
77
51
}
78
52
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 ] ;
53
+ function hasSnippet ( snip : string ) : boolean {
54
+ const m = snip . match ( / \$ \d + | \{ \d + : [ ^ } ] * \} / ) ;
55
+ return m != null ;
87
56
}
88
57
89
- function countLines ( text : string ) : number {
90
- return ( text . match ( / \n / g) || [ ] ) . length ;
58
+ function toSnippetTextEdits (
59
+ edits : vscode . TextEdit [ ]
60
+ ) : ( vscode . TextEdit | vscode . SnippetTextEdit ) [ ] {
61
+ return edits . map ( ( textEdit ) => {
62
+ // Note: text edits without any snippets are returned as-is instead of
63
+ // being wrapped in a SnippetTextEdit, as otherwise it would be
64
+ // treated as if it had a tab stop at the end.
65
+ if ( hasSnippet ( textEdit . newText ) ) {
66
+ return new vscode . SnippetTextEdit (
67
+ textEdit . range ,
68
+ new vscode . SnippetString ( textEdit . newText )
69
+ ) ;
70
+ } else {
71
+ return textEdit ;
72
+ }
73
+ } ) ;
91
74
}
0 commit comments