Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/emacs-for-python/rope-dist/rope/refactor/introduce_parameter.py
1415 views
1
import rope.base.change
2
from rope.base import exceptions, evaluate, worder, codeanalyze
3
from rope.refactor import functionutils, sourceutils, occurrences
4
5
6
class IntroduceParameter(object):
7
"""Introduce parameter refactoring
8
9
This refactoring adds a new parameter to a function and replaces
10
references to an expression in it with the new parameter.
11
12
The parameter finding part is different from finding similar
13
pieces in extract refactorings. In this refactoring parameters
14
are found based on the object they reference to. For instance
15
in::
16
17
class A(object):
18
var = None
19
20
class B(object):
21
a = A()
22
23
b = B()
24
a = b.a
25
26
def f(a):
27
x = b.a.var + a.var
28
29
using this refactoring on ``a.var`` with ``p`` as the new
30
parameter name, will result in::
31
32
def f(p=a.var):
33
x = p + p
34
35
"""
36
37
def __init__(self, project, resource, offset):
38
self.pycore = project.pycore
39
self.resource = resource
40
self.offset = offset
41
self.pymodule = self.pycore.resource_to_pyobject(self.resource)
42
scope = self.pymodule.get_scope().get_inner_scope_for_offset(offset)
43
if scope.get_kind() != 'Function':
44
raise exceptions.RefactoringError(
45
'Introduce parameter should be performed inside functions')
46
self.pyfunction = scope.pyobject
47
self.name, self.pyname = self._get_name_and_pyname()
48
if self.pyname is None:
49
raise exceptions.RefactoringError(
50
'Cannot find the definition of <%s>' % self.name)
51
52
def _get_primary(self):
53
word_finder = worder.Worder(self.resource.read())
54
return word_finder.get_primary_at(self.offset)
55
56
def _get_name_and_pyname(self):
57
return (worder.get_name_at(self.resource, self.offset),
58
evaluate.eval_location(self.pymodule, self.offset))
59
60
def get_changes(self, new_parameter):
61
definition_info = functionutils.DefinitionInfo.read(self.pyfunction)
62
definition_info.args_with_defaults.append((new_parameter,
63
self._get_primary()))
64
collector = codeanalyze.ChangeCollector(self.resource.read())
65
header_start, header_end = self._get_header_offsets()
66
body_start, body_end = sourceutils.get_body_region(self.pyfunction)
67
collector.add_change(header_start, header_end,
68
definition_info.to_string())
69
self._change_function_occurances(collector, body_start,
70
body_end, new_parameter)
71
changes = rope.base.change.ChangeSet('Introduce parameter <%s>' %
72
new_parameter)
73
change = rope.base.change.ChangeContents(self.resource,
74
collector.get_changed())
75
changes.add_change(change)
76
return changes
77
78
def _get_header_offsets(self):
79
lines = self.pymodule.lines
80
start_line = self.pyfunction.get_scope().get_start()
81
end_line = self.pymodule.logical_lines.\
82
logical_line_in(start_line)[1]
83
start = lines.get_line_start(start_line)
84
end = lines.get_line_end(end_line)
85
start = self.pymodule.source_code.find('def', start) + 4
86
end = self.pymodule.source_code.rfind(':', start, end)
87
return start, end
88
89
def _change_function_occurances(self, collector, function_start,
90
function_end, new_name):
91
finder = occurrences.create_finder(self.pycore, self.name, self.pyname)
92
for occurrence in finder.find_occurrences(resource=self.resource):
93
start, end = occurrence.get_primary_range()
94
if function_start <= start < function_end:
95
collector.add_change(start, end, new_name)
96
97