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,49 @@ 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+ # TODO (youben): add project path for indexing
151+ index = _get_index (sys .path )
139152 actions = []
140153 diagnostics = context .get ('diagnostics' , [])
141154 for diagnostic in diagnostics :
142155 if diagnostic .get ('source' ) != SOURCE :
143156 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 ))
147170
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
151172
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
156173
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 ))
158185
159186 return actions
160187
161188
162189def _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+ """
164192 imports = importmagic .Imports (index , document .source )
165193 if variable :
166194 imports .add_import_from (module , variable )
@@ -169,7 +197,7 @@ def _generate_add_action(document, index, module, variable):
169197 start_line , end_line , text = imports .get_update ()
170198
171199 action = {
172- 'title' : _command_title (variable , module ),
200+ 'title' : _add_command_title (variable , module ),
173201 'command' : ADD_IMPORT_COMMAND ,
174202 'arguments' : [{
175203 'uri' : document .uri ,
@@ -182,9 +210,30 @@ def _generate_add_action(document, index, module, variable):
182210 return action
183211
184212
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+
185234@hookimpl
186235def pyls_execute_command (workspace , command , arguments ):
187- if command != ADD_IMPORT_COMMAND :
236+ if command not in [ ADD_IMPORT_COMMAND , REMOVE_IMPORT_COMMAND ] :
188237 return
189238
190239 args = arguments [0 ]
@@ -205,7 +254,11 @@ def pyls_execute_command(workspace, command, arguments):
205254 workspace .apply_edit (edit )
206255
207256
208- def _command_title (variable , module ):
257+ def _add_command_title (variable , module ):
209258 if not variable :
210259 return 'Import "%s"' % module
211260 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