@@ -22,21 +22,21 @@ def create_move(project, resource, offset=None):
2222 return MoveModule (project , resource )
2323 this_pymodule = project .get_pymodule (resource )
2424 pyname = evaluate .eval_location (this_pymodule , offset )
25- if pyname is None :
26- raise exceptions .RefactoringError (
27- 'Move only works on classes, functions, modules and methods.' )
28- pyobject = pyname .get_object ()
29- if isinstance (pyobject , pyobjects .PyModule ) or \
30- isinstance (pyobject , pyobjects .PyPackage ):
31- return MoveModule (project , pyobject .get_resource ())
32- if isinstance (pyobject , pyobjects .PyFunction ) and \
33- isinstance (pyobject .parent , pyobjects .PyClass ):
34- return MoveMethod (project , resource , offset )
35- if isinstance (pyobject , pyobjects .PyDefinedObject ) and \
36- isinstance (pyobject .parent , pyobjects .PyModule ):
37- return MoveGlobal (project , resource , offset )
25+ if pyname is not None :
26+ pyobject = pyname .get_object ()
27+ if isinstance (pyobject , pyobjects .PyModule ) or \
28+ isinstance (pyobject , pyobjects .PyPackage ):
29+ return MoveModule (project , pyobject .get_resource ())
30+ if isinstance (pyobject , pyobjects .PyFunction ) and \
31+ isinstance (pyobject .parent , pyobjects .PyClass ):
32+ return MoveMethod (project , resource , offset )
33+ if isinstance (pyobject , pyobjects .PyDefinedObject ) and \
34+ isinstance (pyobject .parent , pyobjects .PyModule ) or \
35+ isinstance (pyname , pynames .AssignedName ):
36+ return MoveGlobal (project , resource , offset )
3837 raise exceptions .RefactoringError (
39- 'Move only works on global classes/functions, modules and methods.' )
38+ 'Move only works on global classes/functions/variables, modules and '
39+ 'methods.' )
4040
4141
4242class MoveMethod (object ):
@@ -203,42 +203,63 @@ def __init__(self, project, resource, offset):
203203 self .project = project
204204 this_pymodule = self .project .get_pymodule (resource )
205205 self .old_pyname = evaluate .eval_location (this_pymodule , offset )
206+ if self .old_pyname is None :
207+ raise exceptions .RefactoringError (
208+ 'Move refactoring should be performed on a '
209+ 'class/function/variable.' )
210+ if self ._is_variable (self .old_pyname ):
211+ self .old_name = worder .get_name_at (resource , offset )
212+ pymodule = this_pymodule
213+ else :
214+ self .old_name = self .old_pyname .get_object ().get_name ()
215+ pymodule = self .old_pyname .get_object ().get_module ()
206216 self ._check_exceptional_conditions ()
207- self .old_name = self .old_pyname .get_object ().get_name ()
208- pymodule = self .old_pyname .get_object ().get_module ()
209217 self .source = pymodule .get_resource ()
210218 self .tools = _MoveTools (self .project , self .source ,
211219 self .old_pyname , self .old_name )
212220 self .import_tools = self .tools .import_tools
213221
214222 def _import_filter (self , stmt ):
215223 module_name = libutils .modname (self .source )
224+
216225 if isinstance (stmt .import_info , importutils .NormalImport ):
226+ # Affect any statement that imports the source module
217227 return any (module_name == name
218228 for name , alias in stmt .import_info .names_and_aliases )
219229 elif isinstance (stmt .import_info , importutils .FromImport ):
230+ # Affect statements importing from the source package
220231 if '.' in module_name :
221- package_name = '.' .join (module_name .split ('.' )[:- 1 ])
222- if stmt .import_info .module_name == package_name :
232+ package_name , basename = module_name .rsplit ('.' , 1 )
233+ if (stmt .import_info .module_name == package_name and
234+ any (basename == name
235+ for name , alias in stmt .import_info .names_and_aliases )):
223236 return True
224237 return stmt .import_info .module_name == module_name
225238 return False
226239
227240 def _check_exceptional_conditions (self ):
228- if self .old_pyname is None or \
229- not isinstance (self .old_pyname .get_object (),
230- pyobjects .PyDefinedObject ):
231- raise exceptions .RefactoringError (
232- 'Move refactoring should be performed on a class/function.' )
233- moving_pyobject = self .old_pyname .get_object ()
234- if not self ._is_global (moving_pyobject ):
235- raise exceptions .RefactoringError (
236- 'Move refactoring should be performed ' +
237- 'on a global class/function.' )
241+ if self ._is_variable (self .old_pyname ):
242+ pymodule = self .old_pyname .get_definition_location ()[0 ]
243+ try :
244+ pymodule .get_scope ().get_name (self .old_name )
245+ except exceptions .NameNotFoundError :
246+ self ._raise_refactoring_error ()
247+ elif not (isinstance (self .old_pyname .get_object (),
248+ pyobjects .PyDefinedObject ) and
249+ self ._is_global (self .old_pyname .get_object ())):
250+ self ._raise_refactoring_error ()
251+
252+ def _raise_refactoring_error (self ):
253+ raise exceptions .RefactoringError (
254+ 'Move refactoring should be performed on a global class, function '
255+ 'or variable.' )
238256
239257 def _is_global (self , pyobject ):
240258 return pyobject .get_scope ().parent == pyobject .get_module ().get_scope ()
241259
260+ def _is_variable (self , pyname ):
261+ return isinstance (pyname , pynames .AssignedName )
262+
242263 def get_changes (self , dest , resources = None ,
243264 task_handle = taskhandle .NullTaskHandle ()):
244265 if resources is None :
@@ -367,9 +388,23 @@ def _get_moving_element(self):
367388 def _get_moving_region (self ):
368389 pymodule = self .project .get_pymodule (self .source )
369390 lines = pymodule .lines
370- scope = self .old_pyname .get_object ().get_scope ()
371- start = lines .get_line_start (scope .get_start ())
372- end_line = scope .get_end ()
391+ if self ._is_variable (self .old_pyname ):
392+ logical_lines = pymodule .logical_lines
393+ lineno = logical_lines .logical_line_in (
394+ self .old_pyname .get_definition_location ()[1 ])[0 ]
395+ start = lines .get_line_start (lineno )
396+ end_line = logical_lines .logical_line_in (lineno )[1 ]
397+ else :
398+ scope = self .old_pyname .get_object ().get_scope ()
399+ start = lines .get_line_start (scope .get_start ())
400+ end_line = scope .get_end ()
401+
402+ # Include comment lines before the definition
403+ start_line = lines .get_line_number (start )
404+ while start_line > 1 and lines .get_line (start_line - 1 ).startswith ('#' ):
405+ start_line -= 1
406+ start = lines .get_line_start (start_line )
407+
373408 while end_line < lines .length () and \
374409 lines .get_line (end_line + 1 ).strip () == '' :
375410 end_line += 1
@@ -650,6 +685,17 @@ def _add_imports_to_module(import_tools, pymodule, new_imports):
650685def moving_code_with_imports (project , resource , source ):
651686 import_tools = importutils .ImportTools (project )
652687 pymodule = libutils .get_string_module (project , source , resource )
688+
689+ # Strip comment prefix, if any. These need to stay before the moving
690+ # section, but imports would be added between them.
691+ lines = codeanalyze .SourceLinesAdapter (source )
692+ start = 1
693+ while start < lines .length () and lines .get_line (start ).startswith ('#' ):
694+ start += 1
695+ moving_prefix = source [:lines .get_line_start (start )]
696+ pymodule = libutils .get_string_module (
697+ project , source [lines .get_line_start (start ):], resource )
698+
653699 origin = project .get_pymodule (resource )
654700
655701 imports = []
@@ -680,7 +726,9 @@ def moving_code_with_imports(project, resource, source):
680726 lines = codeanalyze .SourceLinesAdapter (source )
681727 while start < lines .length () and not lines .get_line (start ).strip ():
682728 start += 1
683- moving = source [lines .get_line_start (start ):]
729+
730+ # Reinsert the prefix which was removed at the beginning
731+ moving = moving_prefix + source [lines .get_line_start (start ):]
684732 return moving , imports
685733
686734
0 commit comments