--- a/vim/.vimrc Sat May 15 01:44:23 2010 -0400
+++ b/vim/.vimrc Sat May 15 23:20:42 2010 -0400
@@ -104,3 +104,9 @@
let Tlist_WinWidth = 50
map <F3> :TlistToggle<cr>
map <F4> :!/usr/local/bin/ctags -R --c++-kinds=+p --fields=+iaS --extra=+q .<CR>
+
+" Ropevim
+let $PYTHONPATH .= ":" . $HOME . "/lib/python/rope"
+let $PYTHONPATH .= ":" . $HOME . "/lib/dotfiles/vim/notreallybundles/ropevim/ropevim"
+source ~/lib/dotfiles/vim/notreallybundles/ropevim/ropevim.vim
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/notreallybundles/ropevim/docs/done.txt Sat May 15 23:20:42 2010 -0400
@@ -0,0 +1,22 @@
+======
+ Done
+======
+
+> Public Release 0.2 : October 3, 2008
+
+* fixed quickfix error format : August 18, 2008
+* ropevim_guess_project variable : June 18, 2008
+
+> Public Release 0.2c1 : June 12, 2008
+
+* not showing python traceback for bad inputs : May 23, 2008
+* interrupting refactorings : May 22, 2008
+
+> Public Release 0.1 : May 22, 2008
+
+* ropvim menu : May 21, 2008
+* code-assist in insert mode : May 21, 2008
+* using vim quickfix for showing occurrences : May 20, 2008
+* better dialogs : May 19, 2008
+* implementing basic ropemode commands : May 15, 2008
+* project started! : May 12, 2008
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/notreallybundles/ropevim/docs/ropevim.txt Sat May 15 23:20:42 2010 -0400
@@ -0,0 +1,77 @@
+======================
+ ropevim, rope in vim
+======================
+
+Ropevim is a plugin for performing python refactorings in vim. It
+uses rope_ library.
+
+You should install `rope`_ library before using ropevim. You can
+download ropevim from `project download page`_.
+
+.. _rope: http://rope.sf.net/
+
+
+Features
+========
+
+* Supports many of the refactorings that are supported by rope_
+ library:
+
+ * Rename
+ * Extract method/local variable
+ * Move class/function/module/package/method
+ * Inline method/local variable/parameter
+ * Restructuring
+ * Change signature
+ * ...
+
+* Other refactoring-related features
+
+ * Previewing refactorings
+ * Undo/redo refactorings
+ * Showing refactoring progress
+
+* Code-assists
+
+ * Code-completion
+ * Goto definition
+ * Show pydoc
+ * Find occurrences
+ * Organize imports (remove unused and duplicate imports and sort them)
+ * Generating python elements
+
+
+Source Repository
+=================
+
+The repository version needs ropemode (which was once part of
+ropemacs); in order to use the repository version of ropevim you need
+to put ropemode in your ``PYTHONPATH``. Note that ropemode is
+included in released packages.
+
+Ropevim:
+
+* repo url: http://bitbucket.org/agr/ropevim
+* snapshot: http://bitbucket.org/agr/ropevim/get/tip.gz
+
+Ropemode:
+
+* repo url: http://bitbucket.org/agr/ropemode
+* snapshot: http://bitbucket.org/agr/ropemode/get/tip.gz
+
+
+Feedback
+========
+
+Send your bug reports, feature requests and patches to `rope-dev (at)
+googlegroups.com`_.
+
+
+License
+=======
+
+Ropevim is under the terms of GNU GPL (GNU General Public License).
+
+.. _project download page: http://sf.net/projects/rope/files
+.. _`rope-dev (at) googlegroups.com`: http://groups.google.com/group/rope-dev
+.. _Mercurial: http://selenic.com/mercurial
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/notreallybundles/ropevim/docs/todo.txt Sat May 15 23:20:42 2010 -0400
@@ -0,0 +1,6 @@
+==============
+ Ropevim TODO
+==============
+
+* better progress bar
+* prefixing functions
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/notreallybundles/ropevim/ropevim.vim Sat May 15 23:20:42 2010 -0400
@@ -0,0 +1,7 @@
+function! LoadRope()
+python << EOF
+import ropevim
+EOF
+endfunction
+
+call LoadRope()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/notreallybundles/ropevim/ropevim/ropemode/__init__.py Sat May 15 23:20:42 2010 -0400
@@ -0,0 +1,14 @@
+"""ropemode, a helper for using rope refactoring library in IDEs"""
+
+COPYRIGHT = """\
+Copyright (C) 2007-2008 Ali Gholami Rudi
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of GNU General Public License as published by the
+Free Software Foundation; either version 2 of the license, or (at your
+opinion) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details."""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/notreallybundles/ropevim/ropevim/ropemode/decorators.py Sat May 15 23:20:42 2010 -0400
@@ -0,0 +1,98 @@
+import traceback
+
+from rope.base import exceptions
+
+
+class Logger(object):
+
+ message = None
+ only_short = False
+
+ def __call__(self, message, short=None):
+ if short is None or not self.only_short:
+ self._show(message)
+ if short is not None:
+ self._show(short)
+
+ def _show(self, message):
+ if message is None:
+ print message
+ else:
+ self.message(message)
+
+logger = Logger()
+
+
+def lisphook(func):
+ def newfunc(*args, **kwds):
+ try:
+ func(*args, **kwds)
+ except Exception, e:
+ trace = str(traceback.format_exc())
+ short = 'Ignored an exception in ropemode hook: %s' % \
+ _exception_message(e)
+ logger(trace, short)
+ newfunc.lisp = None
+ newfunc.__name__ = func.__name__
+ newfunc.__doc__ = func.__doc__
+ return newfunc
+
+
+def lispfunction(func):
+ func.lisp = None
+ return func
+
+
+input_exceptions = (exceptions.RefactoringError,
+ exceptions.ModuleSyntaxError,
+ exceptions.BadIdentifierError)
+
+def _exception_handler(func):
+ def newfunc(*args, **kwds):
+ try:
+ func(*args, **kwds)
+ except exceptions.RopeError, e:
+ short = None
+ if isinstance(e, input_exceptions):
+ short = _exception_message(e)
+ logger(str(traceback.format_exc()), short)
+ newfunc.__name__ = func.__name__
+ newfunc.__doc__ = func.__doc__
+ return newfunc
+
+def _exception_message(e):
+ return '%s: %s' % (e.__class__.__name__, str(e))
+
+def rope_hook(hook):
+ def decorator(func):
+ func = lisphook(func)
+ func.name = func.__name__
+ func.kind = 'hook'
+ func.hook = hook
+ return func
+ return decorator
+
+
+def local_command(key=None, prefix=False, shortcut=None, name=None):
+ def decorator(func, name=name):
+ func = _exception_handler(func)
+ func.kind = 'local'
+ func.prefix = prefix
+ func.local_key = key
+ func.shortcut_key = shortcut
+ if name is None:
+ name = func.__name__
+ func.name = name
+ return func
+ return decorator
+
+
+def global_command(key=None, prefix=False):
+ def decorator(func):
+ func = _exception_handler(func)
+ func.kind = 'global'
+ func.prefix = prefix
+ func.global_key = key
+ func.name = func.__name__
+ return func
+ return decorator
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/notreallybundles/ropevim/ropevim/ropemode/dialog.py Sat May 15 23:20:42 2010 -0400
@@ -0,0 +1,97 @@
+class Data(object):
+
+ def __init__(self, prompt=None, default=None, values=None,
+ kind=None, decode=None):
+ self.prompt = prompt
+ self.default = default
+ self.values = values
+ self.kind = kind
+ self._decode = decode
+
+ def decode(self, value):
+ if self._decode:
+ return self._decode(value)
+ return value
+
+
+class Boolean(Data):
+
+ def __init__(self, prompt=None, default=False):
+ Data.__init__(self, prompt, self._encode(default),
+ [self._encode(True), self._encode(False)])
+
+ def _encode(self, value):
+ if value:
+ return 'yes'
+ return 'no'
+
+ def decode(self, value):
+ if value.lower() in ('yes', '1', 'true'):
+ return True
+ return False
+
+
+def show_dialog(askdata, actions, confs={}, optionals={}, initial_asking=True):
+ result = {}
+ if initial_asking:
+ for name, conf in confs.items():
+ result[name] = askdata(conf)
+ actions.append('batchset')
+ names = list(actions)
+ names.extend(optionals.keys())
+ names.extend(confs.keys())
+ base_question = Data('Choose what to do: ',
+ default=actions[0], values=names)
+ batchset_question = Data('Batch sets: ')
+ while True:
+ response = askdata(base_question)
+ if response == '':
+ response = base_question.default
+ elif response == 'batchset':
+ sets = askdata(batchset_question)
+ for key, value in _parse_batchset(sets).items():
+ if key.endswith(':'):
+ key = key[:-1]
+ if key in names:
+ conf = confs.get(key, optionals.get(key))
+ result[key] = value
+ elif response in actions:
+ break
+ else:
+ if response in confs:
+ conf = confs[response]
+ else:
+ conf = optionals[response]
+ oldvalue = result.get(response, None)
+ result[response] = askdata(conf, starting=oldvalue)
+ decoded = {}
+ all_confs = dict(confs)
+ all_confs.update(optionals)
+ for key in all_confs:
+ conf = all_confs.get(key)
+ if key in result:
+ decoded[key] = conf.decode(result[key])
+ else:
+ decoded[key] = conf.decode(conf.default)
+ return response, decoded
+
+
+def _parse_batchset(sets):
+ result = []
+ multiline = False
+ for line in sets.splitlines(True):
+ if line[0].isspace():
+ if multiline:
+ result[-1][1] += line[1:]
+ else:
+ if not line.strip():
+ continue
+ multiline= False
+ tokens = line.split(None, 1)
+ value = ''
+ if len(tokens) > 1:
+ result.append([tokens[0], tokens[1].rstrip('\r\n')])
+ else:
+ multiline = True
+ result.append([tokens[0], ''])
+ return dict(result)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/notreallybundles/ropevim/ropevim/ropemode/environment.py Sat May 15 23:20:42 2010 -0400
@@ -0,0 +1,94 @@
+class Environment(object):
+
+ def ask(self, prompt, default=None, starting=None):
+ pass
+
+ def ask_values(self, prompt, values, default=None, starting=None):
+ pass
+
+ def ask_directory(self, prompt, default=None, starting=None):
+ pass
+
+ def ask_completion(self, prompt, values, starting=None):
+ pass
+
+ def message(self, message):
+ pass
+
+ def yes_or_no(self, prompt):
+ pass
+
+ def y_or_n(self, prompt):
+ pass
+
+ def get(self, name, default=None):
+ pass
+
+ def get_offset(self):
+ pass
+
+ def get_text(self):
+ pass
+
+ def get_region(self):
+ pass
+
+ def filename(self):
+ pass
+
+ def is_modified(self):
+ pass
+
+ def goto_line(self, lineno):
+ pass
+
+ def insert_line(self, line, lineno):
+ pass
+
+ def insert(self, text):
+ pass
+
+ def delete(self, start, end):
+ pass
+
+ def filenames(self):
+ pass
+
+ def save_files(self, filenames):
+ pass
+
+ def reload_files(self, filenames, moves={}):
+ pass
+
+ def find_file(self, filename, readonly=False, other=False):
+ pass
+
+ def create_progress(self, name):
+ pass
+
+ def current_word(self):
+ pass
+
+ def push_mark(self):
+ pass
+
+ def prefix_value(self, prefix):
+ pass
+
+ def show_occurrences(self, locations):
+ pass
+
+ def show_doc(self, docs, altview=False):
+ pass
+
+ def preview_changes(self, diffs):
+ pass
+
+ def local_command(self, name, callback, key=None, prefix=False):
+ pass
+
+ def global_command(self, name, callback, key=None, prefix=False):
+ pass
+
+ def add_hook(self, name, callback, hook):
+ pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/notreallybundles/ropevim/ropevim/ropemode/filter.py Sat May 15 23:20:42 2010 -0400
@@ -0,0 +1,39 @@
+from rope.base import exceptions
+
+
+def resources(project, rules):
+ """Find python files in the `project` matching `rules`
+
+ `rules` is a multi-line `str`; each line starts with either a '+'
+ or '-'. Each '+' means include the file (or its children if it's
+ a folder) that comes after it. '-' has the same meaning for
+ exclusion.
+
+ """
+ all = set(project.pycore.get_python_files())
+ files = None
+ for line in rules.splitlines():
+ if not line.strip():
+ continue
+ first, path = (line[0], line[1:])
+ if first not in '+-':
+ continue
+ try:
+ resource = project.get_resource(path.strip())
+ except exceptions.ResourceNotFoundError:
+ continue
+ if resource.is_folder():
+ matches = set(filter(lambda item: resource.contains(item), all))
+ else:
+ matches = set([resource])
+ if first == '+':
+ if files is None:
+ files = set()
+ files.update(matches)
+ if first == '-':
+ if files is None:
+ files = set(all)
+ files -= matches
+ if files is None:
+ return all
+ return files
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/notreallybundles/ropevim/ropevim/ropemode/interface.py Sat May 15 23:20:42 2010 -0400
@@ -0,0 +1,616 @@
+import os
+
+import rope.base.change
+from rope.base import libutils
+from rope.contrib import codeassist, generate, autoimport, findit
+
+from ropemode import refactor, decorators, dialog
+
+
+class RopeMode(object):
+
+ def __init__(self, env):
+ self.project = None
+ self.old_content = None
+ self.env = env
+
+ self._prepare_refactorings()
+ self.autoimport = None
+ self._init_mode()
+
+ def init(self):
+ """Initialize rope mode"""
+
+ def _init_mode(self):
+ for attrname in dir(self):
+ attr = getattr(self, attrname)
+ if not callable(attr):
+ continue
+ kind = getattr(attr, 'kind', None)
+ if kind == 'local':
+ key = getattr(attr, 'local_key', None)
+ prefix = getattr(attr, 'prefix', None)
+ self.env.local_command(attrname, attr, key, prefix)
+ if kind == 'global':
+ key = getattr(attr, 'global_key', None)
+ prefix = getattr(attr, 'prefix', None)
+ self.env.global_command(attrname, attr, key, prefix)
+ if kind == 'hook':
+ hook = getattr(attr, 'hook', None)
+ self.env.add_hook(attrname, attr, hook)
+
+ def _prepare_refactorings(self):
+ for name in dir(refactor):
+ if not name.startswith('_') and name != 'Refactoring':
+ attr = getattr(refactor, name)
+ if isinstance(attr, type) and \
+ issubclass(attr, refactor.Refactoring):
+ refname = self._refactoring_name(attr)
+ @decorators.local_command(attr.key, 'P', None, refname)
+ def do_refactor(prefix, self=self, refactoring=attr):
+ initial_asking = prefix is None
+ refactoring(self, self.env).show(initial_asking=initial_asking)
+ setattr(self, refname, do_refactor)
+
+ def _refactoring_name(self, refactoring):
+ return refactor.refactoring_name(refactoring)
+
+ @decorators.rope_hook('before_save')
+ def before_save_actions(self):
+ if self.project is not None:
+ if not self._is_python_file(self.env.filename()):
+ return
+ resource = self._get_resource()
+ if resource.exists():
+ self.old_content = resource.read()
+ else:
+ self.old_content = ''
+
+ @decorators.rope_hook('after_save')
+ def after_save_actions(self):
+ if self.project is not None and self.old_content is not None:
+ libutils.report_change(self.project, self.env.filename(),
+ self.old_content)
+ self.old_content = None
+
+ @decorators.rope_hook('exit')
+ def exiting_actions(self):
+ if self.project is not None:
+ self.close_project()
+
+ @decorators.global_command('o')
+ def open_project(self, root=None):
+ if not root:
+ root = self.env.ask_directory('Rope project root folder: ')
+ if self.project is not None:
+ self.close_project()
+ progress = self.env.create_progress('Opening [%s] project' % root)
+ self.project = rope.base.project.Project(root)
+ if self.env.get('enable_autoimport'):
+ underlined = self.env.get('autoimport_underlineds')
+ self.autoimport = autoimport.AutoImport(self.project,
+ underlined=underlined)
+ progress.done()
+
+ @decorators.global_command('k')
+ def close_project(self):
+ if self.project is not None:
+ progress = self.env.create_progress('Closing [%s] project' %
+ self.project.address)
+ self.project.close()
+ self.project = None
+ progress.done()
+
+ @decorators.global_command()
+ def write_project(self):
+ if self.project is not None:
+ progress = self.env.create_progress(
+ 'Writing [%s] project data to disk' % self.project.address)
+ self.project.sync()
+ progress.done()
+
+ @decorators.global_command('u')
+ def undo(self):
+ self._check_project()
+ change = self.project.history.tobe_undone
+ if change is None:
+ self.env.message('Nothing to undo!')
+ return
+ if self.env.y_or_n('Undo [%s]? ' % str(change)):
+ def undo(handle):
+ for changes in self.project.history.undo(task_handle=handle):
+ self._reload_buffers(changes, undo=True)
+ refactor.runtask(self.env, undo, 'Undo refactoring',
+ interrupts=False)
+
+ @decorators.global_command('r')
+ def redo(self):
+ self._check_project()
+ change = self.project.history.tobe_redone
+ if change is None:
+ self.env.message('Nothing to redo!')
+ return
+ if self.env.y_or_n('Redo [%s]? ' % str(change)):
+ def redo(handle):
+ for changes in self.project.history.redo(task_handle=handle):
+ self._reload_buffers(changes)
+ refactor.runtask(self.env, redo, 'Redo refactoring',
+ interrupts=False)
+
+ @decorators.local_command('a g', shortcut='C-c g')
+ def goto_definition(self):
+ self._check_project()
+ resource, offset = self._get_location()
+ maxfixes = self.env.get('codeassist_maxfixes')
+ definition = codeassist.get_definition_location(
+ self.project, self._get_text(), offset, resource, maxfixes)
+ if tuple(definition) != (None, None):
+ self.env.push_mark()
+ self._goto_location(definition[0], definition[1])
+ else:
+ self.env.message('Cannot find the definition!')
+
+ @decorators.local_command('a d', 'P', 'C-c d')
+ def show_doc(self, prefix):
+ self._check_project()
+ self._base_show_doc(prefix, codeassist.get_doc)
+
+ @decorators.local_command('a c', 'P')
+ def show_calltip(self, prefix):
+ self._check_project()
+ def _get_doc(project, text, offset, *args, **kwds):
+ try:
+ offset = text.rindex('(', 0, offset) - 1
+ except ValueError:
+ return None
+ return codeassist.get_calltip(project, text, offset, *args, **kwds)
+ self._base_show_doc(prefix, _get_doc)
+
+ def _base_show_doc(self, prefix, get_doc):
+ maxfixes = self.env.get('codeassist_maxfixes')
+ text = self._get_text()
+ offset = self.env.get_offset()
+ docs = get_doc(self.project, text, offset,
+ self._get_resource(), maxfixes)
+ self.env.show_doc(docs, prefix)
+ if docs is None:
+ self.env.message('No docs avilable!')
+
+ def _get_text(self):
+ if not self.env.is_modified():
+ return self._get_resource().read()
+ return self.env.get_text()
+
+ def _base_findit(self, do_find, optionals, get_kwds):
+ self._check_project()
+ self._save_buffers()
+ resource, offset = self._get_location()
+
+ action, values = dialog.show_dialog(
+ self._askdata, ['search', 'cancel'], optionals=optionals)
+ if action == 'search':
+ kwds = get_kwds(values)
+ def calculate(handle):
+ resources = refactor._resources(self.project,
+ values.get('resources'))
+ return do_find(self.project, resource, offset,
+ resources=resources, task_handle=handle, **kwds)
+ result = refactor.runtask(self.env, calculate, 'Find Occurrences')
+ locations = [Location(location) for location in result]
+ self.env.show_occurrences(locations)
+
+ @decorators.local_command('a f', shortcut='C-c f')
+ def find_occurrences(self):
+ optionals = {
+ 'unsure': dialog.Data('Find uncertain occurrences: ',
+ default='no', values=['yes', 'no']),
+ 'resources': dialog.Data('Files to search: '),
+ 'in_hierarchy': dialog.Data(
+ 'Rename methods in class hierarchy: ',
+ default='no', values=['yes', 'no'])}
+ def get_kwds(values):
+ return {'unsure': values.get('unsure') == 'yes',
+ 'in_hierarchy': values.get('in_hierarchy') == 'yes'}
+ self._base_findit(findit.find_occurrences, optionals, get_kwds)
+
+ @decorators.local_command('a i')
+ def find_implementations(self):
+ optionals = {'resources': dialog.Data('Files to search: ')}
+ def get_kwds(values):
+ return {}
+ self._base_findit(findit.find_implementations, optionals, get_kwds)
+
+ @decorators.local_command('a /', 'P', 'M-/')
+ def code_assist(self, prefix):
+ _CodeAssist(self, self.env).code_assist(prefix)
+
+ @decorators.local_command('a ?', 'P', 'M-?')
+ def lucky_assist(self, prefix):
+ _CodeAssist(self, self.env).lucky_assist(prefix)
+
+ @decorators.local_command()
+ def auto_import(self):
+ _CodeAssist(self, self.env).auto_import()
+
+ def _check_autoimport(self):
+ self._check_project()
+ if self.autoimport is None:
+ self.env.message('autoimport is disabled; '
+ 'see `enable_autoimport\' variable')
+ return False
+ return True
+
+ @decorators.global_command()
+ def generate_autoimport_cache(self):
+ if not self._check_autoimport():
+ return
+ modules = self.env.get('autoimport_modules')
+ modnames = []
+ if modules:
+ for i in range(len(modules)):
+ modname = modules[i]
+ if not isinstance(modname, basestring):
+ modname = modname.value()
+ modnames.append(modname)
+ def generate(handle):
+ self.autoimport.generate_cache(task_handle=handle)
+ self.autoimport.generate_modules_cache(modules, task_handle=handle)
+ refactor.runtask(self.env, generate, 'Generate autoimport cache')
+
+ @decorators.global_command('f', 'P')
+ def find_file(self, prefix):
+ file = self._base_find_file(prefix)
+ if file is not None:
+ self.env.find_file(file.real_path)
+
+ @decorators.global_command('4 f', 'P')
+ def find_file_other_window(self, prefix):
+ file = self._base_find_file(prefix)
+ if file is not None:
+ self.env.find_file(file.real_path, other=True)
+
+ def _base_find_file(self, prefix):
+ self._check_project()
+ if prefix:
+ files = self.project.pycore.get_python_files()
+ else:
+ files = self.project.get_files()
+ return self._ask_file(files)
+
+ def _ask_file(self, files):
+ names = []
+ for file in files:
+ names.append('<'.join(reversed(file.path.split('/'))))
+ result = self.env.ask_values('Rope Find File: ', names)
+ if result is not None:
+ path = '/'.join(reversed(result.split('<')))
+ file = self.project.get_file(path)
+ return file
+ self.env.message('No file selected')
+
+ @decorators.local_command('a j')
+ def jump_to_global(self):
+ if not self._check_autoimport():
+ return
+ all_names = list(self.autoimport.get_all_names())
+ name = self.env.ask_values('Global name: ', all_names)
+ result = dict(self.autoimport.get_name_locations(name))
+ if len(result) == 1:
+ resource = list(result.keys())[0]
+ else:
+ resource = self._ask_file(result.keys())
+ if resource:
+ self._goto_location(resource, result[resource])
+
+ @decorators.global_command('c')
+ def project_config(self):
+ self._check_project()
+ if self.project.ropefolder is not None:
+ config = self.project.ropefolder.get_child('config.py')
+ self.env.find_file(config.real_path)
+ else:
+ self.env.message('No rope project folder found')
+
+ @decorators.global_command('n m')
+ def create_module(self):
+ def callback(sourcefolder, name):
+ return generate.create_module(self.project, name, sourcefolder)
+ self._create('module', callback)
+
+ @decorators.global_command('n p')
+ def create_package(self):
+ def callback(sourcefolder, name):
+ folder = generate.create_package(self.project, name, sourcefolder)
+ return folder.get_child('__init__.py')
+ self._create('package', callback)
+
+ @decorators.global_command('n f')
+ def create_file(self):
+ def callback(parent, name):
+ return parent.create_file(name)
+ self._create('file', callback, 'parent')
+
+ @decorators.global_command('n d')
+ def create_directory(self):
+ def callback(parent, name):
+ parent.create_folder(name)
+ self._create('directory', callback, 'parent')
+
+ @decorators.local_command()
+ def analyze_module(self):
+ """Perform static object analysis on this module"""
+ self._check_project()
+ resource = self._get_resource()
+ self.project.pycore.analyze_module(resource)
+
+ @decorators.global_command()
+ def analyze_modules(self):
+ """Perform static object analysis on all project modules"""
+ self._check_project()
+ def _analyze_modules(handle):
+ libutils.analyze_modules(self.project, task_handle=handle)
+ refactor.runtask(self.env, _analyze_modules, 'Analyze project modules')
+
+ @decorators.local_command()
+ def run_module(self):
+ """Run and perform dynamic object analysis on this module"""
+ self._check_project()
+ resource = self._get_resource()
+ process = self.project.pycore.run_module(resource)
+ try:
+ process.wait_process()
+ finally:
+ process.kill_process()
+
+ def _create(self, name, callback, parentname='source'):
+ self._check_project()
+ confs = {'name': dialog.Data(name.title() + ' name: ')}
+ parentname = parentname + 'folder'
+ optionals = {parentname: dialog.Data(
+ parentname.title() + ' Folder: ',
+ default=self.project.address, kind='directory')}
+ action, values = dialog.show_dialog(
+ self._askdata, ['perform', 'cancel'], confs, optionals)
+ if action == 'perform':
+ parent = libutils.path_to_resource(
+ self.project, values.get(parentname, self.project.address))
+ resource = callback(parent, values['name'])
+ if resource:
+ self.env.find_file(resource.real_path)
+
+ def _goto_location(self, resource, lineno):
+ if resource:
+ self.env.find_file(str(resource.real_path),
+ resource.project != self.project)
+ if lineno:
+ self.env.goto_line(lineno)
+
+ def _get_location(self):
+ resource = self._get_resource()
+ offset = self.env.get_offset()
+ return resource, offset
+
+ def _get_resource(self, filename=None):
+ if filename is None:
+ filename = self.env.filename()
+ if filename is None:
+ return
+ resource = libutils.path_to_resource(self.project, filename, 'file')
+ return resource
+
+ def _check_project(self):
+ if self.project is None:
+ if self.env.get('guess_project'):
+ self.open_project(self._guess_project())
+ else:
+ self.open_project()
+ else:
+ self.project.validate(self.project.root)
+
+ def _guess_project(self):
+ cwd = self.env.filename()
+ if cwd is not None:
+ while True:
+ ropefolder = os.path.join(cwd, '.ropeproject')
+ if os.path.exists(ropefolder) and os.path.isdir(ropefolder):
+ return cwd
+ newcwd = os.path.dirname(cwd)
+ if newcwd == cwd:
+ break
+ cwd = newcwd
+
+ def _reload_buffers(self, changes, undo=False):
+ self._reload_buffers_for_changes(
+ changes.get_changed_resources(),
+ self._get_moved_resources(changes, undo))
+
+ def _reload_buffers_for_changes(self, changed, moved={}):
+ filenames = [resource.real_path for resource in changed]
+ moved = dict([(resource.real_path, moved[resource].real_path)
+ for resource in moved])
+ self.env.reload_files(filenames, moved)
+
+ def _get_moved_resources(self, changes, undo=False):
+ result = {}
+ if isinstance(changes, rope.base.change.ChangeSet):
+ for change in changes.changes:
+ result.update(self._get_moved_resources(change))
+ if isinstance(changes, rope.base.change.MoveResource):
+ result[changes.resource] = changes.new_resource
+ if undo:
+ return dict([(value, key) for key, value in result.items()])
+ return result
+
+ def _save_buffers(self, only_current=False):
+ if only_current:
+ filenames = [self.env.filename()]
+ else:
+ filenames = self.env.filenames()
+ pythons = []
+ for filename in filenames:
+ if self._is_python_file(filename):
+ pythons.append(filename)
+ self.env.save_files(pythons)
+
+ def _is_python_file(self, path):
+ resource = self._get_resource(path)
+ return (resource is not None and
+ resource.project == self.project and
+ self.project.pycore.is_python_file(resource))
+
+ def _askdata(self, data, starting=None):
+ ask_func = self.env.ask
+ ask_args = {'prompt': data.prompt, 'starting': starting,
+ 'default': data.default}
+ if data.values:
+ ask_func = self.env.ask_values
+ ask_args['values'] = data.values
+ elif data.kind == 'directory':
+ ask_func = self.env.ask_directory
+ return ask_func(**ask_args)
+
+
+class Location(object):
+ def __init__(self, location):
+ self.location = location
+ self.filename = location.resource.real_path
+ self.offset = location.offset
+ self.note = ''
+ if location.unsure:
+ self.note = '?'
+
+ @property
+ def lineno(self):
+ if hasattr(self.location, 'lineno'):
+ return self.location.lineno
+ return self.location.resource.read().count('\n', 0, self.offset) + 1
+
+
+class _CodeAssist(object):
+
+ def __init__(self, interface, env):
+ self.interface = interface
+ self.autoimport = interface.autoimport
+ self.env = env
+ self._source = None
+ self._offset = None
+ self._starting_offset = None
+ self._starting = None
+ self._expression = None
+
+ def code_assist(self, prefix):
+ names = self._calculate_proposals()
+ if prefix is not None:
+ arg = self.env.prefix_value(prefix)
+ if arg == 0:
+ arg = len(names)
+ common_start = self._calculate_prefix(names[:arg])
+ self.env.insert(common_start[self.offset - self.starting_offset:])
+ self._starting = common_start
+ self._offset = self.starting_offset + len(common_start)
+ prompt = 'Completion for %s: ' % self.expression
+ result = self.env.ask_completion(prompt, names, self.starting)
+ if result is not None:
+ self._apply_assist(result)
+
+ def lucky_assist(self, prefix):
+ names = self._calculate_proposals()
+ selected = 0
+ if prefix is not None:
+ selected = self.env.prefix_value(prefix)
+ if 0 <= selected < len(names):
+ result = names[selected]
+ else:
+ self.env.message('Not enough proposals!')
+ return
+ self._apply_assist(result)
+
+ def auto_import(self):
+ if not self.interface._check_autoimport():
+ return
+ name = self.env.current_word()
+ modules = self.autoimport.get_modules(name)
+ if modules:
+ if len(modules) == 1:
+ module = modules[0]
+ else:
+ module = self.env.ask_values(
+ 'Which module to import: ', modules)
+ self._insert_import(name, module)
+ else:
+ self.env.message('Global name %s not found!' % name)
+
+ def _apply_assist(self, assist):
+ if ' : ' in assist:
+ name, module = assist.rsplit(' : ', 1)
+ self.env.delete(self.starting_offset + 1, self.offset + 1)
+ self.env.insert(name)
+ self._insert_import(name, module)
+ else:
+ self.env.delete(self.starting_offset + 1, self.offset + 1)
+ self.env.insert(assist)
+
+ def _calculate_proposals(self):
+ self.interface._check_project()
+ resource = self.interface._get_resource()
+ maxfixes = self.env.get('codeassist_maxfixes')
+ proposals = codeassist.code_assist(
+ self.interface.project, self.source, self.offset,
+ resource, maxfixes=maxfixes)
+ proposals = codeassist.sorted_proposals(proposals)
+ names = [proposal.name for proposal in proposals]
+ if self.autoimport is not None:
+ if self.starting.strip() and '.' not in self.expression:
+ import_assists = self.autoimport.import_assist(self.starting)
+ names.extend(x[0] + ' : ' + x[1] for x in import_assists)
+ return names
+
+ def _insert_import(self, name, module):
+ lineno = self.autoimport.find_insertion_line(self.source)
+ line = 'from %s import %s' % (module, name)
+ self.env.insert_line(line, lineno)
+
+ def _calculate_prefix(self, names):
+ if not names:
+ return ''
+ prefix = names[0]
+ for name in names:
+ common = 0
+ for c1, c2 in zip(prefix, name):
+ if c1 != c2 or ' ' in (c1, c2):
+ break
+ common += 1
+ prefix = prefix[:common]
+ return prefix
+
+ @property
+ def offset(self):
+ if self._offset is None:
+ self._offset = self.env.get_offset()
+ return self._offset
+
+ @property
+ def source(self):
+ if self._source is None:
+ self._source = self.interface._get_text()
+ return self._source
+
+ @property
+ def starting_offset(self):
+ if self._starting_offset is None:
+ self._starting_offset = codeassist.starting_offset(self.source,
+ self.offset)
+ return self._starting_offset
+
+ @property
+ def starting(self):
+ if self._starting is None:
+ self._starting = self.source[self.starting_offset:self.offset]
+ return self._starting
+
+ @property
+ def expression(self):
+ if self._expression is None:
+ self._expression = codeassist.starting_expression(self.source,
+ self.offset)
+ return self._expression
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/notreallybundles/ropevim/ropevim/ropemode/refactor.py Sat May 15 23:20:42 2010 -0400
@@ -0,0 +1,491 @@
+import re
+
+import rope.base.change
+import rope.contrib.generate
+import rope.refactor.change_signature
+import rope.refactor.extract
+import rope.refactor.inline
+import rope.refactor.introduce_factory
+import rope.refactor.method_object
+import rope.refactor.move
+import rope.refactor.rename
+import rope.refactor.restructure
+import rope.refactor.usefunction
+from rope.base import taskhandle
+
+from ropemode import dialog, filter
+
+
+class Refactoring(object):
+ key = None
+ confs = {}
+ optionals = {}
+ saveall = True
+
+ def __init__(self, interface, env):
+ self.interface = interface
+ self.env = env
+
+ def show(self, initial_asking=True):
+ self.interface._check_project()
+ self.interface._save_buffers(only_current=not self.saveall)
+ self._create_refactoring()
+ action, result = dialog.show_dialog(
+ self.interface._askdata, ['perform', 'preview', 'cancel'],
+ self._get_confs(), self._get_optionals(),
+ initial_asking=initial_asking)
+ if action == 'cancel':
+ self.env.message('Cancelled!')
+ return
+ def calculate(handle):
+ return self._calculate_changes(result, handle)
+ name = 'Calculating %s changes' % self.name
+ changes = runtask(self.env, calculate, name=name)
+ if action == 'perform':
+ self._perform(changes)
+ if action == 'preview':
+ if changes is not None:
+ diffs = str(changes.get_description())
+ if self.env.preview_changes(diffs):
+ self._perform(changes)
+ else:
+ self.env.message('Thrown away!')
+ else:
+ self.env.message('No changes!')
+
+ @property
+ def project(self):
+ return self.interface.project
+
+ @property
+ def resource(self):
+ return self.interface._get_resource()
+
+ @property
+ def offset(self):
+ return self.env.get_offset()
+
+ @property
+ def region(self):
+ return self.env.get_region()
+
+ @property
+ def name(self):
+ return refactoring_name(self.__class__)
+
+ def _calculate_changes(self, option_values, task_handle):
+ pass
+
+ def _create_refactoring(self):
+ pass
+
+ def _done(self):
+ pass
+
+ def _perform(self, changes):
+ if changes is None:
+ self.env.message('No changes!')
+ return
+ def perform(handle, self=self, changes=changes):
+ self.project.do(changes, task_handle=handle)
+ self.interface._reload_buffers(changes)
+ self._done()
+ runtask(self.env, perform, 'Making %s changes' % self.name,
+ interrupts=False)
+ self.env.message(str(changes.description) + ' finished')
+
+ def _get_confs(self):
+ return self.confs
+
+ def _get_optionals(self):
+ return self.optionals
+
+ @property
+ def resources_option(self):
+ return dialog.Data('Files to apply this refactoring on: ',
+ decode=self._decode_resources)
+
+ def _decode_resources(self, value):
+ return _resources(self.project, value)
+
+
+class Rename(Refactoring):
+ key = 'r'
+
+ saveall = True
+
+ def _create_refactoring(self):
+ self.renamer = rope.refactor.rename.Rename(
+ self.project, self.resource, self.offset)
+
+ def _calculate_changes(self, values, task_handle):
+ return self.renamer.get_changes(task_handle=task_handle, **values)
+
+ def _get_optionals(self):
+ opts = {}
+ opts['docs'] = dialog.Boolean('Search comments and docs: ', True)
+ if self.renamer.is_method():
+ opts['in_hierarchy'] = dialog.Boolean('Rename methods in '
+ 'class hierarchy: ')
+ opts['resources'] = self.resources_option
+ opts['unsure'] = dialog.Data('Unsure occurrences: ',
+ decode=self._decode_unsure,
+ values=['ignore', 'match'],
+ default='ignore')
+ return opts
+
+ def _get_confs(self):
+ oldname = str(self.renamer.get_old_name())
+ return {'new_name': dialog.Data('New name: ', default=oldname)}
+
+ def _decode_unsure(self, value):
+ unsure = value == 'match'
+ return lambda occurrence: unsure
+
+
+class RenameCurrentModule(Rename):
+ key = '1 r'
+ offset = None
+
+
+class Restructure(Refactoring):
+ key = 'x'
+ confs = {'pattern': dialog.Data('Restructuring pattern: '),
+ 'goal': dialog.Data('Restructuring goal: ')}
+
+ def _calculate_changes(self, values, task_handle):
+ restructuring = rope.refactor.restructure.Restructure(
+ self.project, values['pattern'], values['goal'],
+ args=values['args'], imports=values['imports'])
+ return restructuring.get_changes(resources=values['resources'],
+ task_handle=task_handle)
+
+ def _get_optionals(self):
+ return {
+ 'args': dialog.Data('Arguments: ', decode=self._decode_args),
+ 'imports': dialog.Data('Imports: ', decode=self._decode_imports),
+ 'resources': self.resources_option}
+
+ def _decode_args(self, value):
+ if value:
+ args = {}
+ for raw_check in value.split('\n'):
+ if raw_check:
+ key, value = raw_check.split(':', 1)
+ args[key.strip()] = value.strip()
+ return args
+
+ def _decode_imports(self, value):
+ if value:
+ return [line.strip() for line in value.split('\n')]
+
+
+class UseFunction(Refactoring):
+ key = 'u'
+
+ def _create_refactoring(self):
+ self.user = rope.refactor.usefunction.UseFunction(
+ self.project, self.resource, self.offset)
+
+ def _calculate_changes(self, values, task_handle):
+ return self.user.get_changes(task_handle=task_handle, **values)
+
+ def _get_optionals(self):
+ return {'resources': self.resources_option}
+
+
+class Move(Refactoring):
+ key = 'v'
+
+ def _create_refactoring(self):
+ self.mover = rope.refactor.move.create_move(self.project,
+ self.resource,
+ self.offset)
+
+ def _calculate_changes(self, values, task_handle):
+ destination = values['destination']
+ resources = values.get('resources', None)
+ if isinstance(self.mover, rope.refactor.move.MoveGlobal):
+ return self._move_global(destination, resources, task_handle)
+ if isinstance(self.mover, rope.refactor.move.MoveModule):
+ return self._move_module(destination, resources, task_handle)
+ if isinstance(self.mover, rope.refactor.move.MoveMethod):
+ return self._move_method(destination, resources, task_handle)
+
+ def _move_global(self, dest, resources, handle):
+ destination = self.project.pycore.find_module(dest)
+ return self.mover.get_changes(
+ destination, resources=resources, task_handle=handle)
+
+ def _move_method(self, dest, resources, handle):
+ return self.mover.get_changes(
+ dest, self.mover.get_method_name(),
+ resources=resources, task_handle=handle)
+
+ def _move_module(self, dest, resources, handle):
+ destination = self.project.pycore.find_module(dest)
+ return self.mover.get_changes(
+ destination, resources=resources, task_handle=handle)
+
+ def _get_confs(self):
+ if isinstance(self.mover, rope.refactor.move.MoveGlobal):
+ prompt = 'Destination module: '
+ if isinstance(self.mover, rope.refactor.move.MoveModule):
+ prompt = 'Destination package: '
+ if isinstance(self.mover, rope.refactor.move.MoveMethod):
+ prompt = 'Destination attribute: '
+ return {'destination': dialog.Data(prompt)}
+
+ def _get_optionals(self):
+ return {'resources': self.resources_option}
+
+
+class MoveCurrentModule(Move):
+ key = '1 v'
+ offset = None
+
+
+class ModuleToPackage(Refactoring):
+ key = '1 p'
+ saveall = False
+
+ def _create_refactoring(self):
+ self.packager = rope.refactor.ModuleToPackage(
+ self.project, self.resource)
+
+ def _calculate_changes(self, values, task_handle):
+ return self.packager.get_changes()
+
+
+class Inline(Refactoring):
+ key = 'i'
+
+ def _create_refactoring(self):
+ self.inliner = rope.refactor.inline.create_inline(
+ self.project, self.resource, self.offset)
+
+ def _calculate_changes(self, values, task_handle):
+ return self.inliner.get_changes(task_handle=task_handle, **values)
+
+ def _get_optionals(self):
+ opts = {'resources': self.resources_option}
+ if self.inliner.get_kind() == 'parameter':
+ opts['in_hierarchy'] = dialog.Boolean(
+ 'Apply on all matching methods in class hierarchy: ', False)
+ else:
+ opts['remove'] = dialog.Boolean('Remove the definition: ', True)
+ opts['only_current'] = dialog.Boolean('Inline this '
+ 'occurrence only: ')
+ return opts
+
+
+class _Extract(Refactoring):
+ saveall = False
+ optionals = {'similar': dialog.Boolean('Extract similar pieces: ', True),
+ 'global_': dialog.Boolean('Make global: ')}
+ kind = None
+ constructor = None
+
+ def _create_refactoring(self):
+ start, end = self.region
+ self.extractor = self.constructor(self.project,
+ self.resource, start, end)
+
+ def _calculate_changes(self, values, task_handle):
+ similar = values.get('similar')
+ global_ = values.get('global_')
+ return self.extractor.get_changes(values['name'], similar=similar,
+ global_=global_)
+
+ def _get_confs(self):
+ return {'name': dialog.Data('Extracted %s name: ' % self.kind)}
+
+
+class ExtractVariable(_Extract):
+ key = 'l'
+ kind = 'variable'
+ constructor = rope.refactor.extract.ExtractVariable
+
+
+class ExtractMethod(_Extract):
+ key = 'm'
+ kind = 'method'
+ constructor = rope.refactor.extract.ExtractMethod
+
+
+class OrganizeImports(Refactoring):
+ key = 'o'
+ saveall = False
+
+ def _create_refactoring(self):
+ self.organizer = rope.refactor.ImportOrganizer(self.project)
+
+ def _calculate_changes(self, values, task_handle):
+ return self.organizer.organize_imports(self.resource)
+
+
+class MethodObject(Refactoring):
+ saveall = False
+ confs = {'classname': dialog.Data('New class name: ',
+ default='_ExtractedClass')}
+
+ def _create_refactoring(self):
+ self.objecter = rope.refactor.method_object.MethodObject(
+ self.project, self.resource, self.offset)
+
+ def _calculate_changes(self, values, task_handle):
+ classname = values.get('classname')
+ return self.objecter.get_changes(classname)
+
+
+class IntroduceFactory(Refactoring):
+ saveall = True
+ key = 'f'
+
+ def _create_refactoring(self):
+ self.factory = rope.refactor.introduce_factory.IntroduceFactory(
+ self.project, self.resource, self.offset)
+
+ def _calculate_changes(self, values, task_handle):
+ return self.factory.get_changes(task_handle=task_handle, **values)
+
+ def _get_confs(self):
+ default = 'create_%s' % self.factory.old_name.lower()
+ return {'factory_name': dialog.Data('Factory name: ', default)}
+
+ def _get_optionals(self):
+ return {'global_factory': dialog.Boolean('Make global: ', True),
+ 'resources': self.resources_option}
+
+
+class ChangeSignature(Refactoring):
+ saveall = True
+ key = 's'
+
+ def _create_refactoring(self):
+ self.changer = rope.refactor.change_signature.ChangeSignature(
+ self.project, self.resource, self.offset)
+
+ def _calculate_changes(self, values, task_handle):
+ signature = values.get('signature')
+ args = re.sub(r'[\s\(\)]+', '', signature).split(',')
+ olds = [arg[0] for arg in self._get_args()]
+
+ changers = []
+ for arg in list(olds):
+ if arg in args:
+ continue
+ changers.append(rope.refactor.change_signature.
+ ArgumentRemover(olds.index(arg)))
+ olds.remove(arg)
+
+ order = []
+ for index, arg in enumerate(args):
+ if arg not in olds:
+ changers.append(rope.refactor.change_signature.
+ ArgumentAdder(index, arg))
+ olds.insert(index, arg)
+ order.append(olds.index(arg))
+ changers.append(rope.refactor.change_signature.
+ ArgumentReorderer(order, autodef='None'))
+
+ del values['signature']
+ return self.changer.get_changes(changers, task_handle=task_handle,
+ **values)
+
+ def _get_args(self):
+ if hasattr(self.changer, 'get_args'):
+ return self.changer.get_args()
+ return self.changer.get_definition_info().args_with_defaults
+
+ def _get_confs(self):
+ args = []
+ for arg, default in self._get_args():
+ args.append(arg)
+ signature = '(' + ', '.join(args) + ')'
+ return {'signature': dialog.Data('Change the signature: ',
+ default=signature)}
+
+ def _get_optionals(self):
+ opts = {'resources': self.resources_option}
+ if self.changer.is_method():
+ opts['in_hierarchy'] = dialog.Boolean('Rename methods in '
+ 'class hierarchy: ')
+ return opts
+
+
+class _GenerateElement(Refactoring):
+
+ def _create_refactoring(self):
+ kind = self.name.split('_')[-1]
+ self.generator = rope.contrib.generate.create_generate(
+ kind, self.project, self.resource, self.offset)
+
+ def _calculate_changes(self, values, task_handle):
+ return self.generator.get_changes()
+
+ def _done(self):
+ resource, lineno = self.generator.get_location()
+ self.interface._goto_location(resource, lineno)
+
+
+class GenerateVariable(_GenerateElement):
+ key = 'n v'
+
+
+class GenerateFunction(_GenerateElement):
+ key = 'n f'
+
+
+class GenerateClass(_GenerateElement):
+ key = 'n c'
+
+
+class GenerateModule(_GenerateElement):
+ key = 'n m'
+
+
+class GeneratePackage(_GenerateElement):
+ key = 'n p'
+
+
+def refactoring_name(refactoring):
+ classname = refactoring.__name__
+ result = []
+ for c in classname:
+ if result and c.isupper():
+ result.append('_')
+ result.append(c.lower())
+ name = ''.join(result)
+ return name
+
+def _resources(project, text):
+ if text is None or text.strip() == '':
+ return None
+ return filter.resources(project, text)
+
+
+def runtask(env, command, name, interrupts=True):
+ return RunTask(env, command, name, interrupts)()
+
+class RunTask(object):
+
+ def __init__(self, env, task, name, interrupts=True):
+ self.env = env
+ self.task = task
+ self.name = name
+ self.interrupts = interrupts
+
+ def __call__(self):
+ handle = taskhandle.TaskHandle(name=self.name)
+ progress = self.env.create_progress(self.name)
+ def update_progress():
+ jobset = handle.current_jobset()
+ if jobset:
+ percent = jobset.get_percent_done()
+ if percent is not None:
+ progress.update(percent)
+ handle.add_observer(update_progress)
+ result = self.task(handle)
+ progress.done()
+ return result
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/notreallybundles/ropevim/ropevim/ropevim.py Sat May 15 23:20:42 2010 -0400
@@ -0,0 +1,359 @@
+"""ropevim, a vim mode for using rope refactoring library"""
+import os
+import tempfile
+
+import ropemode.decorators
+import ropemode.environment
+import ropemode.interface
+import vim
+
+
+class VimUtils(ropemode.environment.Environment):
+
+ def ask(self, prompt, default=None, starting=None):
+ if starting is None:
+ starting = ''
+ if default is not None:
+ prompt = prompt + ('[%s] ' % default)
+ result = call('input("%s", "%s")' % (prompt, starting))
+ if default is not None and result == '':
+ return default
+ return result
+
+ def ask_values(self, prompt, values, default=None,
+ starting=None, show_values=None):
+ if show_values or (show_values is None and len(values) < 14):
+ self._print_values(values)
+ if default is not None:
+ prompt = prompt + ('[%s] ' % default)
+ starting = starting or ''
+ _completer.values = values
+ answer = call('input("%s", "%s", "customlist,RopeValueCompleter")' %
+ (prompt, starting))
+ if answer is None:
+ if 'cancel' in values:
+ return 'cancel'
+ return
+ if default is not None and not answer:
+ return default
+ if answer.isdigit() and 0 <= int(answer) < len(values):
+ return values[int(answer)]
+ return answer
+
+ def _print_values(self, values):
+ numbered = []
+ for index, value in enumerate(values):
+ numbered.append('%s. %s' % (index, str(value)))
+ echo('\n'.join(numbered) + '\n')
+
+ def ask_directory(self, prompt, default=None, starting=None):
+ return call('input("%s", ".", "dir")' % prompt)
+
+ def ask_completion(self, prompt, values, starting=None):
+ if self.get('vim_completion') and 'i' in call('mode()'):
+ col = int(call('col(".")'))
+ if starting:
+ col -= len(starting)
+ vim.command('call complete(%s, %s)' % (col, values))
+ return None
+ return self.ask_values(prompt, values, starting=starting,
+ show_values=False)
+
+ def message(self, message):
+ echo(message)
+
+ def yes_or_no(self, prompt):
+ return self.ask_values(prompt, ['yes', 'no']) == 'yes'
+
+ def y_or_n(self, prompt):
+ return self.yes_or_no(prompt)
+
+ def get(self, name, default=None):
+ vimname = 'g:ropevim_%s' % name
+ if str(vim.eval('exists("%s")' % vimname)) == '0':
+ return default
+ result = vim.eval(vimname)
+ if result.isdigit():
+ return int(result)
+ return result
+
+ def get_offset(self):
+ result = self._position_to_offset(*vim.current.window.cursor)
+ return result
+
+ def _position_to_offset(self, lineno, colno):
+ result = min(colno, len(self.buffer[lineno -1]) + 1)
+ for line in self.buffer[:lineno-1]:
+ result += len(line) + 1
+ return result
+
+ def get_text(self):
+ return '\n'.join(self.buffer) + '\n'
+
+ def get_region(self):
+ start = self._position_to_offset(*self.buffer.mark('<'))
+ end = self._position_to_offset(*self.buffer.mark('>'))
+ return start, end
+
+ @property
+ def buffer(self):
+ return vim.current.buffer
+
+ def filename(self):
+ return self.buffer.name
+
+ def is_modified(self):
+ return vim.eval('&modified')
+
+ def goto_line(self, lineno):
+ vim.current.window.cursor = (lineno, 0)
+
+ def insert_line(self, line, lineno):
+ self.buffer[lineno - 1:lineno - 1] = [line]
+
+ def insert(self, text):
+ lineno, colno = vim.current.window.cursor
+ line = self.buffer[lineno - 1]
+ self.buffer[lineno - 1] = line[:colno] + text + line[colno:]
+ vim.current.window.cursor = (lineno, colno + len(text))
+
+ def delete(self, start, end):
+ lineno1, colno1 = self._offset_to_position(start - 1)
+ lineno2, colno2 = self._offset_to_position(end - 1)
+ lineno, colno = vim.current.window.cursor
+ if lineno1 == lineno2:
+ line = self.buffer[lineno1 - 1]
+ self.buffer[lineno1 - 1] = line[:colno1] + line[colno2:]
+ if lineno == lineno1 and colno >= colno1:
+ diff = colno2 - colno1
+ vim.current.window.cursor = (lineno, max(0, colno - diff))
+
+ def _offset_to_position(self, offset):
+ text = self.get_text()
+ lineno = text.count('\n', 0, offset) + 1
+ try:
+ colno = offset - text.rindex('\n', 0, offset) - 1
+ except ValueError:
+ colno = offset
+ return lineno, colno
+
+ def filenames(self):
+ result = []
+ for buffer in vim.buffers:
+ if buffer.name:
+ result.append(buffer.name)
+ return result
+
+ def save_files(self, filenames):
+ vim.command('wall')
+
+ def reload_files(self, filenames, moves={}):
+ initial = self.filename()
+ for filename in filenames:
+ self.find_file(moves.get(filename, filename), force=True)
+ if initial:
+ self.find_file(initial)
+
+ def find_file(self, filename, readonly=False, other=False, force=False):
+ if filename != self.filename() or force:
+ if other:
+ vim.command('new')
+ vim.command('e %s' % filename)
+ if readonly:
+ vim.command('set nomodifiable')
+
+ def create_progress(self, name):
+ return VimProgress(name)
+
+ def current_word(self):
+ pass
+
+ def push_mark(self):
+ vim.command('mark `')
+
+ def prefix_value(self, prefix):
+ return prefix
+
+ def show_occurrences(self, locations):
+ self._quickfixdefs(locations)
+
+ def _quickfixdefs(self, locations):
+ filename = os.path.join(tempfile.gettempdir(), tempfile.mktemp())
+ try:
+ self._writedefs(locations, filename)
+ vim.command('let old_errorfile = &errorfile')
+ vim.command('let old_errorformat = &errorformat')
+ vim.command('set errorformat=%f:%l:\ %m')
+ vim.command('cfile ' + filename)
+ vim.command('let &errorformat = old_errorformat')
+ vim.command('let &errorfile = old_errorfile')
+ finally:
+ os.remove(filename)
+
+ def _writedefs(self, locations, filename):
+ tofile = open(filename, 'w')
+ try:
+ for location in locations:
+ err = '%s:%d: - %s\n' % (location.filename,
+ location.lineno, location.note)
+ echo(err)
+ tofile.write(err)
+ finally:
+ tofile.close()
+
+ def show_doc(self, docs, altview=False):
+ if docs:
+ echo(docs)
+
+ def preview_changes(self, diffs):
+ echo(diffs)
+ return self.y_or_n('Do the changes? ')
+
+ def local_command(self, name, callback, key=None, prefix=False):
+ self._add_command(name, callback, key, prefix,
+ prekey=self.get('local_prefix'))
+
+ def global_command(self, name, callback, key=None, prefix=False):
+ self._add_command(name, callback, key, prefix,
+ prekey=self.get('global_prefix'))
+
+ def add_hook(self, name, callback, hook):
+ mapping = {'before_save': 'FileWritePre,BufWritePre',
+ 'after_save': 'FileWritePost,BufWritePost',
+ 'exit': 'VimLeave'}
+ self._add_function(name, callback)
+ vim.command('autocmd %s *.py call %s()' %
+ (mapping[hook], _vim_name(name)))
+
+ def _add_command(self, name, callback, key, prefix, prekey):
+ self._add_function(name, callback, prefix)
+ vim.command('command! -range %s call %s()' %
+ (_vim_name(name), _vim_name(name)))
+ if key is not None:
+ key = prekey + key.replace(' ', '')
+ vim.command('map %s :call %s()<cr>' % (key, _vim_name(name)))
+
+ def _add_function(self, name, callback, prefix=False):
+ globals()[name] = callback
+ arg = 'None' if prefix else ''
+ vim.command('function! %s()\n' % _vim_name(name) +
+ 'python ropevim.%s(%s)\n' % (name, arg) +
+ 'endfunction\n')
+
+
+def _vim_name(name):
+ tokens = name.split('_')
+ newtokens = ['Rope'] + [token.title() for token in tokens]
+ return ''.join(newtokens)
+
+
+class VimProgress(object):
+
+ def __init__(self, name):
+ self.name = name
+ self.last = 0
+ echo('%s ... ' % self.name)
+
+ def update(self, percent):
+ try:
+ vim.eval('getchar(0)')
+ except vim.error:
+ raise KeyboardInterrupt('Task %s was interrupted!' % self.name)
+ if percent > self.last + 4:
+ echo('%s ... %s%%%%' % (self.name, percent))
+ self.last = percent
+
+ def done(self):
+ echo('%s ... done' % self.name)
+
+
+def echo(message):
+ vim.command('echo "%s"' % message.replace('\n', '\\n').replace('"', '\\"'))
+
+def call(command):
+ return vim.eval(command)
+
+
+class _ValueCompleter(object):
+
+ def __init__(self):
+ self.values = []
+ vim.command('python import vim')
+ vim.command('function! RopeValueCompleter(A, L, P)\n'
+ 'python args = [vim.eval("a:" + p) for p in "ALP"]\n'
+ 'python ropevim._completer(*args)\n'
+ 'return s:completions\n'
+ 'endfunction\n')
+
+ def __call__(self, arg_lead, cmd_line, cursor_pos):
+ result = [value for value in self.values if value.startswith(arg_lead)]
+ vim.command('let s:completions = %s' % result)
+
+
+variables = {'ropevim_enable_autoimport': 1,
+ 'ropevim_autoimport_underlineds': 0,
+ 'ropevim_codeassist_maxfixes' : 1,
+ 'ropevim_enable_shortcuts' : 1,
+ 'ropevim_autoimport_modules': '[]',
+ 'ropevim_confirm_saving': 0,
+ 'ropevim_local_prefix': '"<C-c>r"',
+ 'ropevim_global_prefix': '"<C-x>p"',
+ 'ropevim_vim_completion': 0,
+ 'ropevim_guess_project': 0}
+
+shortcuts = {'code_assist': '<M-/>',
+ 'lucky_assist': '<M-?>',
+ 'goto_definition': '<C-c>g',
+ 'show_doc': '<C-c>d',
+ 'find_occurrences': '<C-c>f'}
+
+insert_shortcuts = {'code_assist': '<M-/>',
+ 'lucky_assist': '<M-?>'}
+
+def _init_variables():
+ for variable, default in variables.items():
+ vim.command('if !exists("g:%s")\n' % variable +
+ ' let g:%s = %s\n' % (variable, default))
+
+def _enable_shortcuts(env):
+ if env.get('enable_shortcuts'):
+ for command, shortcut in shortcuts.items():
+ vim.command('map %s :call %s()<cr>' %
+ (shortcut, _vim_name(command)))
+ for command, shortcut in insert_shortcuts.items():
+ command_name = _vim_name(command) + 'InsertMode'
+ vim.command('func! %s()\n' % command_name +
+ 'call %s()\n' % _vim_name(command) +
+ 'return ""\n'
+ 'endfunc')
+ vim.command('imap %s <C-R>=%s()<cr>' % (shortcut, command_name))
+
+def _add_menu(env):
+ project = ['open_project', 'close_project', 'find_file', 'undo', 'redo']
+ refactor = ['rename', 'extract_variable', 'extract_method', 'inline',
+ 'move', 'restructure', 'use_function', 'introduce_factory',
+ 'change_signature', 'rename_current_module',
+ 'move_current_module', 'module_to_package']
+ assists = ['code_assist', 'goto_definition', 'show_doc', 'find_occurrences',
+ 'lucky_assist', 'jump_to_global', 'show_calltip']
+ vim.command('silent! aunmenu Ropevim')
+ for index, items in enumerate([project, assists, refactor]):
+ if index != 0:
+ vim.command('amenu <silent> &Ropevim.-SEP%s- :' % index)
+ for name in items:
+ item = '\ '.join(token.title() for token in name.split('_'))
+ for command in ['amenu', 'vmenu']:
+ vim.command('%s <silent> &Ropevim.%s :call %s()<cr>' %
+ (command, item, _vim_name(name)))
+
+
+ropemode.decorators.logger.message = echo
+ropemode.decorators.logger.only_short = True
+_completer = _ValueCompleter()
+
+_init_variables()
+_env = VimUtils()
+_interface = ropemode.interface.RopeMode(env=_env)
+_interface.init()
+_enable_shortcuts(_env)
+_add_menu(_env)