@@ -7,18 +7,31 @@ import (
77 "github.com/shopware/shopware-lsp/internal/lsp"
88 "github.com/shopware/shopware-lsp/internal/lsp/protocol"
99 treesitterhelper "github.com/shopware/shopware-lsp/internal/tree_sitter_helper"
10+ "github.com/shopware/shopware-lsp/internal/twig"
11+ tree_sitter "github.com/tree-sitter/go-tree-sitter"
1012)
1113
1214type TwigCodeActionProvider struct {
15+ twigIndexer * twig.TwigIndexer
16+ projectRoot string
1317}
1418
15- func NewTwigCodeActionProvider (server * lsp.Server ) * TwigCodeActionProvider {
16- return & TwigCodeActionProvider {}
19+ func NewTwigCodeActionProvider (projectRoot string , server * lsp.Server ) * TwigCodeActionProvider {
20+ indexer , ok := server .GetIndexer ("twig.indexer" )
21+ if ! ok {
22+ return & TwigCodeActionProvider {twigIndexer : nil , projectRoot : projectRoot }
23+ }
24+ twigIndexer , ok := indexer .(* twig.TwigIndexer )
25+ if ! ok {
26+ return & TwigCodeActionProvider {twigIndexer : nil , projectRoot : projectRoot }
27+ }
28+ return & TwigCodeActionProvider {twigIndexer : twigIndexer , projectRoot : projectRoot }
1729}
1830
1931func (p * TwigCodeActionProvider ) GetCodeActionKinds () []protocol.CodeActionKind {
2032 return []protocol.CodeActionKind {
2133 protocol .CodeActionRefactorExtract ,
34+ protocol .CodeActionQuickFix ,
2235 }
2336}
2437
@@ -27,27 +40,250 @@ func (p *TwigCodeActionProvider) GetCodeActions(ctx context.Context, params *pro
2740 return nil
2841 }
2942
30- if ! strings .Contains (params .TextDocument .URI , "Resources/views/storefront" ) {
43+ var codeActions []protocol.CodeAction
44+
45+ if IsBlock ().Matches (params .Node , params .DocumentContent ) {
46+ if strings .Contains (params .TextDocument .URI , "Resources/views/storefront" ) {
47+ textValue := treesitterhelper .GetNodeText (params .Node , params .DocumentContent )
48+
49+ codeActions = append (codeActions , protocol.CodeAction {
50+ Title : "Overwrite this block in Extension" ,
51+ Kind : protocol .CodeActionRefactorExtract ,
52+ Command : & protocol.CommandAction {
53+ Title : "Overwrite Block" ,
54+ Command : "shopware.twig.extendBlock" ,
55+ Arguments : []any {params .TextDocument .URI , textValue },
56+ },
57+ })
58+ }
59+
60+ if action := p .getVersioningHashAction (params ); action != nil {
61+ codeActions = append (codeActions , * action )
62+ }
63+
64+ if action := p .getShowDiffAction (params ); action != nil {
65+ codeActions = append (codeActions , * action )
66+ }
67+ }
68+
69+ if action := p .getShowDiffActionFromComment (params ); action != nil {
70+ codeActions = append (codeActions , * action )
71+ }
72+
73+ return codeActions
74+ }
75+
76+ func (p * TwigCodeActionProvider ) getVersioningHashAction (params * protocol.CodeActionParams ) * protocol.CodeAction {
77+ if p .twigIndexer == nil {
78+ return nil
79+ }
80+
81+ if twig .IsStorefrontTemplate (params .TextDocument .URI ) {
3182 return nil
3283 }
3384
34- var codeActions []protocol.CodeAction
85+ blockNode := params .Node .Parent ()
86+ if blockNode == nil || blockNode .Kind () != "block" {
87+ return nil
88+ }
3589
36- if IsBlock ().Matches (params .Node , params .DocumentContent ) {
37- textValue := treesitterhelper .GetNodeText (params .Node , params .DocumentContent )
38-
39- codeActions = append (codeActions , protocol.CodeAction {
40- Title : "Overwrite this block in Extension" ,
41- Kind : protocol .CodeActionRefactorExtract ,
42- Command : & protocol.CommandAction {
43- Title : "Overwrite Block" ,
44- Command : "shopware.twig.extendBlock" ,
45- Arguments : []any {params .TextDocument .URI , textValue },
90+ blockName := treesitterhelper .GetNodeText (params .Node , params .DocumentContent )
91+
92+ if p .hasVersioningComment (blockNode , params .DocumentContent ) {
93+ return nil
94+ }
95+
96+ allBlockHashes , err := p .twigIndexer .GetTwigBlockHashes (blockName )
97+ if err != nil || len (allBlockHashes ) == 0 {
98+ return nil
99+ }
100+
101+ originalHash := twig .FindOriginalStorefrontHash (allBlockHashes )
102+ if originalHash == nil {
103+ return nil
104+ }
105+
106+ blockLine := int (blockNode .Range ().StartPoint .Row )
107+ versionComment := twig .FormatVersionComment (originalHash .Hash , twig .DetectShopwareVersion (p .projectRoot ))
108+
109+ edit := & protocol.WorkspaceEdit {
110+ Changes : map [string ][]protocol.TextEdit {
111+ params .TextDocument .URI : {
112+ {
113+ Range : protocol.Range {
114+ Start : protocol.Position {Line : blockLine , Character : 0 },
115+ End : protocol.Position {Line : blockLine , Character : 0 },
116+ },
117+ NewText : versionComment ,
118+ },
46119 },
47- })
120+ },
48121 }
49122
50- return codeActions
123+ return & protocol.CodeAction {
124+ Title : "Add twig versioning hash" ,
125+ Kind : protocol .CodeActionQuickFix ,
126+ Edit : edit ,
127+ }
128+ }
129+
130+ func (p * TwigCodeActionProvider ) getShowDiffAction (params * protocol.CodeActionParams ) * protocol.CodeAction {
131+ if p .twigIndexer == nil {
132+ return nil
133+ }
134+
135+ if twig .IsStorefrontTemplate (params .TextDocument .URI ) {
136+ return nil
137+ }
138+
139+ blockNode := params .Node .Parent ()
140+ if blockNode == nil || blockNode .Kind () != "block" {
141+ return nil
142+ }
143+
144+ blockName := treesitterhelper .GetNodeText (params .Node , params .DocumentContent )
145+
146+ rootNode := params .Node
147+ for rootNode .Parent () != nil {
148+ rootNode = rootNode .Parent ()
149+ }
150+
151+ twigFile , err := twig .ParseTwig (params .TextDocument .URI , rootNode , params .DocumentContent )
152+ if err != nil {
153+ return nil
154+ }
155+
156+ block , exists := twigFile .Blocks [blockName ]
157+ if ! exists || block .VersionComment == nil {
158+ return nil
159+ }
160+
161+ allBlockHashes , err := p .twigIndexer .GetTwigBlockHashes (blockName )
162+ if err != nil || len (allBlockHashes ) == 0 {
163+ return nil
164+ }
165+
166+ originalHash := twig .FindOriginalStorefrontHash (allBlockHashes )
167+ if originalHash == nil {
168+ return nil
169+ }
170+
171+ if block .VersionComment .Hash == originalHash .Hash {
172+ return nil
173+ }
174+
175+ return & protocol.CodeAction {
176+ Title : "Show block difference" ,
177+ Kind : protocol .CodeActionQuickFix ,
178+ Command : & protocol.CommandAction {
179+ Title : "Show Block Difference" ,
180+ Command : "shopware.twig.showBlockDiff" ,
181+ Arguments : []any {params .TextDocument .URI , blockName },
182+ },
183+ }
184+ }
185+
186+ func (p * TwigCodeActionProvider ) getShowDiffActionFromComment (params * protocol.CodeActionParams ) * protocol.CodeAction {
187+ if p .twigIndexer == nil {
188+ return nil
189+ }
190+
191+ if twig .IsStorefrontTemplate (params .TextDocument .URI ) {
192+ return nil
193+ }
194+
195+ if params .Node .Kind () != "comment" {
196+ return nil
197+ }
198+
199+ commentText := string (params .Node .Utf8Text (params .DocumentContent ))
200+ if ! strings .Contains (commentText , twig .VersionCommentPrefix ) {
201+ return nil
202+ }
203+
204+ versionComment := twig .ParseVersionComment (commentText , int (params .Node .Range ().StartPoint .Row )+ 1 )
205+ if versionComment == nil {
206+ return nil
207+ }
208+
209+ commentLine := int (params .Node .Range ().StartPoint .Row ) + 1
210+
211+ rootNode := params .Node
212+ for rootNode .Parent () != nil {
213+ rootNode = rootNode .Parent ()
214+ }
215+
216+ twigFile , err := twig .ParseTwig (params .TextDocument .URI , rootNode , params .DocumentContent )
217+ if err != nil {
218+ return nil
219+ }
220+
221+ var blockName string
222+ for _ , block := range twigFile .Blocks {
223+ if block .VersionComment != nil && block .VersionComment .Line == commentLine {
224+ blockName = block .Name
225+ break
226+ }
227+ }
228+
229+ if blockName == "" {
230+ return nil
231+ }
232+
233+ allBlockHashes , err := p .twigIndexer .GetTwigBlockHashes (blockName )
234+ if err != nil || len (allBlockHashes ) == 0 {
235+ return nil
236+ }
237+
238+ originalHash := twig .FindOriginalStorefrontHash (allBlockHashes )
239+ if originalHash == nil {
240+ return nil
241+ }
242+
243+ if versionComment .Hash == originalHash .Hash {
244+ return nil
245+ }
246+
247+ return & protocol.CodeAction {
248+ Title : "Show block difference" ,
249+ Kind : protocol .CodeActionQuickFix ,
250+ Command : & protocol.CommandAction {
251+ Title : "Show Block Difference" ,
252+ Command : "shopware.twig.showBlockDiff" ,
253+ Arguments : []any {params .TextDocument .URI , blockName },
254+ },
255+ }
256+ }
257+
258+ func (p * TwigCodeActionProvider ) hasVersioningComment (blockNode * tree_sitter.Node , content []byte ) bool {
259+ parent := blockNode .Parent ()
260+ if parent == nil {
261+ return false
262+ }
263+
264+ blockStartLine := blockNode .Range ().StartPoint .Row
265+
266+ for i := 0 ; i < int (parent .NamedChildCount ()); i ++ {
267+ child := parent .NamedChild (uint (i ))
268+
269+ if child .Range ().StartPoint .Row == blockNode .Range ().StartPoint .Row &&
270+ child .Range ().StartPoint .Column == blockNode .Range ().StartPoint .Column {
271+ if i > 0 {
272+ prevSibling := parent .NamedChild (uint (i - 1 ))
273+ if prevSibling .Kind () == "comment" {
274+ commentEndLine := prevSibling .Range ().EndPoint .Row
275+ if blockStartLine - commentEndLine <= 1 {
276+ commentText := string (prevSibling .Utf8Text (content ))
277+ if strings .Contains (commentText , twig .VersionCommentPrefix ) {
278+ return true
279+ }
280+ }
281+ }
282+ }
283+ break
284+ }
285+ }
286+ return false
51287}
52288
53289func IsBlock () treesitterhelper.Pattern {
0 commit comments