1010
1111SOURCE = 'importmagic'
1212ADD_IMPORT_COMMAND = 'importmagic.addimport'
13+ REMOVE_IMPORT_COMMAND = 'importmagic.removeimport'
1314MAX_COMMANDS = 4
1415UNRES_RE = re .compile (r"Unresolved import '(?P<unresolved>[\w.]+)'" )
16+ UNREF_RE = re .compile (r"Unreferenced import '(?P<unreferenced>[\w.]+)'" )
1517
1618_index_cache = {}
1719
@@ -31,9 +33,22 @@ def _get_index(sys_path):
3133 return _index_cache [key ]
3234
3335
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+
3449@hookimpl
3550def pyls_commands ():
36- return [ADD_IMPORT_COMMAND ]
51+ return [ADD_IMPORT_COMMAND , REMOVE_IMPORT_COMMAND ]
3752
3853
3954@hookimpl
@@ -68,10 +83,6 @@ def pyls_lint(document):
6883
6984 # Annoyingly, we only get the text of an unresolved import, so we'll look for it ourselves
7085 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-
7586 for line_no , line in enumerate (document .lines ):
7687 pos = line .find (unres )
7788 if pos < 0 :
@@ -93,10 +104,9 @@ def pyls_lint(document):
93104 if pos < 0 :
94105 continue
95106
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 :
100110 message = "Unreferenced import '%s'" % unref
101111 else :
102112 message = "Unreferenced variable/function '%s'" % unref
@@ -119,7 +129,7 @@ def pyls_code_actions(config, document, context):
119129 """Build a list of actions to be suggested to the user. Each action follow this format:
120130 {
121131 'title': 'importmagic',
122- 'command': command ('importmagic.add_import') ,
132+ 'command': command,
123133 'arguments':
124134 {
125135 'uri': document.uri,
@@ -136,31 +146,48 @@ def pyls_code_actions(config, document, context):
136146 log .debug ("Got importmagic settings: %s" , conf )
137147 importmagic .Imports .set_style (** {_utils .camel_to_underscore (k ): v for k , v in conf .items ()})
138148
149+ # Might be slow but is cached once built
150+ index = _get_index (sys .path ) # TODO (youben): add project path for indexing
139151 actions = []
140152 diagnostics = context .get ('diagnostics' , [])
141153 for diagnostic in diagnostics :
142154 if diagnostic .get ('source' ) != SOURCE :
143155 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 ))
147169
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
151171
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
156172
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 ))
158184
159185 return actions
160186
161187
162188def _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+ """
164191 imports = importmagic .Imports (index , document .source )
165192 if variable :
166193 imports .add_import_from (module , variable )
@@ -169,7 +196,7 @@ def _generate_add_action(document, index, module, variable):
169196 start_line , end_line , text = imports .get_update ()
170197
171198 action = {
172- 'title' : _command_title (variable , module ),
199+ 'title' : _add_command_title (variable , module ),
173200 'command' : ADD_IMPORT_COMMAND ,
174201 'arguments' : [{
175202 'uri' : document .uri ,
@@ -182,9 +209,30 @@ def _generate_add_action(document, index, module, variable):
182209 return action
183210
184211
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+
185233@hookimpl
186234def pyls_execute_command (workspace , command , arguments ):
187- if command != ADD_IMPORT_COMMAND :
235+ if command not in [ ADD_IMPORT_COMMAND , REMOVE_IMPORT_COMMAND ] :
188236 return
189237
190238 args = arguments [0 ]
@@ -205,7 +253,11 @@ def pyls_execute_command(workspace, command, arguments):
205253 workspace .apply_edit (edit )
206254
207255
208- def _command_title (variable , module ):
256+ def _add_command_title (variable , module ):
209257 if not variable :
210258 return 'Import "%s"' % module
211259 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