1
- import { CodeAction , CodeActionKind , CodeActionParams , CreateFile , Position , Range , TextDocumentEdit , TextEdit , WorkspaceFolder } from 'vscode-languageserver' ;
1
+ import { CodeAction , CodeActionKind , CodeActionParams , Position , Range , TextEdit } from 'vscode-languageserver' ;
2
2
import { TextDocument } from 'vscode-languageserver-textdocument' ;
3
- import { documents , parser , prettyKeywords } from '.' ;
4
- import Cache , { RpgleTypeDetail , RpgleVariableType } from '../../../../language/models/cache' ;
3
+ import { documents , parser } from '.' ;
4
+ import Cache from '../../../../language/models/cache' ;
5
5
import { getLinterCodeActions } from './linter/codeActions' ;
6
6
import { createExtract , caseInsensitiveReplaceAll } from './language' ;
7
- import { Keywords } from '../../../../language/parserTypes' ;
8
- import path = require( 'path' ) ;
9
- import { getWorkspaceFolder } from '../connection' ;
10
- import Declaration from '../../../../language/models/declaration' ;
11
-
12
- interface TestCaseSpec {
13
- prototype : string [ ] ;
14
- testCase : string [ ] ;
15
- includes : string [ ] ;
16
- }
17
7
18
8
export default async function genericCodeActionsProvider ( params : CodeActionParams ) : Promise < CodeAction [ ] | undefined > {
19
9
const uri = params . textDocument . uri ;
@@ -43,11 +33,6 @@ export default async function genericCodeActionsProvider(params: CodeActionParam
43
33
}
44
34
}
45
35
46
- const testActions = await getTestActions ( document , docs , range ) ;
47
- if ( testActions ) {
48
- actions . push ( ...testActions ) ;
49
- }
50
-
51
36
const monitorAction = surroundWithMonitorAction ( isFree , document , docs , range ) ;
52
37
if ( monitorAction ) {
53
38
actions . push ( monitorAction ) ;
@@ -58,284 +43,6 @@ export default async function genericCodeActionsProvider(params: CodeActionParam
58
43
return actions ;
59
44
}
60
45
61
- export async function getTestActions ( document : TextDocument , docs : Cache , range : Range ) : Promise < CodeAction [ ] | undefined > {
62
- const codeActions : CodeAction [ ] = [ ] ;
63
-
64
- const exportProcedures = docs . procedures . filter ( proc => proc . keyword [ `EXPORT` ] ) ;
65
- if ( exportProcedures . length > 0 ) {
66
- const workspaceFolder = await getWorkspaceFolder ( document . uri ) ; // TODO: Can workspace folder not be a requirement?
67
- if ( workspaceFolder ) {
68
- // Build new test file uri
69
- const parsedPath = path . parse ( document . uri ) ;
70
- const fileName = parsedPath . base ;
71
- const testFileName = `${ parsedPath . name } .test${ parsedPath . ext } ` ;
72
- const testFileUri = workspaceFolder ?
73
- `${ workspaceFolder . uri } /qtestsrc/${ testFileName } ` :
74
- `${ parsedPath . dir } /${ testFileName } ` ;
75
-
76
- // Test case generation
77
- const currentProcedure = exportProcedures . find ( sub => sub . range . start && sub . range . end && range . start . line >= sub . range . start && range . end . line <= sub . range . end ) ;
78
- if ( currentProcedure ) {
79
- const testCaseSpec = await getTestCaseSpec ( docs , currentProcedure , workspaceFolder ) ;
80
- const newTestSuite = generateTestSuite ( [ testCaseSpec ] ) ;
81
- const testCaseAction = CodeAction . create ( `Generate test case for '${ currentProcedure . name } '` , CodeActionKind . RefactorExtract ) ;
82
- testCaseAction . edit = {
83
- documentChanges : [
84
- CreateFile . create ( testFileUri , { ignoreIfExists : true } ) ,
85
- TextDocumentEdit . create ( { uri : testFileUri , version : null } , [ TextEdit . insert ( Position . create ( 0 , 0 ) , newTestSuite . join ( `\n` ) ) ] )
86
- ]
87
- } ;
88
- codeActions . push ( testCaseAction ) ;
89
- }
90
-
91
- // Test suite generation
92
- const newTestCases = await Promise . all ( exportProcedures . map ( async proc => await getTestCaseSpec ( docs , proc , workspaceFolder ) ) ) ;
93
- const newTestSuite = generateTestSuite ( newTestCases ) ;
94
- const testSuiteAction = CodeAction . create ( `Generate test suite for '${ fileName } '` , CodeActionKind . RefactorExtract ) ;
95
- testSuiteAction . edit = {
96
- documentChanges : [
97
- CreateFile . create ( testFileUri , { ignoreIfExists : true } ) ,
98
- TextDocumentEdit . create ( { uri : testFileUri , version : null } , [ TextEdit . insert ( Position . create ( 0 , 0 ) , newTestSuite . join ( `\n` ) ) ] )
99
- ]
100
- } ;
101
- codeActions . push ( testSuiteAction ) ;
102
- }
103
- }
104
-
105
- return codeActions ;
106
- }
107
-
108
-
109
- function generateTestSuite ( testCaseSpecs : TestCaseSpec [ ] ) {
110
- const prototypes = testCaseSpecs . map ( tc => tc . prototype . length > 0 ? [ `` , ...tc . prototype ] : tc . prototype ) . flat ( ) ;
111
- const testCases = testCaseSpecs . map ( tc => tc . testCase . length > 0 ? [ `` , ...tc . testCase ] : tc . testCase ) . flat ( ) ;
112
- const allIncludes = testCaseSpecs . map ( tc => tc . includes ) . flat ( ) ;
113
- const uniqueIncludes = [ ...new Set ( allIncludes ) ] ;
114
-
115
- return [
116
- `**free` ,
117
- `` ,
118
- `ctl-opt nomain;` ,
119
- ...prototypes ,
120
- `` ,
121
- `/include qinclude,TESTCASE` ,
122
- ...uniqueIncludes ,
123
- ...testCases
124
- ]
125
- }
126
-
127
- async function getTestCaseSpec ( docs : Cache , procedure : Declaration , workspaceFolder : WorkspaceFolder ) : Promise < TestCaseSpec > {
128
- // Get procedure prototype
129
- const prototype = await getPrototype ( procedure ) ;
130
-
131
- // Get inputs
132
- const inputDecs : string [ ] = [ ] ;
133
- const inputInits : string [ ] = [ ] ;
134
- const inputIncludes : string [ ] = [ ] ;
135
- for ( const subItem of procedure . subItems ) {
136
- const subItemType = docs . resolveType ( subItem ) ;
137
-
138
- const subItemDec = getDeclaration ( subItemType , `${ subItem . name } ` ) ;
139
- inputDecs . push ( ...subItemDec ) ;
140
-
141
- const subItemInits = getInitializations ( docs , subItemType , `${ subItem . name } ` ) ;
142
- inputInits . push ( ...subItemInits ) ;
143
-
144
- const subItemIncludes = getIncludes ( subItemType , workspaceFolder ) ;
145
- inputIncludes . push ( ...subItemIncludes ) ;
146
- }
147
-
148
- // Get return
149
- const resolvedType = docs . resolveType ( procedure ) ;
150
- const actualDec = getDeclaration ( resolvedType , 'actual' ) ;
151
- const expectedDec = getDeclaration ( resolvedType , 'expected' ) ;
152
- const expectedInits = getInitializations ( docs , resolvedType , 'expected' ) ;
153
- const returnIncludes = getIncludes ( resolvedType , workspaceFolder ) ;
154
-
155
- // Get unique includes
156
- const includes = [ ...new Set ( [ ...inputIncludes , ...returnIncludes ] ) ] ;
157
-
158
- // Get assertions
159
- const assertions = getAssertions ( docs , resolvedType , 'expected' , 'actual' ) ;
160
-
161
- const testCase = [
162
- `dcl-proc test_${ procedure . name } export;` ,
163
- ` dcl-pi *n extproc(*dclcase) end-pi;` ,
164
- `` ,
165
- ...inputDecs . map ( dec => ` ${ dec } ` ) ,
166
- ...actualDec . map ( dec => ` ${ dec } ` ) ,
167
- ...expectedDec . map ( dec => ` ${ dec } ` ) ,
168
- `` ,
169
- ` // Input` ,
170
- ...inputInits . map ( init => ` ${ init } ` ) ,
171
- `` ,
172
- ` // Actual results` ,
173
- ` actual = ${ procedure . name } (${ procedure . subItems . map ( s => s . name ) . join ( ` : ` ) } );` ,
174
- `` ,
175
- ` // Expected results` ,
176
- ...expectedInits . map ( init => ` ${ init } ` ) ,
177
- `` ,
178
- ` // Assertions` ,
179
- ...assertions . map ( assert => ` ${ assert } ` ) ,
180
- `end-proc;`
181
- ] ;
182
-
183
- return {
184
- prototype,
185
- testCase,
186
- includes
187
- } ;
188
- }
189
-
190
- function getDeclaration ( detail : RpgleTypeDetail , name : string ) : string [ ] {
191
- const declarations : string [ ] = [ ] ;
192
-
193
- if ( detail ) {
194
- if ( detail . type ) {
195
- declarations . push ( `dcl-s ${ name } ${ detail . type . name } ${ detail . type . value ? `(${ detail . type . value } )` : `` } ;` ) ;
196
- } else if ( detail . reference ) {
197
- declarations . push ( `dcl-ds ${ name } likeDs(${ detail . reference . name } );` ) ;
198
- }
199
- }
200
-
201
- return declarations ;
202
- }
203
-
204
- function getInitializations ( docs : Cache , detail : RpgleTypeDetail , name : string ) : string [ ] {
205
- const inits : string [ ] = [ ] ;
206
-
207
- if ( detail ) {
208
- if ( detail . type ) {
209
- const defaultValue = getDefaultValue ( detail . type . name ) ;
210
- inits . push ( `${ name } = ${ defaultValue } ;` ) ;
211
- } else if ( detail . reference ) {
212
- for ( const subItem of detail . reference . subItems ) {
213
- const subItemType = docs . resolveType ( subItem ) ;
214
- const subItemInits = subItemType ?
215
- getInitializations ( docs , subItemType , `${ name } .${ subItem . name } ` ) : [ ] ;
216
- inits . push ( ...subItemInits ) ;
217
- }
218
- }
219
- }
220
-
221
- return inits ;
222
- }
223
-
224
- async function getPrototype ( procedure : Declaration ) : Promise < string [ ] > {
225
- for ( const reference of procedure . references ) {
226
- const docs = await parser . getDocs ( reference . uri ) ;
227
- if ( docs ) {
228
- const prototype = docs . procedures . some ( proc => proc . name === procedure . name && proc . keyword [ 'EXTPROC' ] )
229
- if ( prototype ) {
230
- return [ ] ;
231
- }
232
- }
233
- }
234
-
235
- return [
236
- `dcl-pr ${ procedure . name } ${ prettyKeywords ( procedure . keyword , true ) } extproc('${ procedure . name . toLocaleUpperCase ( ) } ');` ,
237
- ...procedure . subItems . map ( s => ` ${ s . name } ${ prettyKeywords ( s . keyword , true ) } ;` ) ,
238
- `end-pr;`
239
- ] ;
240
- }
241
-
242
- function getIncludes ( detail : RpgleTypeDetail , workspaceFolder : WorkspaceFolder ) : string [ ] {
243
- const includes : string [ ] = [ ] ;
244
-
245
- if ( detail . reference ) {
246
- const structPath = detail . reference . position . path ;
247
- if ( workspaceFolder ) {
248
- const relativePath = asPosix ( path . relative ( workspaceFolder . uri , structPath ) ) ;
249
- if ( ! includes . includes ( relativePath ) ) {
250
- includes . push ( `/include '${ relativePath } '` ) ; // TODO: Support members style includes
251
- }
252
- }
253
- }
254
-
255
- return includes ;
256
- }
257
-
258
- function getAssertions ( docs : Cache , detail : RpgleTypeDetail , expected : string , actual : string ) : string [ ] {
259
- const assertions : string [ ] = [ ] ;
260
-
261
- if ( detail ) {
262
- if ( detail . type ) {
263
- const assertion = getAssertion ( detail . type . name ) ;
264
- const fieldName = actual . split ( `.` ) . pop ( ) ;
265
- if ( assertion === `assert` ) {
266
- assertions . push ( `${ assertion } (${ expected } = ${ actual } ${ fieldName ? ` : '${ fieldName } '` : `` } );` ) ;
267
- } else {
268
- assertions . push ( `${ assertion } (${ expected } : ${ actual } ${ fieldName ? ` : '${ fieldName } '` : `` } );` ) ;
269
- }
270
- } else if ( detail . reference ) {
271
- for ( const subItem of detail . reference . subItems ) {
272
- const subItemType = docs . resolveType ( subItem ) ;
273
- const subItemAssertions = subItemType ?
274
- getAssertions ( docs , subItemType , `${ expected } .${ subItem . name } ` , `${ actual } .${ subItem . name } ` ) : [ ] ;
275
- assertions . push ( ...subItemAssertions ) ;
276
- }
277
- }
278
- }
279
-
280
- return assertions ;
281
- }
282
-
283
- function getDefaultValue ( type : RpgleVariableType ) : string {
284
- switch ( type ) {
285
- case `char` :
286
- case `varchar` :
287
- return `''` ;
288
- case `int` :
289
- case `uns` :
290
- return `0` ;
291
- case `packed` :
292
- case `zoned` :
293
- return `0.0` ;
294
- case `ind` :
295
- return `*off` ;
296
- case `date` :
297
- return `%date('0001-01-01' : *iso)` ;
298
- case `time` :
299
- return `%time('00.00.00' : *iso)` ;
300
- case `timestamp` :
301
- return `%timestamp('0001-01-01-00.00.00.000000' : *iso)` ;
302
- case `pointer` :
303
- return `*null` ;
304
- default :
305
- return 'unknown' ;
306
- }
307
- }
308
-
309
- function getAssertion ( type : RpgleVariableType ) : string {
310
- switch ( type ) {
311
- case `char` :
312
- case `varchar` :
313
- return `aEqual` ;
314
- case `int` :
315
- case `uns` :
316
- return `iEqual` ;
317
- case `packed` :
318
- case `zoned` :
319
- return `assert` ;
320
- case `ind` :
321
- return `nEqual` ;
322
- case `date` :
323
- return `assert` ;
324
- case `time` :
325
- return `assert` ;
326
- case `timestamp` :
327
- return `assert` ;
328
- case `pointer` :
329
- return `assert` ;
330
- default :
331
- return 'unknown' ;
332
- }
333
- }
334
-
335
- function asPosix ( inPath ?: string ) {
336
- return inPath ? inPath . split ( path . sep ) . join ( path . posix . sep ) : `` ;
337
- }
338
-
339
46
function lineAt ( document : TextDocument , line : number ) : string {
340
47
return document . getText ( Range . create ( line , 0 , line , 1000 ) ) . trimEnd ( ) ;
341
48
}
0 commit comments