10
10
11
11
SOURCE = 'importmagic'
12
12
ADD_IMPORT_COMMAND = 'importmagic.addimport'
13
+ REMOVE_IMPORT_COMMAND = 'importmagic.removeimport'
13
14
MAX_COMMANDS = 4
14
15
UNRES_RE = re .compile (r"Unresolved import '(?P<unresolved>[\w.]+)'" )
16
+ UNREF_RE = re .compile (r"Unreferenced import '(?P<unreferenced>[\w.]+)'" )
15
17
16
18
_index_cache = {}
17
19
@@ -31,9 +33,22 @@ def _get_index(sys_path):
31
33
return _index_cache [key ]
32
34
33
35
36
+ def _get_imports_list (source , index = None ):
37
+ """Get modules, functions and variables that are imported.
38
+ """
39
+ if index is None :
40
+ index = importmagic .SymbolIndex ()
41
+ imports = importmagic .Imports (index , source )
42
+ imported = [i .name for i in list (imports ._imports )]
43
+ # Go over from imports
44
+ for from_import in list (imports ._imports_from .values ()):
45
+ imported .extend ([i .name for i in list (from_import )])
46
+ return imported
47
+
48
+
34
49
@hookimpl
35
50
def pyls_commands ():
36
- return [ADD_IMPORT_COMMAND ]
51
+ return [ADD_IMPORT_COMMAND , REMOVE_IMPORT_COMMAND ]
37
52
38
53
39
54
@hookimpl
@@ -68,10 +83,6 @@ def pyls_lint(document):
68
83
69
84
# Annoyingly, we only get the text of an unresolved import, so we'll look for it ourselves
70
85
for unres in unresolved :
71
- # TODO (youben): delete this test as it double execution time (next for loop will do the same)
72
- if unres not in document .source :
73
- continue
74
-
75
86
for line_no , line in enumerate (document .lines ):
76
87
pos = line .find (unres )
77
88
if pos < 0 :
@@ -93,10 +104,9 @@ def pyls_lint(document):
93
104
if pos < 0 :
94
105
continue
95
106
96
- # Find out if the unref is a module or a variable/func
97
- imports = importmagic .Imports (importmagic .SymbolIndex (), document .source )
98
- modules = [m .name for m in list (imports ._imports )]
99
- if unref in modules :
107
+ # Find out if the unref is an import or a variable/func
108
+ imports = _get_imports_list (document .source )
109
+ if unref in imports :
100
110
message = "Unreferenced import '%s'" % unref
101
111
else :
102
112
message = "Unreferenced variable/function '%s'" % unref
@@ -119,7 +129,7 @@ def pyls_code_actions(config, document, context):
119
129
"""Build a list of actions to be suggested to the user. Each action follow this format:
120
130
{
121
131
'title': 'importmagic',
122
- 'command': command ('importmagic.add_import') ,
132
+ 'command': command,
123
133
'arguments':
124
134
{
125
135
'uri': document.uri,
@@ -136,31 +146,49 @@ def pyls_code_actions(config, document, context):
136
146
log .debug ("Got importmagic settings: %s" , conf )
137
147
importmagic .Imports .set_style (** {_utils .camel_to_underscore (k ): v for k , v in conf .items ()})
138
148
149
+ # Might be slow but is cached once built
150
+ # TODO (youben): add project path for indexing
151
+ index = _get_index (sys .path )
139
152
actions = []
140
153
diagnostics = context .get ('diagnostics' , [])
141
154
for diagnostic in diagnostics :
142
155
if diagnostic .get ('source' ) != SOURCE :
143
156
continue
144
- m = UNRES_RE .match (diagnostic ['message' ])
145
- if not m :
146
- continue
157
+ message = diagnostic .get ('message' , '' )
158
+ if message .startswith ('Unreferenced' ):
159
+ m = UNREF_RE .match (message )
160
+ if not m :
161
+ continue
162
+ unref = m .group ('unreferenced' )
163
+ actions .append (_generate_remove_action (document , index , unref ))
164
+ elif message .startswith ('Unresolved' ):
165
+ m = UNRES_RE .match (message )
166
+ if not m :
167
+ continue
168
+ unres = m .group ('unresolved' )
169
+ actions .extend (_get_actions_for_unres (document , index , min_score , unres ))
147
170
148
- unres = m .group ('unresolved' )
149
- # Might be slow but is cached once built
150
- index = _get_index (sys .path ) # TODO (youben): add project path for indexing
171
+ return actions
151
172
152
- for score , module , variable in sorted (index .symbol_scores (unres )[:MAX_COMMANDS ], reverse = True ):
153
- if score < min_score :
154
- # Skip low score results
155
- continue
156
173
157
- actions .append (_generate_add_action (document , index , module , variable ))
174
+ def _get_actions_for_unres (document , index , min_score , unres ):
175
+ """Get the list of possible actions to be applied to solve an unresolved symbol.
176
+ Get a maximun of MAX_COMMANDS actions with the highest score, also filter low score actions
177
+ using the min_score value.
178
+ """
179
+ actions = []
180
+ for score , module , variable in sorted (index .symbol_scores (unres )[:MAX_COMMANDS ], reverse = True ):
181
+ if score < min_score :
182
+ # Skip low score results
183
+ continue
184
+ actions .append (_generate_add_action (document , index , module , variable ))
158
185
159
186
return actions
160
187
161
188
162
189
def _generate_add_action (document , index , module , variable ):
163
- # Generate the patch we would need to apply
190
+ """Generate the patch we would need to apply to import a module.
191
+ """
164
192
imports = importmagic .Imports (index , document .source )
165
193
if variable :
166
194
imports .add_import_from (module , variable )
@@ -169,7 +197,7 @@ def _generate_add_action(document, index, module, variable):
169
197
start_line , end_line , text = imports .get_update ()
170
198
171
199
action = {
172
- 'title' : _command_title (variable , module ),
200
+ 'title' : _add_command_title (variable , module ),
173
201
'command' : ADD_IMPORT_COMMAND ,
174
202
'arguments' : [{
175
203
'uri' : document .uri ,
@@ -182,9 +210,30 @@ def _generate_add_action(document, index, module, variable):
182
210
return action
183
211
184
212
213
+ def _generate_remove_action (document , index , unref ):
214
+ """Generate the patch we would need to apply to remove an import.
215
+ """
216
+ imports = importmagic .Imports (index , document .source )
217
+ imports .remove (unref )
218
+ start_line , end_line , text = imports .get_update ()
219
+
220
+ action = {
221
+ 'title' : _remove_command_title (unref ),
222
+ 'command' : REMOVE_IMPORT_COMMAND ,
223
+ 'arguments' : [{
224
+ 'uri' : document .uri ,
225
+ 'version' : document .version ,
226
+ 'startLine' : start_line ,
227
+ 'endLine' : end_line ,
228
+ 'newText' : text
229
+ }]
230
+ }
231
+ return action
232
+
233
+
185
234
@hookimpl
186
235
def pyls_execute_command (workspace , command , arguments ):
187
- if command != ADD_IMPORT_COMMAND :
236
+ if command not in [ ADD_IMPORT_COMMAND , REMOVE_IMPORT_COMMAND ] :
188
237
return
189
238
190
239
args = arguments [0 ]
@@ -205,7 +254,11 @@ def pyls_execute_command(workspace, command, arguments):
205
254
workspace .apply_edit (edit )
206
255
207
256
208
- def _command_title (variable , module ):
257
+ def _add_command_title (variable , module ):
209
258
if not variable :
210
259
return 'Import "%s"' % module
211
260
return 'Import "%s" from "%s"' % (variable , module )
261
+
262
+
263
+ def _remove_command_title (import_name ):
264
+ return 'Remove import of "%s"' % import_name
0 commit comments