# HG changeset patch # User Steve Losh # Date 1273980042 14400 # Node ID 467a001aa0ee9698faa6d18796139a21691e2d4d # Parent cb726f354f48102d4e23b818e6ab58c11da3154d vim: add ropevim diff -r cb726f354f48 -r 467a001aa0ee vim/.vimrc --- 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 :TlistToggle map :!/usr/local/bin/ctags -R --c++-kinds=+p --fields=+iaS --extra=+q . + +" Ropevim +let $PYTHONPATH .= ":" . $HOME . "/lib/python/rope" +let $PYTHONPATH .= ":" . $HOME . "/lib/dotfiles/vim/notreallybundles/ropevim/ropevim" +source ~/lib/dotfiles/vim/notreallybundles/ropevim/ropevim.vim + diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/docs/done.txt --- /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 diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/docs/ropevim.txt --- /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 diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/docs/todo.txt --- /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 diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/ropevim.vim --- /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() diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/ropevim/__init__.py diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/ropevim/ropemode/__init__.py --- /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.""" diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/ropevim/ropemode/decorators.py --- /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 diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/ropevim/ropemode/dialog.py --- /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) diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/ropevim/ropemode/environment.py --- /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 diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/ropevim/ropemode/filter.py --- /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 diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/ropevim/ropemode/interface.py --- /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 diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/ropevim/ropemode/refactor.py --- /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 diff -r cb726f354f48 -r 467a001aa0ee vim/notreallybundles/ropevim/ropevim/ropevim.py --- /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()' % (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': '"r"', + 'ropevim_global_prefix': '"p"', + 'ropevim_vim_completion': 0, + 'ropevim_guess_project': 0} + +shortcuts = {'code_assist': '', + 'lucky_assist': '', + 'goto_definition': 'g', + 'show_doc': 'd', + 'find_occurrences': 'f'} + +insert_shortcuts = {'code_assist': '', + 'lucky_assist': ''} + +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()' % + (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 =%s()' % (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 &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 &Ropevim.%s :call %s()' % + (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)