@@ -24,7 +24,7 @@ export default async function genericCodeActionsProvider(params: CodeActionParam
24
24
25
25
const extractOption = getExtractProcedureAction ( document , docs , range ) ;
26
26
if ( extractOption ) {
27
- return [ extractOption ] ;
27
+ actions . push ( extractOption ) ;
28
28
}
29
29
30
30
const linterActions = await getLinterCodeActions ( docs , document , range ) ;
@@ -37,6 +37,11 @@ export default async function genericCodeActionsProvider(params: CodeActionParam
37
37
// if (testCaseOption) {
38
38
// actions.push(testCaseOption);
39
39
// }
40
+
41
+ const monitorAction = surroundWithMonitorAction ( isFree , document , docs , range ) ;
42
+ if ( monitorAction ) {
43
+ actions . push ( monitorAction ) ;
44
+ }
40
45
}
41
46
}
42
47
@@ -71,7 +76,102 @@ export function getTestCaseAction(document: TextDocument, docs: Cache, range: Ra
71
76
}
72
77
}
73
78
74
- export function getSubroutineActions ( document : TextDocument , docs : Cache , range : Range ) : CodeAction | undefined {
79
+ function lineAt ( document : TextDocument , line : number ) : string {
80
+ return document . getText ( Range . create ( line , 0 , line , 1000 ) ) . trimEnd ( ) ;
81
+ }
82
+
83
+ function getBodyRangeForRange ( docs : Cache , rangeStart : number , rangeEnd : number , document : TextDocument ) {
84
+ let validStart = - 1 ;
85
+ let validEnd = - 1 ;
86
+
87
+ const currentProcedure = docs . procedures . find ( sub => rangeStart >= sub . position . range . line && rangeEnd <= sub . range . end ! ) ;
88
+
89
+ if ( currentProcedure && currentProcedure . scope && currentProcedure . range . end ) {
90
+ validStart = currentProcedure . scope . getDefinitionBlockEnd ( document . uri ) + 1 ;
91
+ validEnd = currentProcedure . range . end - 1 ;
92
+ } else {
93
+ validStart = docs . getDefinitionBlockEnd ( document . uri ) + 1 ;
94
+ const firstProc = docs . procedures . find ( p => ! Object . keys ( p . keyword ) . some ( k => k . toLowerCase ( ) . startsWith ( `ext` ) ) ) ;
95
+ validEnd = firstProc && firstProc . range . start ? firstProc . range . start - 1 : document . lineCount - 1 ;
96
+ }
97
+
98
+ if ( validStart < 0 || validEnd < 0 ) {
99
+ return ;
100
+ }
101
+
102
+ return { validStart, validEnd } ;
103
+ }
104
+
105
+ function determineIndent ( line : string ) : number {
106
+ const match = line . match ( / ^ \s + / ) ;
107
+ if ( match ) {
108
+ return match [ 0 ] . length ;
109
+ }
110
+ return 0 ;
111
+ }
112
+
113
+ export function surroundWithMonitorAction ( isFree : boolean , document : TextDocument , docs : Cache , range : Range ) : CodeAction | undefined {
114
+ let rangeStart = range . start . line ;
115
+ let rangeEnd = range . end . line ;
116
+
117
+ if ( rangeStart === rangeEnd ) {
118
+ // Only works for multi-line selections
119
+ return ;
120
+ }
121
+
122
+ const validRange = getBodyRangeForRange ( docs , rangeStart , rangeEnd , document ) ;
123
+
124
+ if ( ! validRange ) {
125
+ return ;
126
+ }
127
+
128
+ let indent = 0 ;
129
+
130
+ if ( isFree ) {
131
+ indent = determineIndent ( lineAt ( document , rangeStart ) ) ;
132
+ } else {
133
+ const freePortion = lineAt ( document , rangeStart ) . substring ( 7 ) ;
134
+ indent = determineIndent ( freePortion ) + 7 ;
135
+ }
136
+
137
+ const newLine = `\n` ;
138
+ const indentStr = ` ` . repeat ( indent ) ;
139
+ const space = `${ newLine } ${ indentStr } ` ;
140
+
141
+ if ( rangeStart >= validRange . validStart && rangeEnd <= validRange . validEnd ) {
142
+ const refactorAction = CodeAction . create ( `Surround with monitor` , CodeActionKind . RefactorRewrite ) ;
143
+
144
+ refactorAction . edit = {
145
+ changes : {
146
+ [ document . uri ] : [
147
+ TextEdit . insert (
148
+ Position . create ( rangeStart , 0 ) ,
149
+ `${ indentStr } monitor;${ newLine } `
150
+ ) ,
151
+ TextEdit . insert (
152
+ Position . create ( rangeEnd + 1 , 0 ) ,
153
+ `${ space } on-error *all;${ space } // TODO: implement${ space } endmon;${ newLine } `
154
+ )
155
+ ]
156
+ } ,
157
+ } ;
158
+
159
+ if ( isFree ) {
160
+ // Then format the document
161
+ refactorAction . command = {
162
+ command : `editor.action.formatDocument` ,
163
+ title : `Format`
164
+ } ;
165
+ }
166
+
167
+ return refactorAction ;
168
+ }
169
+
170
+
171
+ return ;
172
+ }
173
+
174
+ export function getSubroutineActions ( document : TextDocument , docs : Cache , range : Range ) : CodeAction | undefined {
75
175
if ( range . start . line === range . end . line ) {
76
176
const currentGlobalSubroutine = docs . subroutines . find ( sub => sub . position . range . line === range . start . line && sub . range . start && sub . range . end ) ;
77
177
@@ -93,7 +193,7 @@ export function getSubroutineActions(document: TextDocument, docs: Cache, range:
93
193
const newProcedure = [
94
194
`Dcl-Proc ${ currentGlobalSubroutine . name } ;` ,
95
195
` Dcl-Pi *N;` ,
96
- ...extracted . references . map ( ( ref , i ) => ` ${ extracted . newParamNames [ i ] } ${ ref . dec . type === `struct` ? `LikeDS` : `Like` } (${ ref . dec . name } );` ) ,
196
+ ...extracted . references . map ( ( ref , i ) => ` ${ extracted . newParamNames [ i ] } ${ ref . dec . type === `struct` ? `LikeDS` : `Like` } (${ ref . dec . name } );` ) ,
97
197
` End-Pi;` ,
98
198
`` ,
99
199
caseInsensitiveReplaceAll ( extracted . newBody , `leavesr` , `return` ) ,
@@ -131,30 +231,40 @@ export function getSubroutineActions(document: TextDocument, docs: Cache, range:
131
231
}
132
232
}
133
233
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 } ) ;
234
+ export function getExtractProcedureAction ( document : TextDocument , docs : Cache , range : Range ) : CodeAction | undefined {
235
+ const rangeStart = range . start . line ;
236
+ const rangeEnd = range . end . line ;
237
+ if ( rangeEnd > rangeStart ) {
238
+
239
+ const validRange = getBodyRangeForRange ( docs , rangeStart , rangeEnd , document ) ;
137
240
241
+ if ( ! validRange ) {
242
+ return ;
243
+ }
244
+
245
+ // Ensure the selected range is within a body range
246
+ if ( rangeStart >= validRange . validStart && rangeEnd <= validRange . validEnd ) {
138
247
const extracted = createExtract ( document , range , docs ) ;
139
248
140
249
const newProcedure = [
141
250
`Dcl-Proc NewProcedure;` ,
142
251
` Dcl-Pi *N;` ,
143
- ...extracted . references . map ( ( ref , i ) => ` ${ extracted . newParamNames [ i ] } ${ ref . dec . type === `struct` ? `LikeDS` : `Like` } (${ ref . dec . name } );` ) ,
252
+ ...extracted . references . map ( ( ref , i ) => ` ${ extracted . newParamNames [ i ] } ${ ref . dec . type === `struct` ? `LikeDS` : `Like` } (${ ref . dec . name } );` ) ,
144
253
` End-Pi;` ,
145
254
`` ,
146
255
extracted . newBody ,
147
256
`End-Proc;`
148
257
] . join ( `\n` )
149
258
150
259
const newAction = CodeAction . create ( `Extract to new procedure` , CodeActionKind . RefactorExtract ) ;
260
+ const lastLine = document . offsetAt ( { line : document . lineCount , character : 0 } ) ;
151
261
152
262
// First do the exit
153
263
newAction . edit = {
154
264
changes : {
155
265
[ document . uri ] : [
156
266
TextEdit . replace ( extracted . range , `NewProcedure(${ extracted . references . map ( r => r . dec . name ) . join ( `:` ) } );` ) ,
157
- TextEdit . insert ( document . positionAt ( lastLine ) , `\n\n` + newProcedure )
267
+ TextEdit . insert ( document . positionAt ( lastLine ) , `\n\n` + newProcedure )
158
268
]
159
269
} ,
160
270
} ;
@@ -166,5 +276,6 @@ export function getExtractProcedureAction(document: TextDocument, docs: Cache, r
166
276
} ;
167
277
168
278
return newAction ;
279
+ }
169
280
}
170
281
}
0 commit comments