10
10
ChangeAnnotation ,
11
11
CodeAction ,
12
12
CodeActionContext ,
13
+ CodeActionDisabledType ,
13
14
CodeActionKind ,
14
15
CodeActionTriggerKind ,
15
16
Command ,
@@ -126,34 +127,68 @@ async def collect(
126
127
async def code_action_create_keyword (
127
128
self , document : TextDocument , range : Range , context : CodeActionContext
128
129
) -> Optional [List [Union [Command , CodeAction ]]]:
130
+ from robot .parsing .model .statements import (
131
+ Fixture ,
132
+ KeywordCall ,
133
+ Template ,
134
+ TestTemplate ,
135
+ )
136
+
129
137
result : List [Union [Command , CodeAction ]] = []
130
138
131
139
if (context .only and CodeActionKind .QUICK_FIX in context .only ) or context .trigger_kind in [
132
140
CodeActionTriggerKind .INVOKED ,
133
141
CodeActionTriggerKind .AUTOMATIC ,
134
142
]:
143
+ model = await self .parent .documents_cache .get_model (document , False )
144
+ namespace = await self .parent .documents_cache .get_namespace (document )
145
+
135
146
for diagnostic in (
136
147
d
137
148
for d in context .diagnostics
138
149
if d .source == DIAGNOSTICS_SOURCE_NAME and d .code == Error .KEYWORD_NOT_FOUND
139
150
):
140
- text = document .get_lines ()[diagnostic .range .start .line ][
141
- diagnostic .range .start .character : diagnostic .range .end .character
142
- ]
143
- if not text :
144
- continue
145
- result .append (
146
- CodeAction (
147
- f"Create Keyword `{ text } `" ,
148
- kind = CodeActionKind .QUICK_FIX ,
149
- command = Command (
150
- self .parent .commands .get_command_name (self .create_keyword_command ),
151
- self .parent .commands .get_command_name (self .create_keyword_command ),
152
- [document .document_uri , diagnostic .range ],
153
- ),
154
- diagnostics = [diagnostic ],
151
+ disabled = None
152
+ node = await get_node_at_position (model , diagnostic .range .start )
153
+
154
+ if isinstance (node , (KeywordCall , Fixture , TestTemplate , Template )):
155
+ tokens = get_tokens_at_position (node , diagnostic .range .start )
156
+ if not tokens :
157
+ continue
158
+
159
+ keyword_token = tokens [- 1 ]
160
+
161
+ bdd_token , token = self .split_bdd_prefix (namespace , keyword_token )
162
+ if bdd_token is not None and token is not None :
163
+ keyword_token = token
164
+
165
+ lib_entry , kw_namespace = await self .get_namespace_info_from_keyword (namespace , keyword_token )
166
+
167
+ if lib_entry is not None and lib_entry .library_doc .type == "LIBRARY" :
168
+ disabled = CodeActionDisabledType ("Keyword is from a library" )
169
+
170
+ text = keyword_token .value
171
+
172
+ if lib_entry and kw_namespace :
173
+ text = text [len (kw_namespace ) + 1 :].strip ()
174
+
175
+ if not text :
176
+ continue
177
+
178
+ result .append (
179
+ CodeAction (
180
+ f"Create Keyword `{ text } `" ,
181
+ kind = CodeActionKind .QUICK_FIX ,
182
+ command = Command (
183
+ self .parent .commands .get_command_name (self .create_keyword_command ),
184
+ self .parent .commands .get_command_name (self .create_keyword_command ),
185
+ [document .document_uri , diagnostic .range ],
186
+ ),
187
+ diagnostics = [diagnostic ],
188
+ disabled = disabled ,
189
+ is_preferred = True ,
190
+ )
155
191
)
156
- )
157
192
158
193
return result if result else None
159
194
@@ -187,9 +222,20 @@ async def create_keyword_command(self, document_uri: DocumentUri, range: Range)
187
222
188
223
bdd_token , token = self .split_bdd_prefix (namespace , keyword_token )
189
224
if bdd_token is not None and token is not None :
190
- keyword = token .value
191
- else :
192
- keyword = keyword_token .value
225
+ keyword_token = token
226
+
227
+ lib_entry , kw_namespace = await self .get_namespace_info_from_keyword (namespace , keyword_token )
228
+
229
+ if lib_entry is not None and lib_entry .library_doc .type == "LIBRARY" :
230
+ return
231
+
232
+ text = keyword_token .value
233
+
234
+ if lib_entry and kw_namespace :
235
+ text = text [len (kw_namespace ) + 1 :].strip ()
236
+
237
+ if not text :
238
+ return
193
239
194
240
arguments = []
195
241
@@ -202,54 +248,65 @@ async def create_keyword_command(self, document_uri: DocumentUri, range: Range)
202
248
arguments .append (f"${{arg{ len (arguments )+ 1 } }}" )
203
249
204
250
insert_text = (
205
- KEYWORD_WITH_ARGS_TEMPLATE .substitute (name = keyword , args = " " .join (arguments ))
251
+ KEYWORD_WITH_ARGS_TEMPLATE .substitute (name = text , args = " " .join (arguments ))
206
252
if arguments
207
- else KEYWORD_TEMPLATE .substitute (name = keyword )
253
+ else KEYWORD_TEMPLATE .substitute (name = text )
208
254
)
209
255
210
- keyword_sections = find_keyword_sections (model )
211
- keyword_section = keyword_sections [- 1 ] if keyword_sections else None
256
+ if lib_entry is not None and lib_entry .library_doc .type == "RESOURCE" and lib_entry .library_doc .source :
257
+ dest_document = await self .parent .documents .get_or_open_document (lib_entry .library_doc .source )
258
+ else :
259
+ dest_document = document
212
260
213
- if keyword_section is not None :
214
- node_range = range_from_node (keyword_section )
261
+ await self ._apply_create_keyword (dest_document , insert_text )
215
262
216
- insert_pos = Position (node_range .end .line + 1 , 0 )
217
- insert_range = Range (insert_pos , insert_pos )
263
+ async def _apply_create_keyword (self , document : TextDocument , insert_text : str ) -> None :
264
+ model = await self .parent .documents_cache .get_model (document , False )
265
+ namespace = await self .parent .documents_cache .get_namespace (document )
218
266
219
- insert_text = f"\n { insert_text } "
220
- else :
221
- if namespace .languages is None or not namespace .languages .languages :
222
- keywords_text = "Keywords"
223
- else :
224
- keywords_text = namespace .languages .languages [- 1 ].keywords_header
267
+ keyword_sections = find_keyword_sections (model )
268
+ keyword_section = keyword_sections [- 1 ] if keyword_sections else None
225
269
226
- insert_text = f"\n \n *** { keywords_text } ***\n { insert_text } "
270
+ if keyword_section is not None :
271
+ node_range = range_from_node (keyword_section )
227
272
228
- lines = document .get_lines ()
229
- end_line = len (lines ) - 1
230
- while end_line >= 0 and not lines [end_line ].strip ():
231
- end_line -= 1
232
- doc_pos = Position (end_line + 1 , 0 )
273
+ insert_pos = Position (node_range .end .line + 1 , 0 )
274
+ insert_range = Range (insert_pos , insert_pos )
233
275
234
- insert_range = Range (doc_pos , doc_pos )
276
+ insert_text = f"\n { insert_text } "
277
+ else :
278
+ if namespace .languages is None or not namespace .languages .languages :
279
+ keywords_text = "Keywords"
280
+ else :
281
+ keywords_text = namespace .languages .languages [- 1 ].keywords_header
235
282
236
- we = WorkspaceEdit (
237
- document_changes = [
238
- TextDocumentEdit (
239
- OptionalVersionedTextDocumentIdentifier (str (document .uri ), document .version ),
240
- [AnnotatedTextEdit ("create_keyword" , insert_range , insert_text )],
241
- )
242
- ],
243
- change_annotations = {"create_keyword" : ChangeAnnotation ("Create Keyword" , False )},
244
- )
283
+ insert_text = f"\n \n *** { keywords_text } ***\n { insert_text } "
245
284
246
- if (await self .parent .workspace .apply_edit (we )).applied :
247
- lines = insert_text .rstrip ().splitlines ()
248
- insert_range .start .line += len (lines ) - 1
249
- insert_range .start .character = 4
250
- insert_range .end = Position (insert_range .start .line , insert_range .start .character )
251
- insert_range .end .character += len (lines [- 1 ])
252
- await self .parent .window .show_document (str (document .uri ), take_focus = True , selection = insert_range )
285
+ lines = document .get_lines ()
286
+ end_line = len (lines ) - 1
287
+ while end_line >= 0 and not lines [end_line ].strip ():
288
+ end_line -= 1
289
+ doc_pos = Position (end_line + 1 , 0 )
290
+
291
+ insert_range = Range (doc_pos , doc_pos )
292
+
293
+ we = WorkspaceEdit (
294
+ document_changes = [
295
+ TextDocumentEdit (
296
+ OptionalVersionedTextDocumentIdentifier (str (document .uri ), document .version ),
297
+ [AnnotatedTextEdit ("create_keyword" , insert_range , insert_text )],
298
+ )
299
+ ],
300
+ change_annotations = {"create_keyword" : ChangeAnnotation ("Create Keyword" , False )},
301
+ )
302
+
303
+ if (await self .parent .workspace .apply_edit (we )).applied :
304
+ lines = insert_text .rstrip ().splitlines ()
305
+ insert_range .start .line += len (lines ) - 1
306
+ insert_range .start .character = 4
307
+ insert_range .end = Position (insert_range .start .line , insert_range .start .character )
308
+ insert_range .end .character += len (lines [- 1 ])
309
+ await self .parent .window .show_document (str (document .uri ), take_focus = True , selection = insert_range )
253
310
254
311
async def code_action_assign_result_to_variable (
255
312
self , document : TextDocument , range : Range , context : CodeActionContext
@@ -262,10 +319,14 @@ async def code_action_assign_result_to_variable(
262
319
TestTemplate ,
263
320
)
264
321
265
- if (context .only and QUICK_FIX_OTHER in context .only ) or context .trigger_kind in [
266
- CodeActionTriggerKind .INVOKED ,
267
- CodeActionTriggerKind .AUTOMATIC ,
268
- ]:
322
+ if range .start .line == range .end .line and (
323
+ (context .only and QUICK_FIX_OTHER in context .only )
324
+ or context .trigger_kind
325
+ in [
326
+ CodeActionTriggerKind .INVOKED ,
327
+ CodeActionTriggerKind .AUTOMATIC ,
328
+ ]
329
+ ):
269
330
model = await self .parent .documents_cache .get_model (document , False )
270
331
node = await get_node_at_position (model , range .start )
271
332
0 commit comments