1
1
import { BaseRenameProvider } from '../BaseRenameProvider' ;
2
- import { DocumentManager } from '../../documents' ;
2
+ import { AugmentedLiquidSourceCode , DocumentManager , isLiquidSourceCode } from '../../documents' ;
3
3
import {
4
4
LiquidHtmlNode ,
5
5
LiquidTagFor ,
6
6
LiquidTagTablerow ,
7
7
NamedTags ,
8
8
NodeTypes ,
9
9
Position ,
10
+ RenderMarkup ,
10
11
AssignMarkup ,
11
12
TextNode ,
12
13
LiquidVariableLookup ,
13
14
ForMarkup ,
14
15
} from '@shopify/liquid-html-parser' ;
15
- import { Range } from 'vscode-languageserver' ;
16
+ import { Connection , Range } from 'vscode-languageserver' ;
16
17
import {
18
+ ApplyWorkspaceEditRequest ,
17
19
PrepareRenameParams ,
18
20
PrepareRenameResult ,
19
21
RenameParams ,
20
22
TextDocumentEdit ,
21
23
TextEdit ,
22
24
WorkspaceEdit ,
23
25
} from 'vscode-languageserver-protocol' ;
24
- import { visit } from '@shopify/theme-check-common' ;
25
26
import { TextDocument } from 'vscode-languageserver-textdocument' ;
26
- import { JSONNode } from '@shopify/theme-check-common' ;
27
+ import { JSONNode , SourceCodeType , visit } from '@shopify/theme-check-common' ;
28
+ import { snippetName } from '../../utils/uri' ;
29
+ import { ClientCapabilities } from '../../ClientCapabilities' ;
27
30
28
31
export class LiquidVariableRenameProvider implements BaseRenameProvider {
29
- constructor ( private documentManager : DocumentManager ) { }
32
+ constructor (
33
+ private connection : Connection ,
34
+ private clientCapabilities : ClientCapabilities ,
35
+ private documentManager : DocumentManager ,
36
+ private findThemeRootURI : ( uri : string ) => Promise < string > ,
37
+ ) { }
30
38
31
39
async prepare (
32
40
node : LiquidHtmlNode ,
@@ -60,6 +68,7 @@ export class LiquidVariableRenameProvider implements BaseRenameProvider {
60
68
params : RenameParams ,
61
69
) : Promise < null | WorkspaceEdit > {
62
70
const document = this . documentManager . get ( params . textDocument . uri ) ;
71
+ const rootUri = await this . findThemeRootURI ( params . textDocument . uri ) ;
63
72
const textDocument = document ?. textDocument ;
64
73
65
74
if ( ! textDocument || ! node || ! ancestors ) return null ;
@@ -70,17 +79,29 @@ export class LiquidVariableRenameProvider implements BaseRenameProvider {
70
79
const scope = variableNameBlockScope ( oldName , ancestors ) ;
71
80
const replaceRange = textReplaceRange ( oldName , textDocument , scope ) ;
72
81
82
+ let liquidDocParamUpdated = false ;
83
+
73
84
const ranges : Range [ ] = visit ( document . ast , {
74
85
VariableLookup : replaceRange ,
75
86
AssignMarkup : replaceRange ,
76
87
ForMarkup : replaceRange ,
77
88
TextNode : ( node : LiquidHtmlNode , ancestors : ( LiquidHtmlNode | JSONNode ) [ ] ) => {
78
89
if ( ancestors . at ( - 1 ) ?. type !== NodeTypes . LiquidDocParamNode ) return ;
79
90
91
+ liquidDocParamUpdated = true ;
92
+
80
93
return replaceRange ( node , ancestors ) ;
81
94
} ,
82
95
} ) ;
83
96
97
+ if ( this . clientCapabilities . hasApplyEditSupport && liquidDocParamUpdated ) {
98
+ const themeFiles = this . documentManager . theme ( rootUri , true ) ;
99
+ const liquidSourceCodes = themeFiles . filter ( isLiquidSourceCode ) ;
100
+ const name = snippetName ( params . textDocument . uri ) ;
101
+
102
+ updateRenderTags ( this . connection , liquidSourceCodes , name , oldName , params . newName ) ;
103
+ }
104
+
84
105
const textDocumentEdit = TextDocumentEdit . create (
85
106
{ uri : textDocument . uri , version : textDocument . version } ,
86
107
ranges . map ( ( range ) => TextEdit . replace ( range , params . newName ) ) ,
@@ -184,3 +205,67 @@ function textReplaceRange(
184
205
) ;
185
206
} ;
186
207
}
208
+
209
+ async function updateRenderTags (
210
+ connection : Connection ,
211
+ liquidSourceCodes : AugmentedLiquidSourceCode [ ] ,
212
+ snippetName : string ,
213
+ oldParamName : string ,
214
+ newParamName : string ,
215
+ ) {
216
+ const editLabel = `Rename snippet parameter '${ oldParamName } ' to '${ newParamName } '` ;
217
+ const annotationId = 'renameSnippetParameter' ;
218
+ const workspaceEdit : WorkspaceEdit = {
219
+ documentChanges : [ ] ,
220
+ changeAnnotations : {
221
+ [ annotationId ] : {
222
+ label : editLabel ,
223
+ needsConfirmation : false ,
224
+ } ,
225
+ } ,
226
+ } ;
227
+
228
+ for ( const sourceCode of liquidSourceCodes ) {
229
+ if ( sourceCode . ast instanceof Error ) continue ;
230
+ const textDocument = sourceCode . textDocument ;
231
+ const edits : TextEdit [ ] = visit < SourceCodeType . LiquidHtml , TextEdit > ( sourceCode . ast , {
232
+ RenderMarkup ( node : RenderMarkup ) {
233
+ if ( node . snippet . type !== NodeTypes . String || node . snippet . value !== snippetName ) {
234
+ return ;
235
+ }
236
+
237
+ const renamedNameParamNode = node . args . find ( ( arg ) => arg . name === oldParamName ) ;
238
+
239
+ if ( renamedNameParamNode ) {
240
+ return {
241
+ newText : `${ newParamName } : ` ,
242
+ range : Range . create (
243
+ textDocument . positionAt ( renamedNameParamNode . position . start ) ,
244
+ textDocument . positionAt ( renamedNameParamNode . value . position . start ) ,
245
+ ) ,
246
+ } ;
247
+ }
248
+ } ,
249
+ } ) ;
250
+
251
+ if ( edits . length === 0 ) continue ;
252
+ workspaceEdit . documentChanges ! . push ( {
253
+ textDocument : {
254
+ uri : textDocument . uri ,
255
+ version : sourceCode . version ?? null /* null means file from disk in this API */ ,
256
+ } ,
257
+ annotationId,
258
+ edits,
259
+ } ) ;
260
+ }
261
+
262
+ if ( workspaceEdit . documentChanges ! . length === 0 ) {
263
+ console . error ( 'Nothing to do!' ) ;
264
+ return ;
265
+ }
266
+
267
+ await connection . sendRequest ( ApplyWorkspaceEditRequest . type , {
268
+ label : editLabel ,
269
+ edit : workspaceEdit ,
270
+ } ) ;
271
+ }
0 commit comments