@@ -53,36 +53,57 @@ export function selectAndApplySourceChange(ctx: Ctx): Cmd {
5353 } ;
5454}
5555
56- export function applySnippetWorkspaceEdit ( _ctx : Ctx ) : Cmd {
56+ export function applySnippetWorkspaceEditCommand ( _ctx : Ctx ) : Cmd {
5757 return async ( edit : vscode . WorkspaceEdit ) => {
58- assert ( edit . entries ( ) . length === 1 , `bad ws edit: ${ JSON . stringify ( edit ) } ` ) ;
59- const [ uri , edits ] = edit . entries ( ) [ 0 ] ;
58+ await applySnippetWorkspaceEdit ( edit ) ;
59+ } ;
60+ }
61+
62+ export async function applySnippetWorkspaceEdit ( edit : vscode . WorkspaceEdit ) {
63+ assert ( edit . entries ( ) . length === 1 , `bad ws edit: ${ JSON . stringify ( edit ) } ` ) ;
64+ const [ uri , edits ] = edit . entries ( ) [ 0 ] ;
6065
61- const editor = vscode . window . visibleTextEditors . find ( ( it ) => it . document . uri . toString ( ) === uri . toString ( ) ) ;
62- if ( ! editor ) return ;
66+ const editor = vscode . window . visibleTextEditors . find ( ( it ) => it . document . uri . toString ( ) === uri . toString ( ) ) ;
67+ if ( ! editor ) return ;
6368
64- let editWithSnippet : vscode . TextEdit | undefined = undefined ;
65- let lineDelta = 0 ;
66- await editor . edit ( ( builder ) => {
67- for ( const indel of edits ) {
68- const isSnippet = indel . newText . indexOf ( '$0' ) !== - 1 || indel . newText . indexOf ( '${' ) !== - 1 ;
69- if ( isSnippet ) {
70- editWithSnippet = indel ;
71- } else {
72- if ( ! editWithSnippet ) {
73- lineDelta = ( indel . newText . match ( / \n / g) || [ ] ) . length - ( indel . range . end . line - indel . range . start . line ) ;
74- }
75- builder . replace ( indel . range , indel . newText ) ;
76- }
69+ let selection : vscode . Selection | undefined = undefined ;
70+ let lineDelta = 0 ;
71+ await editor . edit ( ( builder ) => {
72+ for ( const indel of edits ) {
73+ const parsed = parseSnippet ( indel . newText ) ;
74+ if ( parsed ) {
75+ const [ newText , [ placeholderStart , placeholderLength ] ] = parsed ;
76+ const prefix = newText . substr ( 0 , placeholderStart ) ;
77+ const lastNewline = prefix . lastIndexOf ( '\n' ) ;
78+
79+ const startLine = indel . range . start . line + lineDelta + countLines ( prefix ) ;
80+ const startColumn = lastNewline === - 1 ?
81+ indel . range . start . character + placeholderStart
82+ : prefix . length - lastNewline - 1 ;
83+ const endColumn = startColumn + placeholderLength ;
84+ selection = new vscode . Selection (
85+ new vscode . Position ( startLine , startColumn ) ,
86+ new vscode . Position ( startLine , endColumn ) ,
87+ ) ;
88+ builder . replace ( indel . range , newText ) ;
89+ } else {
90+ lineDelta = countLines ( indel . newText ) - ( indel . range . end . line - indel . range . start . line ) ;
91+ builder . replace ( indel . range , indel . newText ) ;
7792 }
78- } ) ;
79- if ( editWithSnippet ) {
80- const snip = editWithSnippet as vscode . TextEdit ;
81- const range = snip . range . with (
82- snip . range . start . with ( snip . range . start . line + lineDelta ) ,
83- snip . range . end . with ( snip . range . end . line + lineDelta ) ,
84- ) ;
85- await editor . insertSnippet ( new vscode . SnippetString ( snip . newText ) , range ) ;
8693 }
87- } ;
94+ } ) ;
95+ if ( selection ) editor . selection = selection ;
96+ }
97+
98+ function parseSnippet ( snip : string ) : [ string , [ number , number ] ] | undefined {
99+ const m = snip . match ( / \$ ( 0 | \{ 0 : ( [ ^ } ] * ) \} ) / ) ;
100+ if ( ! m ) return undefined ;
101+ const placeholder = m [ 2 ] ?? "" ;
102+ const range : [ number , number ] = [ m . index ! ! , placeholder . length ] ;
103+ const insert = snip . replace ( m [ 0 ] , placeholder ) ;
104+ return [ insert , range ] ;
105+ }
106+
107+ function countLines ( text : string ) : number {
108+ return ( text . match ( / \n / g) || [ ] ) . length ;
88109}
0 commit comments