1
+ import { CodeAction , CodeActionKind , CodeActionParams , Position , Range , TextEdit } from 'vscode-languageserver' ;
2
+ import { TextDocument } from 'vscode-languageserver-textdocument' ;
3
+ import { documents , parser , prettyKeywords } from '.' ;
4
+ import Cache from '../../../../language/models/cache' ;
5
+ import { getLinterCodeActions } from './linter/codeActions' ;
6
+ import { createExtract , caseInsensitiveReplaceAll } from './language' ;
7
+
8
+ export default async function genericCodeActionsProvider ( params : CodeActionParams ) : Promise < CodeAction [ ] | undefined > {
9
+ const uri = params . textDocument . uri ;
10
+ const range = params . range ;
11
+ const document = documents . get ( uri ) ;
12
+
13
+ let actions : CodeAction [ ] = [ ] ;
14
+
15
+ if ( document ) {
16
+ const docs = await parser . getDocs ( document . uri ) ;
17
+ if ( docs ) {
18
+ const isFree = ( document . getText ( Range . create ( 0 , 0 , 0 , 6 ) ) . toUpperCase ( ) === `**FREE` ) ;
19
+ if ( isFree ) {
20
+ const subroutineOption = getSubroutineActions ( document , docs , range ) ;
21
+ if ( subroutineOption ) {
22
+ return [ subroutineOption ] ;
23
+ }
24
+
25
+ const extractOption = getExtractProcedureAction ( document , docs , range ) ;
26
+ if ( extractOption ) {
27
+ return [ extractOption ] ;
28
+ }
29
+
30
+ const linterActions = await getLinterCodeActions ( docs , document , range ) ;
31
+ if ( linterActions ) {
32
+ actions = actions . concat ( linterActions ) ;
33
+ }
34
+ }
35
+
36
+ // const testCaseOption = getTestCaseAction(document, docs, range);
37
+ // if (testCaseOption) {
38
+ // actions.push(testCaseOption);
39
+ // }
40
+ }
41
+ }
42
+
43
+ return actions ;
44
+ }
45
+
46
+ export function getTestCaseAction ( document : TextDocument , docs : Cache , range : Range ) : CodeAction | undefined {
47
+ const currentProcedure = docs . procedures . find ( sub => range . start . line >= sub . position . range . line && sub . range . start && sub . range . end ) ;
48
+ if ( currentProcedure ) {
49
+
50
+ const refactorAction = CodeAction . create ( `Create IBM i test case` , CodeActionKind . RefactorExtract ) ;
51
+
52
+ refactorAction . edit = {
53
+ changes : {
54
+ [ 'mynewtest.rpgle' ] : [
55
+ TextEdit . insert (
56
+ Position . create ( 0 , 0 ) , // Insert at the start of the new test case file
57
+ [
58
+ `**free` ,
59
+ `` ,
60
+ `dcl-proc test_${ currentProcedure . name . toLowerCase ( ) } export;` ,
61
+ `` ,
62
+ `end-proc;`
63
+ ] . join ( `\n` )
64
+
65
+ )
66
+ ]
67
+ } ,
68
+ } ;
69
+
70
+ return refactorAction ;
71
+ }
72
+ }
73
+
74
+ export function getSubroutineActions ( document : TextDocument , docs : Cache , range : Range ) : CodeAction | undefined {
75
+ if ( range . start . line === range . end . line ) {
76
+ const currentGlobalSubroutine = docs . subroutines . find ( sub => sub . position . range . line === range . start . line && sub . range . start && sub . range . end ) ;
77
+
78
+ if ( currentGlobalSubroutine ) {
79
+ const subroutineRange = Range . create (
80
+ Position . create ( currentGlobalSubroutine . range . start ! , 0 ) ,
81
+ Position . create ( currentGlobalSubroutine . range . end ! , 1000 )
82
+ ) ;
83
+
84
+ const bodyRange = Range . create (
85
+ Position . create ( currentGlobalSubroutine . range . start ! + 1 , 0 ) ,
86
+ Position . create ( currentGlobalSubroutine . range . end ! - 1 , 0 )
87
+ ) ;
88
+
89
+ // First, let's create the extract data
90
+ const extracted = createExtract ( document , bodyRange , docs ) ;
91
+
92
+ // Create the new procedure body
93
+ const newProcedure = [
94
+ `Dcl-Proc ${ currentGlobalSubroutine . name } ;` ,
95
+ ` Dcl-Pi *N;` ,
96
+ ...extracted . references . map ( ( ref , i ) => ` ${ extracted . newParamNames [ i ] } ${ ref . dec . type === `struct` ? `LikeDS` : `Like` } (${ ref . dec . name } );` ) ,
97
+ ` End-Pi;` ,
98
+ `` ,
99
+ caseInsensitiveReplaceAll ( extracted . newBody , `leavesr` , `return` ) ,
100
+ `End-Proc;`
101
+ ] . join ( `\n` )
102
+
103
+ // Then update the references that invokes this subroutine
104
+ const referenceUpdates : TextEdit [ ] = currentGlobalSubroutine . references . map ( ref => {
105
+ const lineNumber = document . positionAt ( ref . offset . start ) . line ;
106
+ // If this reference is outside of the subroutine
107
+ if ( lineNumber < currentGlobalSubroutine . range . start ! || lineNumber > currentGlobalSubroutine . range . end ! ) {
108
+ return TextEdit . replace (
109
+ Range . create (
110
+ // - 5 `EXSR `
111
+ document . positionAt ( ref . offset . start - 5 ) ,
112
+ document . positionAt ( ref . offset . end )
113
+ ) ,
114
+ currentGlobalSubroutine . name + `(${ extracted . references . map ( r => r . dec . name ) . join ( `:` ) } )`
115
+ ) ;
116
+ }
117
+ } ) . map ( x => x ) as TextEdit [ ] ;
118
+
119
+ const refactorAction = CodeAction . create ( `Convert to procedure` , CodeActionKind . RefactorExtract ) ;
120
+ refactorAction . edit = {
121
+ changes : {
122
+ [ document . uri ] : [
123
+ ...referenceUpdates ,
124
+ TextEdit . replace ( subroutineRange , newProcedure )
125
+ ]
126
+ } ,
127
+ } ;
128
+
129
+ return refactorAction ;
130
+ }
131
+ }
132
+ }
133
+
134
+ export function getExtractProcedureAction ( document : TextDocument , docs : Cache , range : Range ) : CodeAction | undefined {
135
+ if ( range . end . line > range . start . line ) {
136
+ const lastLine = document . offsetAt ( { line : document . lineCount , character : 0 } ) ;
137
+
138
+ const extracted = createExtract ( document , range , docs ) ;
139
+
140
+ const newProcedure = [
141
+ `Dcl-Proc NewProcedure;` ,
142
+ ` Dcl-Pi *N;` ,
143
+ ...extracted . references . map ( ( ref , i ) => ` ${ extracted . newParamNames [ i ] } ${ ref . dec . type === `struct` ? `LikeDS` : `Like` } (${ ref . dec . name } );` ) ,
144
+ ` End-Pi;` ,
145
+ `` ,
146
+ extracted . newBody ,
147
+ `End-Proc;`
148
+ ] . join ( `\n` )
149
+
150
+ const newAction = CodeAction . create ( `Extract to new procedure` , CodeActionKind . RefactorExtract ) ;
151
+
152
+ // First do the exit
153
+ newAction . edit = {
154
+ changes : {
155
+ [ document . uri ] : [
156
+ TextEdit . replace ( extracted . range , `NewProcedure(${ extracted . references . map ( r => r . dec . name ) . join ( `:` ) } );` ) ,
157
+ TextEdit . insert ( document . positionAt ( lastLine ) , `\n\n` + newProcedure )
158
+ ]
159
+ } ,
160
+ } ;
161
+
162
+ // Then format the document
163
+ newAction . command = {
164
+ command : `editor.action.formatDocument` ,
165
+ title : `Format`
166
+ } ;
167
+
168
+ return newAction ;
169
+ }
170
+ }
0 commit comments