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,48 @@ 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
+ index = _get_index (sys .path ) # TODO (youben): add project path for indexing
139
151
actions = []
140
152
diagnostics = context .get ('diagnostics' , [])
141
153
for diagnostic in diagnostics :
142
154
if diagnostic .get ('source' ) != SOURCE :
143
155
continue
144
- m = UNRES_RE .match (diagnostic ['message' ])
145
- if not m :
146
- continue
156
+ message = diagnostic .get ('message' , '' )
157
+ if message .startswith ('Unreferenced' ):
158
+ m = UNREF_RE .match (message )
159
+ if not m :
160
+ continue
161
+ unref = m .group ('unreferenced' )
162
+ actions .append (_generate_remove_action (document , index , unref ))
163
+ elif message .startswith ('Unresolved' ):
164
+ m = UNRES_RE .match (message )
165
+ if not m :
166
+ continue
167
+ unres = m .group ('unresolved' )
168
+ actions .extend (_get_actions_for_unres (document , index , min_score , unres ))
147
169
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
170
+ return actions
151
171
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
172
157
- actions .append (_generate_add_action (document , index , module , variable ))
173
+ def _get_actions_for_unres (document , index , min_score , unres ):
174
+ """Get the list of possible actions to be applied to solve an unresolved symbol.
175
+ Get a maximun of MAX_COMMANDS actions with the highest score, also filter low score actions
176
+ using the min_score value.
177
+ """
178
+ actions = []
179
+ for score , module , variable in sorted (index .symbol_scores (unres )[:MAX_COMMANDS ], reverse = True ):
180
+ if score < min_score :
181
+ # Skip low score results
182
+ continue
183
+ actions .append (_generate_add_action (document , index , module , variable ))
158
184
159
185
return actions
160
186
161
187
162
188
def _generate_add_action (document , index , module , variable ):
163
- # Generate the patch we would need to apply
189
+ """Generate the patch we would need to apply to import a module.
190
+ """
164
191
imports = importmagic .Imports (index , document .source )
165
192
if variable :
166
193
imports .add_import_from (module , variable )
@@ -169,7 +196,7 @@ def _generate_add_action(document, index, module, variable):
169
196
start_line , end_line , text = imports .get_update ()
170
197
171
198
action = {
172
- 'title' : _command_title (variable , module ),
199
+ 'title' : _add_command_title (variable , module ),
173
200
'command' : ADD_IMPORT_COMMAND ,
174
201
'arguments' : [{
175
202
'uri' : document .uri ,
@@ -182,9 +209,30 @@ def _generate_add_action(document, index, module, variable):
182
209
return action
183
210
184
211
212
+ def _generate_remove_action (document , index , unref ):
213
+ """Generate the patch we would need to apply to remove an import.
214
+ """
215
+ imports = importmagic .Imports (index , document .source )
216
+ imports .remove (unref )
217
+ start_line , end_line , text = imports .get_update ()
218
+
219
+ action = {
220
+ 'title' : _remove_command_title (unref ),
221
+ 'command' : REMOVE_IMPORT_COMMAND ,
222
+ 'arguments' : [{
223
+ 'uri' : document .uri ,
224
+ 'version' : document .version ,
225
+ 'startLine' : start_line ,
226
+ 'endLine' : end_line ,
227
+ 'newText' : text
228
+ }]
229
+ }
230
+ return action
231
+
232
+
185
233
@hookimpl
186
234
def pyls_execute_command (workspace , command , arguments ):
187
- if command != ADD_IMPORT_COMMAND :
235
+ if command not in [ ADD_IMPORT_COMMAND , REMOVE_IMPORT_COMMAND ] :
188
236
return
189
237
190
238
args = arguments [0 ]
@@ -205,7 +253,11 @@ def pyls_execute_command(workspace, command, arguments):
205
253
workspace .apply_edit (edit )
206
254
207
255
208
- def _command_title (variable , module ):
256
+ def _add_command_title (variable , module ):
209
257
if not variable :
210
258
return 'Import "%s"' % module
211
259
return 'Import "%s" from "%s"' % (variable , module )
260
+
261
+
262
+ def _remove_command_title (import_name ):
263
+ return 'Remove import of "%s"' % import_name
0 commit comments