935 lines
25 KiB
Python
935 lines
25 KiB
Python
"""Integration with Rope library."""
|
|
|
|
from __future__ import absolute_import, print_function
|
|
|
|
import os.path
|
|
import re
|
|
import site
|
|
import sys
|
|
|
|
from rope.base import project, libutils, exceptions, change, worder, pycore
|
|
from rope.base.fscommands import FileSystemCommands # noqa
|
|
from rope.base.taskhandle import TaskHandle # noqa
|
|
from rope.contrib import autoimport as rope_autoimport, codeassist, findit, generate # noqa
|
|
from rope.refactor import ModuleToPackage, ImportOrganizer, rename, extract, inline, usefunction, move, change_signature, importutils # noqa
|
|
|
|
from .environment import env
|
|
|
|
|
|
def look_ropeproject(path):
|
|
"""Search for ropeproject in current and parent dirs.
|
|
|
|
:return str|None: A finded path
|
|
|
|
"""
|
|
env.debug('Look project', path)
|
|
p = os.path.abspath(path)
|
|
|
|
while True:
|
|
if '.ropeproject' in os.listdir(p):
|
|
return p
|
|
|
|
new_p = os.path.abspath(os.path.join(p, ".."))
|
|
if new_p == p:
|
|
return path
|
|
|
|
p = new_p
|
|
|
|
|
|
@env.catch_exceptions
|
|
def completions():
|
|
""" Search completions.
|
|
|
|
:return None:
|
|
|
|
"""
|
|
row, col = env.cursor
|
|
if env.var('a:findstart', True):
|
|
count = 0
|
|
for char in reversed(env.current.line[:col]):
|
|
if not re.match(r'[\w\d]', char):
|
|
break
|
|
count += 1
|
|
env.debug('Complete find start', (col - count))
|
|
return env.stop(col - count)
|
|
|
|
base = env.var('a:base')
|
|
source, offset = env.get_offset_params((row, col), base)
|
|
proposals = get_proporsals(source, offset, base)
|
|
return env.stop(proposals)
|
|
|
|
|
|
FROM_RE = re.compile(r'^\s*from\s+[\.\w\d_]+$')
|
|
|
|
|
|
@env.catch_exceptions
|
|
def complete(dot=False):
|
|
""" Ctrl+Space completion.
|
|
|
|
:return bool: success
|
|
|
|
"""
|
|
row, col = env.cursor
|
|
source, offset = env.get_offset_params()
|
|
|
|
cline = env.current.line[:col]
|
|
env.debug('dot completion', cline)
|
|
if FROM_RE.match(cline) or cline.endswith('..') or cline.endswith('\.'): # noqa
|
|
return env.stop("")
|
|
|
|
proposals = get_proporsals(source, offset, dot=dot)
|
|
if not proposals:
|
|
return False
|
|
|
|
prefix = proposals[0]['word']
|
|
|
|
# Find common part
|
|
for p in proposals:
|
|
common = len([
|
|
c1 for c1, c2 in zip(prefix, p['word']) if c1 == c2 and c1 != ' '
|
|
])
|
|
prefix = prefix[:common]
|
|
s_offset = codeassist.starting_offset(source, offset)
|
|
p_prefix = prefix[offset - s_offset:]
|
|
line = env.lines[row - 1]
|
|
cline = line[:col] + p_prefix + line[col:]
|
|
if cline != line:
|
|
env.curbuf[row - 1] = env.prepare_value(cline, dumps=False)
|
|
env.current.window.cursor = (row, col + len(p_prefix))
|
|
env.run('complete', col - len(prefix) + len(p_prefix) + 1, proposals)
|
|
return True
|
|
|
|
|
|
def get_proporsals(source, offset, base='', dot=False):
|
|
""" Code assist.
|
|
|
|
:return str:
|
|
|
|
"""
|
|
with RopeContext() as ctx: # noqa
|
|
|
|
try:
|
|
proposals = codeassist.code_assist(
|
|
ctx.project, source, offset, ctx.resource, maxfixes=3,
|
|
later_locals=False)
|
|
|
|
except exceptions.ModuleSyntaxError:
|
|
proposals = []
|
|
|
|
proposals = sorted(proposals, key=_sort_proporsals)
|
|
|
|
out = []
|
|
preview = 'preview' in ctx.options.get('completeopt')
|
|
for p in proposals:
|
|
out.append(dict(
|
|
word=p.name,
|
|
menu=p.type,
|
|
kind=p.scope + ':',
|
|
info=p.get_doc() or "No docs." if preview else "",
|
|
))
|
|
|
|
out = _get_autoimport_proposals(out, ctx, source, offset, dot=dot)
|
|
|
|
return out
|
|
|
|
|
|
@env.catch_exceptions
|
|
def goto():
|
|
""" Goto definition. """
|
|
with RopeContext() as ctx:
|
|
source, offset = env.get_offset_params()
|
|
|
|
found_resource, line = codeassist.get_definition_location(
|
|
ctx.project, source, offset, ctx.resource, maxfixes=3)
|
|
|
|
if not found_resource:
|
|
env.error('Definition not found')
|
|
return
|
|
|
|
env.goto_file(
|
|
found_resource.real_path,
|
|
cmd=ctx.options.get('goto_definition_cmd'))
|
|
env.goto_line(line)
|
|
|
|
|
|
@env.catch_exceptions
|
|
def show_doc():
|
|
""" Show documentation. """
|
|
with RopeContext() as ctx:
|
|
source, offset = env.get_offset_params()
|
|
try:
|
|
doc = codeassist.get_doc(
|
|
ctx.project, source, offset, ctx.resource, maxfixes=3)
|
|
if not doc:
|
|
raise exceptions.BadIdentifierError
|
|
env.let('l:output', doc.split('\n'))
|
|
except exceptions.BadIdentifierError:
|
|
env.error("No documentation found.")
|
|
|
|
|
|
def find_it():
|
|
""" Find occurrences. """
|
|
with RopeContext() as ctx:
|
|
_, offset = env.get_offset_params()
|
|
try:
|
|
occurrences = findit.find_occurrences(
|
|
ctx.project, ctx.resource, offset)
|
|
except exceptions.BadIdentifierError:
|
|
occurrences = []
|
|
|
|
lst = []
|
|
for oc in occurrences:
|
|
lst.append(dict(
|
|
filename=oc.resource.path,
|
|
text=env.lines[oc.lineno - 1] if oc.resource.real_path == env.curbuf.name else "", # noqa
|
|
lnum=oc.lineno,
|
|
))
|
|
env.let('loclist._loclist', lst)
|
|
|
|
|
|
def update_python_path(paths):
|
|
""" Update sys.path and make sure the new items come first. """
|
|
old_sys_path_items = list(sys.path)
|
|
|
|
for path in paths:
|
|
# see if it is a site dir
|
|
if path.find('site-packages') != -1:
|
|
site.addsitedir(path)
|
|
|
|
else:
|
|
sys.path.insert(0, path)
|
|
|
|
# Reorder sys.path so new directories at the front.
|
|
new_sys_path_items = set(sys.path) - set(old_sys_path_items)
|
|
sys.path = list(new_sys_path_items) + old_sys_path_items
|
|
|
|
|
|
def organize_imports():
|
|
""" Organize imports in current file. """
|
|
with RopeContext() as ctx:
|
|
organizer = ImportOrganizer(ctx.project)
|
|
changes = organizer.organize_imports(ctx.resource)
|
|
if changes is not None:
|
|
progress = ProgressHandler('Organize imports')
|
|
ctx.project.do(changes, task_handle=progress.handle)
|
|
reload_changes(changes)
|
|
|
|
|
|
@env.catch_exceptions
|
|
def regenerate():
|
|
""" Clear cache. """
|
|
with RopeContext() as ctx:
|
|
ctx.project.pycore._invalidate_resource_cache(ctx.resource) # noqa
|
|
ctx.importer.generate_cache()
|
|
ctx.project.sync()
|
|
|
|
|
|
def new():
|
|
""" Create a new project. """
|
|
root = None
|
|
if env.var('a:0') != '0':
|
|
root = env.var('a:1')
|
|
else:
|
|
default = env.var('g:pymode_rope_project_root')
|
|
if not default:
|
|
default = env.var('getcwd()')
|
|
root = env.var('input("Enter project root: ", "%s")' % default)
|
|
ropefolder = env.var('g:pymode_rope_ropefolder')
|
|
prj = project.Project(projectroot=root, ropefolder=ropefolder)
|
|
prj.close()
|
|
env.message("Project is opened: %s" % root)
|
|
|
|
|
|
def undo():
|
|
""" Undo last changes.
|
|
|
|
:return bool:
|
|
|
|
"""
|
|
with RopeContext() as ctx:
|
|
changes = ctx.project.history.tobe_undone
|
|
if changes is None:
|
|
env.error('Nothing to undo!')
|
|
return False
|
|
|
|
if env.user_confirm('Undo [%s]?' % str(changes)):
|
|
progress = ProgressHandler('Undo %s' % str(changes))
|
|
for c in ctx.project.history.undo(task_handle=progress.handle):
|
|
reload_changes(c)
|
|
|
|
|
|
def redo():
|
|
""" Redo last changes.
|
|
|
|
:return bool:
|
|
|
|
"""
|
|
with RopeContext() as ctx:
|
|
changes = ctx.project.history.tobe_redone
|
|
if changes is None:
|
|
env.error('Nothing to redo!')
|
|
return False
|
|
|
|
if env.user_confirm('Redo [%s]?' % str(changes)):
|
|
progress = ProgressHandler('Redo %s' % str(changes))
|
|
for c in ctx.project.history.redo(task_handle=progress.handle):
|
|
reload_changes(c)
|
|
|
|
|
|
def cache_project(cls):
|
|
""" Cache projects.
|
|
|
|
:return func:
|
|
|
|
"""
|
|
projects = dict()
|
|
resources = dict()
|
|
|
|
def get_ctx(*args, **kwargs):
|
|
path = env.curbuf.name
|
|
if resources.get(path):
|
|
return resources.get(path)
|
|
|
|
project_path = env.var('g:pymode_rope_project_root')
|
|
if not project_path:
|
|
project_path = env.curdir
|
|
env.debug('Look ctx', project_path)
|
|
if env.var('g:pymode_rope_lookup_project', True):
|
|
project_path = look_ropeproject(project_path)
|
|
|
|
if not os.path.exists(project_path):
|
|
env.error("Rope project root not exist: %s" % project_path)
|
|
ctx = None
|
|
|
|
else:
|
|
ctx = projects.get(project_path)
|
|
|
|
if not ctx:
|
|
projects[project_path] = ctx = cls(path, project_path)
|
|
|
|
resources[path] = ctx
|
|
return ctx
|
|
return get_ctx
|
|
|
|
|
|
def autoimport():
|
|
""" Autoimport modules.
|
|
|
|
:return bool:
|
|
|
|
"""
|
|
word = env.var('a:word')
|
|
if not word:
|
|
env.error("Should be word under cursor.")
|
|
return False
|
|
|
|
with RopeContext() as ctx:
|
|
if not ctx.importer.names:
|
|
ctx.generate_autoimport_cache()
|
|
modules = ctx.importer.get_modules(word)
|
|
if not modules:
|
|
env.message('Global name %s not found.' % word)
|
|
return False
|
|
|
|
if len(modules) == 1:
|
|
_insert_import(word, modules[0], ctx)
|
|
|
|
else:
|
|
module = env.user_input_choices(
|
|
'Which module to import:', *modules)
|
|
_insert_import(word, module, ctx)
|
|
|
|
return True
|
|
|
|
|
|
@cache_project
|
|
class RopeContext(object):
|
|
|
|
""" A context manager to have a rope project context. """
|
|
|
|
projects = {}
|
|
resource = {}
|
|
|
|
def __init__(self, path=None, project_path=None):
|
|
""" Init Rope context. """
|
|
self.path = path
|
|
|
|
self.project = project.Project(project_path, fscommands=FileSystemCommands())
|
|
|
|
self.importer = rope_autoimport.AutoImport(
|
|
project=self.project, observe=False)
|
|
|
|
update_python_path(self.project.prefs.get('python_path', []))
|
|
|
|
self.resource = None
|
|
self.current = None
|
|
self.options = dict(
|
|
completeopt=env.var('&completeopt'),
|
|
autoimport=env.var('g:pymode_rope_autoimport', True),
|
|
autoimport_modules=env.var('g:pymode_rope_autoimport_modules'),
|
|
goto_definition_cmd=env.var('g:pymode_rope_goto_definition_cmd'),
|
|
)
|
|
|
|
if os.path.exists("%s/__init__.py" % project_path):
|
|
sys.path.append(project_path)
|
|
|
|
if self.options.get('autoimport'):
|
|
self.generate_autoimport_cache()
|
|
|
|
env.debug('Context init', project_path)
|
|
env.message('Init Rope project: %s' % project_path)
|
|
|
|
def __enter__(self):
|
|
""" Enter to Rope ctx. """
|
|
env.let('g:pymode_rope_current', self.project.root.real_path)
|
|
self.project.validate(self.project.root)
|
|
self.resource = libutils.path_to_resource(
|
|
self.project, env.curbuf.name, 'file')
|
|
|
|
if not self.resource.exists() or os.path.isdir(
|
|
self.resource.real_path):
|
|
self.resource = None
|
|
else:
|
|
env.debug('Found resource', self.resource.path)
|
|
|
|
return self
|
|
|
|
def __exit__(self, t, value, traceback):
|
|
""" Exit from Rope ctx. """
|
|
if t is None:
|
|
self.project.close()
|
|
|
|
def generate_autoimport_cache(self):
|
|
""" Update autoimport cache. """
|
|
env.message('Regenerate autoimport cache.')
|
|
modules = self.options.get('autoimport_modules', [])
|
|
|
|
def _update_cache(importer, modules=None):
|
|
importer.generate_cache()
|
|
if modules:
|
|
importer.generate_modules_cache(modules)
|
|
importer.project.sync()
|
|
|
|
_update_cache(self.importer, modules)
|
|
|
|
|
|
class ProgressHandler(object):
|
|
|
|
""" Handle task progress. """
|
|
|
|
def __init__(self, msg):
|
|
""" Init progress handler. """
|
|
self.handle = TaskHandle(name="refactoring_handle")
|
|
self.handle.add_observer(self)
|
|
self.message = msg
|
|
|
|
def __call__(self):
|
|
""" Show current progress. """
|
|
percent_done = self.handle.current_jobset().get_percent_done()
|
|
env.message('%s - done %s%%' % (self.message, percent_done))
|
|
|
|
|
|
_scope_weight = {
|
|
'local': 10, 'attribute': 20, 'global': 30, 'imported': 40, 'builtin': 50}
|
|
|
|
|
|
def _sort_proporsals(p):
|
|
return (
|
|
_scope_weight.get(p.scope, 100), int(p.name.startswith('_')), p.name)
|
|
|
|
|
|
class Refactoring(object): # noqa
|
|
|
|
""" Base class for refactor operations. """
|
|
|
|
def run(self):
|
|
""" Run refactoring.
|
|
|
|
:return bool:
|
|
|
|
"""
|
|
with RopeContext() as ctx:
|
|
|
|
if not ctx.resource:
|
|
env.error("You should save the file before refactoring.")
|
|
return None
|
|
|
|
try:
|
|
env.message(self.__doc__)
|
|
refactor = self.get_refactor(ctx)
|
|
input_str = self.get_input_str(refactor, ctx)
|
|
if not input_str:
|
|
return False
|
|
|
|
action = env.user_input_choices(
|
|
'Choose what to do:', 'perform', 'preview',
|
|
'perform in class hierarchy',
|
|
'preview in class hierarchy')
|
|
|
|
in_hierarchy = action.endswith("in class hierarchy")
|
|
|
|
changes = self.get_changes(refactor, input_str, in_hierarchy)
|
|
|
|
if not action:
|
|
return False
|
|
|
|
if action.startswith('preview'):
|
|
print("\n ")
|
|
print("-------------------------------")
|
|
print("\n%s\n" % changes.get_description())
|
|
print("-------------------------------\n\n")
|
|
if not env.user_confirm('Do the changes?'):
|
|
return False
|
|
|
|
progress = ProgressHandler('Apply changes ...')
|
|
ctx.project.do(changes, task_handle=progress.handle)
|
|
reload_changes(changes)
|
|
except exceptions.RefactoringError as e:
|
|
env.error(str(e))
|
|
|
|
except Exception as e: # noqa
|
|
env.error('Unhandled exception in Pymode: %s' % e)
|
|
|
|
@staticmethod
|
|
def get_refactor(ctx):
|
|
""" Get refactor object. """
|
|
raise NotImplementedError
|
|
|
|
@staticmethod
|
|
def get_input_str(refactor, ctx):
|
|
""" Get user input. Skip by default.
|
|
|
|
:return bool: True
|
|
|
|
"""
|
|
return True
|
|
|
|
@staticmethod
|
|
def get_changes(refactor, input_str, in_hierarchy=False):
|
|
return refactor.get_changes(input_str)
|
|
|
|
|
|
class RenameRefactoring(Refactoring):
|
|
|
|
""" Rename var/function/method/class. """
|
|
|
|
def __init__(self, module=False):
|
|
self.module = module
|
|
super(RenameRefactoring, self).__init__()
|
|
|
|
def get_refactor(self, ctx):
|
|
""" Function description.
|
|
|
|
:return Rename:
|
|
|
|
"""
|
|
offset = None
|
|
if not self.module:
|
|
_, offset = env.get_offset_params()
|
|
env.debug('Prepare rename', offset)
|
|
return rename.Rename(ctx.project, ctx.resource, offset)
|
|
|
|
def get_input_str(self, refactor, ctx):
|
|
""" Return user input. """
|
|
|
|
oldname = str(refactor.get_old_name())
|
|
msg = 'Renaming method/variable. New name:'
|
|
if self.module:
|
|
msg = 'Renaming module. New name:'
|
|
newname = env.user_input(msg, oldname)
|
|
|
|
if newname == oldname:
|
|
env.message("Nothing to do.")
|
|
return False
|
|
|
|
return newname
|
|
|
|
@staticmethod
|
|
def get_changes(refactor, input_str, in_hierarchy=False):
|
|
""" Get changes.
|
|
|
|
:return Changes:
|
|
|
|
"""
|
|
return refactor.get_changes(input_str, in_hierarchy=in_hierarchy)
|
|
|
|
|
|
class ExtractMethodRefactoring(Refactoring):
|
|
|
|
""" Extract method. """
|
|
|
|
@staticmethod
|
|
def get_input_str(refactor, ctx):
|
|
""" Return user input. """
|
|
|
|
return env.user_input('New method name:')
|
|
|
|
@staticmethod
|
|
def get_refactor(ctx):
|
|
""" Function description.
|
|
|
|
:return Rename:
|
|
|
|
"""
|
|
cursor1, cursor2 = env.curbuf.mark('<'), env.curbuf.mark('>')
|
|
_, offset1 = env.get_offset_params(cursor1)
|
|
_, offset2 = env.get_offset_params(cursor2)
|
|
return extract.ExtractMethod(
|
|
ctx.project, ctx.resource, offset1, offset2)
|
|
|
|
|
|
class ExtractVariableRefactoring(Refactoring):
|
|
|
|
""" Extract variable. """
|
|
|
|
@staticmethod
|
|
def get_input_str(refactor, ctx):
|
|
""" Return user input. """
|
|
|
|
return env.user_input('New variable name:')
|
|
|
|
@staticmethod
|
|
def get_refactor(ctx):
|
|
""" Function description.
|
|
|
|
:return Rename:
|
|
|
|
"""
|
|
cursor1, cursor2 = env.curbuf.mark('<'), env.curbuf.mark('>')
|
|
_, offset1 = env.get_offset_params(cursor1)
|
|
_, offset2 = env.get_offset_params(cursor2)
|
|
return extract.ExtractVariable(
|
|
ctx.project, ctx.resource, offset1, offset2)
|
|
|
|
|
|
class InlineRefactoring(Refactoring):
|
|
|
|
""" Inline variable/method. """
|
|
|
|
@staticmethod
|
|
def get_refactor(ctx):
|
|
""" Function description.
|
|
|
|
:return Rename:
|
|
|
|
"""
|
|
_, offset = env.get_offset_params()
|
|
return inline.create_inline(ctx.project, ctx.resource, offset)
|
|
|
|
@staticmethod
|
|
def get_changes(refactor, input_str, in_hierarchy=False):
|
|
""" Get changes.
|
|
|
|
:return Changes:
|
|
|
|
"""
|
|
return refactor.get_changes()
|
|
|
|
|
|
class UseFunctionRefactoring(Refactoring):
|
|
|
|
""" Use selected function as possible. """
|
|
|
|
@staticmethod
|
|
def get_refactor(ctx):
|
|
""" Function description.
|
|
|
|
:return Rename:
|
|
|
|
"""
|
|
_, offset = env.get_offset_params()
|
|
return usefunction.UseFunction(ctx.project, ctx.resource, offset)
|
|
|
|
@staticmethod
|
|
def get_changes(refactor, input_str, in_hierarchy=False):
|
|
""" Get changes.
|
|
|
|
:return Changes:
|
|
|
|
"""
|
|
return refactor.get_changes()
|
|
|
|
|
|
class ModuleToPackageRefactoring(Refactoring):
|
|
|
|
""" Convert module to package. """
|
|
|
|
@staticmethod
|
|
def get_refactor(ctx):
|
|
""" Function description.
|
|
|
|
:return Rename:
|
|
|
|
"""
|
|
return ModuleToPackage(ctx.project, ctx.resource)
|
|
|
|
@staticmethod
|
|
def get_changes(refactor, input_str, in_hierarchy=False):
|
|
""" Get changes.
|
|
|
|
:return Changes:
|
|
|
|
"""
|
|
return refactor.get_changes()
|
|
|
|
|
|
class MoveRefactoring(Refactoring):
|
|
|
|
""" Move method/module to other class/global. """
|
|
|
|
@staticmethod
|
|
def get_input_str(refactor, ctx):
|
|
""" Get destination.
|
|
|
|
:return str:
|
|
|
|
"""
|
|
|
|
return env.user_input('Enter destination:')
|
|
|
|
@staticmethod
|
|
def get_refactor(ctx):
|
|
""" Function description.
|
|
|
|
:return Rename:
|
|
|
|
"""
|
|
_, offset = env.get_offset_params()
|
|
if offset == 0:
|
|
offset = None
|
|
return move.create_move(ctx.project, ctx.resource, offset)
|
|
|
|
|
|
class ChangeSignatureRefactoring(Refactoring):
|
|
|
|
""" Change function signature (add/remove/sort arguments). """
|
|
|
|
@staticmethod
|
|
def get_input_str(refactor, ctx):
|
|
""" Get destination.
|
|
|
|
:return str:
|
|
|
|
"""
|
|
args = refactor.get_args()
|
|
default = ', '.join(a[0] for a in args)
|
|
return env.user_input('Change the signature:', default)
|
|
|
|
@staticmethod
|
|
def get_refactor(ctx):
|
|
""" Function description.
|
|
|
|
:return Rename:
|
|
|
|
"""
|
|
_, offset = env.get_offset_params()
|
|
return change_signature.ChangeSignature(
|
|
ctx.project, ctx.resource, offset)
|
|
|
|
def get_changes(self, refactor, input_string, in_hierarchy=False):
|
|
""" Function description.
|
|
|
|
:return Rope.changes:
|
|
|
|
"""
|
|
args = re.sub(r'[\s\(\)]+', '', input_string).split(',')
|
|
olds = [arg[0] for arg in refactor.get_args()]
|
|
|
|
changers = []
|
|
for arg in [a for a in olds if a not in args]:
|
|
changers.append(change_signature.ArgumentRemover(olds.index(arg)))
|
|
olds.remove(arg)
|
|
|
|
order = []
|
|
for index, arg in enumerate(args):
|
|
if arg not in olds:
|
|
changers.append(change_signature.ArgumentAdder(index, arg))
|
|
olds.insert(index, arg)
|
|
order.append(olds.index(arg))
|
|
|
|
changers.append(change_signature.ArgumentReorderer(
|
|
order, autodef='None'))
|
|
|
|
return refactor.get_changes(changers, in_hierarchy=in_hierarchy)
|
|
|
|
|
|
class GenerateElementRefactoring(Refactoring):
|
|
|
|
""" Class description. """
|
|
|
|
def __init__(self, kind, *args, **kwargs):
|
|
""" Function description. """
|
|
self.kind = kind
|
|
super(GenerateElementRefactoring, self).__init__(*args, **kwargs)
|
|
|
|
def get_refactor(self, ctx):
|
|
""" Function description.
|
|
|
|
:return Rename:
|
|
|
|
"""
|
|
_, offset = env.get_offset_params()
|
|
return generate.create_generate(
|
|
self.kind, ctx.project, ctx.resource, offset)
|
|
|
|
def get_changes(self, refactor, input_str, in_hierarchy=False):
|
|
""" Function description.
|
|
|
|
:return Rope.changes:
|
|
|
|
"""
|
|
|
|
return refactor.get_changes()
|
|
|
|
|
|
@env.catch_exceptions
|
|
def reload_changes(changes):
|
|
""" Reload changed buffers. """
|
|
|
|
resources = changes.get_changed_resources()
|
|
moved = _get_moved_resources(changes) # noqa
|
|
current = env.curbuf.number
|
|
|
|
for f in resources:
|
|
bufnr = env.var('bufnr("%s")' % f.real_path)
|
|
env.goto_buffer(bufnr)
|
|
|
|
path = env.curbuf.name
|
|
if f in moved:
|
|
path = moved[f].real_path
|
|
|
|
env.debug('Reload', f.real_path, path, bufnr)
|
|
env.goto_file(path, 'e!', force=True)
|
|
env.message("%s has been changed." % f.real_path, history=True)
|
|
|
|
env.goto_buffer(current)
|
|
|
|
|
|
def _get_moved_resources(changes):
|
|
|
|
moved = dict()
|
|
|
|
if isinstance(changes, change.ChangeSet):
|
|
for c in changes.changes:
|
|
moved.update(_get_moved_resources(c))
|
|
|
|
if isinstance(changes, change.MoveResource):
|
|
moved[changes.resource] = changes.new_resource
|
|
|
|
return moved
|
|
|
|
|
|
def _get_autoimport_proposals(out, ctx, source, offset, dot=False):
|
|
|
|
if not ctx.options.get('autoimport') or dot:
|
|
return out
|
|
|
|
if '.' in codeassist.starting_expression(source, offset):
|
|
return out
|
|
|
|
current_offset = offset - 1
|
|
while current_offset > 0 and (
|
|
source[current_offset].isalnum() or source[current_offset] == '_'):
|
|
current_offset -= 1
|
|
starting = source[current_offset:offset]
|
|
starting = starting.strip()
|
|
|
|
if not starting:
|
|
return out
|
|
|
|
for assist in ctx.importer.import_assist(starting):
|
|
out.append(dict(
|
|
abbr=' : '.join(assist),
|
|
word=assist[0],
|
|
kind='autoimport:',
|
|
))
|
|
|
|
return out
|
|
|
|
|
|
@env.catch_exceptions
|
|
def complete_check():
|
|
""" Function description.
|
|
|
|
:return bool:
|
|
|
|
"""
|
|
|
|
row, column = env.cursor
|
|
line = env.lines[row - 1]
|
|
word_finder = worder.Worder(line, True)
|
|
parent, name, _ = word_finder.get_splitted_primary_before(column - 1)
|
|
if parent:
|
|
return False
|
|
|
|
with RopeContext() as ctx:
|
|
modules = ctx.importer.get_modules(name)
|
|
|
|
if not modules:
|
|
return False
|
|
|
|
if name in ctx.project.pycore.resource_to_pyobject(ctx.resource):
|
|
return False
|
|
|
|
if not env.user_confirm("Import %s?" % name, True):
|
|
return False
|
|
|
|
if len(modules) == 1:
|
|
_insert_import(name, modules[0], ctx)
|
|
|
|
else:
|
|
module = env.user_input_choices('With module to import:', *modules)
|
|
if module:
|
|
_insert_import(name, module, ctx)
|
|
|
|
|
|
def _insert_import(name, module, ctx):
|
|
if not ctx.resource:
|
|
source, _ = env.get_offset_params()
|
|
lineno = ctx.importer.find_insertion_line(source)
|
|
line = 'from %s import %s' % (module, name)
|
|
env.curbuf[lineno - 1:lineno - 1] = [
|
|
env.prepare_value(line, dumps=False)]
|
|
return True
|
|
|
|
pyobject = ctx.project.pycore.resource_to_pyobject(ctx.resource)
|
|
import_tools = importutils.ImportTools(ctx.project.pycore)
|
|
module_imports = import_tools.module_imports(pyobject)
|
|
new_import = importutils.FromImport(module, 0, [[name, None]])
|
|
module_imports.add_import(new_import)
|
|
changes = change.ChangeContents(
|
|
ctx.resource, module_imports.get_changed_source())
|
|
|
|
action = env.user_input_choices(
|
|
'Choose what to do:', 'perform', 'preview')
|
|
|
|
if not action:
|
|
return False
|
|
|
|
if action == 'preview':
|
|
print("\n ")
|
|
print("-------------------------------")
|
|
print("\n%s\n" % changes.get_description())
|
|
print("-------------------------------\n\n")
|
|
if not env.user_confirm('Do the changes?'):
|
|
return False
|
|
|
|
progress = ProgressHandler('Apply changes ...')
|
|
ctx.project.do(changes, task_handle=progress.handle)
|
|
reload_changes(changes)
|
|
|
|
|
|
# Monkey patch Rope
|
|
def find_source_folders(self, folder):
|
|
"""Look only python files an packages."""
|
|
for resource in folder.get_folders():
|
|
if self._is_package(resource): # noqa
|
|
return [folder]
|
|
|
|
for resource in folder.get_files():
|
|
if resource.name.endswith('.py'):
|
|
return [folder]
|
|
|
|
return []
|
|
|
|
pycore.PyCore._find_source_folders = find_source_folders # noqa
|