Commits

Anonymous committed b20f595

Changed ropeide to contain only ropeide package

  • Participants
  • Parent commits eb0d53c

Comments (0)

Files changed (13)

rope/contrib/__init__.py

+"""rope IDE tools package
+
+This package contains modules that can be used in IDEs
+but do not depend on the UI.  So these modules will be used
+by `rope.ui` modules.
+
+"""

rope/contrib/codeassist.py

+import keyword
+import re
+import sys
+
+import rope.base.codeanalyze
+from rope.base import pyobjects, pynames, taskhandle, builtins
+from rope.base.codeanalyze import (StatementRangeFinder, ArrayLinesAdapter,
+                                   WordRangeFinder, ScopeNameFinder,
+                                   SourceLinesAdapter, BadIdentifierError)
+from rope.refactor import occurrences, functionutils
+
+
+def code_assist(project, source_code, offset, resource=None, templates={}):
+    """Return python code completions as a list of `CodeAssistProposal`\s
+
+    `resource` is a `rope.base.resources.Resource` object.  If
+    provided things relative imports are handled.
+
+    `templates` should be a dictionary of template name to `Template`
+    objects.  The matches are returned as `TemplateProposal`
+    instances.
+
+    """
+    assist = _PythonCodeAssist(project, templates=templates)
+    return assist.assist(source_code, offset, resource)
+
+
+def starting_offset(source_code, offset):
+    """Return the offset in which the completion should be inserted
+
+    Usually code assist proposals should be inserted like::
+
+        completion = proposal.name # not for templates
+        result = (source_code[:starting_offset] +
+                  completion + source_code[offset:])
+
+    Where starting_offset is the offset returned by this function.
+
+    """
+    word_finder = WordRangeFinder(source_code)
+    expression, starting, starting_offset = \
+        word_finder.get_splitted_primary_before(offset)
+    return starting_offset
+
+
+def get_doc(project, source_code, offset, resource=None):
+    """Get the pydoc"""
+    pymodule = _get_pymodule(project.pycore, source_code, resource)
+    scope_finder = ScopeNameFinder(pymodule)
+    element = scope_finder.get_pyname_at(offset)
+    if element is None:
+        return None
+    pyobject = element.get_object()
+    return PyDocExtractor().get_doc(pyobject)
+
+
+def get_definition_location(project, source_code, offset, resource=None):
+    """Return a (`rope.base.resources.Resource`, lineno) tuple"""
+    pymodule = project.pycore.get_string_module(source_code, resource)
+    scope_finder = ScopeNameFinder(pymodule)
+    element = scope_finder.get_pyname_at(offset)
+    if element is not None:
+        module, lineno = element.get_definition_location()
+        if module is not None:
+            return module.get_module().get_resource(), lineno
+    return (None, None)
+
+
+def find_occurrences(project, resource, offset, unsure=False,
+                     task_handle=taskhandle.NullTaskHandle()):
+    """Return a list of `Location`\s
+
+    if `unsure` is `True`, possible matches are returned, too.
+
+    """
+    name = rope.base.codeanalyze.get_name_at(resource, offset)
+    pyname = rope.base.codeanalyze.get_pyname_at(project.get_pycore(),
+                                                 resource, offset)
+    finder = occurrences.FilteredFinder(
+        project.get_pycore(), name, [pyname], unsure=unsure)
+    files = project.get_pycore().get_python_files()
+    job_set = task_handle.create_jobset('Finding Occurrences',
+                                        count=len(files))
+    result = []
+    for resource in files:
+        job_set.started_job('Working On <%s>' % resource.path)
+        for occurrence in finder.find_occurrences(resource):
+            location = Location()
+            location.resource = resource
+            location.offset = occurrence.get_word_range()[0]
+            location.unsure = occurrence.is_unsure()
+            result.append(location)
+        job_set.finished_job()
+    return result
+
+
+class Location(object):
+
+    resource = None
+    offset = None
+    unsure = False
+
+
+class CodeAssistProposal(object):
+    """The base class for proposals reported by `code_assist`
+
+    The `kind` instance variable shows the kind of the proposal and
+    can be 'global', 'local', 'builtin', 'attribute', 'keyword',
+    'parameter_keyword' and 'template'.
+
+    """
+
+    def __init__(self, name, kind):
+        self.name = name
+        self.kind = kind
+
+
+class CompletionProposal(CodeAssistProposal):
+    """A completion proposal
+
+    The `type` instance variable shows the type of the proposal and
+    can be 'variable', 'class', 'function', 'imported' , 'paramter'
+    and `None`.
+
+    """
+
+    def __init__(self, name, kind, type=None):
+        super(CompletionProposal, self).__init__(name, kind)
+        self.type = type
+
+    def __str__(self):
+        return '%s (%s, %s)' % (self.name, self.kind, self.type)
+
+    def __repr__(self):
+        return str(self)
+
+
+class TemplateProposal(CodeAssistProposal):
+    """A template proposal
+
+    The `template` attribute is a `Template` object.
+    """
+
+    def __init__(self, name, template):
+        super(TemplateProposal, self).__init__(name, 'template')
+        self.template = template
+
+
+class Template(object):
+    """Templates that are used in code assists
+
+    Variables in templates are in ``${variable}`` format. To put a
+    dollar sign in the template put $$.  To specify cursor position
+    use ${cursor}.
+
+    """
+
+    def __init__(self, template):
+        self.template = template
+
+    var_pattern = re.compile(r'((?<=[^\$])|^)\${(?P<variable>\w+)}')
+
+    def variables(self):
+        """Get template variables
+
+        Return the list of variables sorted by their order of
+        occurence in the template.
+
+        """
+        result = []
+        for match in self.var_pattern.finditer(self.template):
+            new_var = match.group('variable')
+            if new_var not in result and new_var != 'cursor':
+                result.append(new_var)
+        return result
+
+    def _substitute(self, input_string, mapping):
+        import string
+        single_dollar = re.compile('((?<=[^\$])|^)\$((?=[^{\$])|$)')
+        template = single_dollar.sub('$$', input_string)
+        t = string.Template(template)
+        return t.substitute(mapping, cursor='')
+
+    def substitute(self, mapping):
+        return self._substitute(self.template, mapping)
+
+    def get_cursor_location(self, mapping):
+        cursor_index = len(self.template)
+        for match in self.var_pattern.finditer(self.template):
+            new_var = match.group('variable')
+            if new_var == 'cursor':
+                cursor_index = match.start('variable') - 2
+        new_template = self.template[0:cursor_index]
+        start = len(self._substitute(new_template, mapping))
+        return start
+
+
+def sorted_proposals(proposals, kindpref=None, typepref=None):
+    """Sort a list of proposals
+
+    Return a sorted list of the given `CodeAssistProposal`\s.
+
+    `typepref` can be a list of proposal kinds.  Defaults to ``['local',
+    'parameter_keyword', 'global', 'attribute', 'template', 'builtin',
+    'keyword']``.
+
+    `typepref` can be a list of proposal types.  Defaults to
+    ``['class', 'function', 'variable', 'parameter', 'imported',
+    None]``.  (`None` stands for completions with no type like
+    keywords.)
+
+    """
+    sorter = _ProposalSorter(proposals, kindpref, typepref)
+    return sorter.get_sorted_proposal_list()
+
+
+def default_templates():
+    templates = {}
+    templates['main'] = Template("if __name__ == '__main__':\n    ${cursor}\n")
+    test_case_template = \
+        ('import unittest\n\n\n'
+         'class ${TestClass}(unittest.TestCase):\n\n'
+         '    def setUp(self):\n        super(${TestClass}, self).setUp()\n\n'
+         '    def tearDown(self):\n        super(${TestClass}, self).tearDown()\n\n'
+         '    def test_trivial_case${cursor}(self):\n        pass\n\n\n'
+         'if __name__ == \'__main__\':\n'
+         '    unittest.main()\n')
+    templates['testcase'] = Template(test_case_template)
+    templates['hash'] = Template('\n    def __hash__(self):\n' +
+                                 '        return 1${cursor}\n')
+    templates['eq'] = Template('\n    def __eq__(self, obj):\n' +
+                               '        ${cursor}return obj is self\n')
+    templates['super'] = Template('super(${class}, self)')
+    return templates
+
+
+class _PythonCodeAssist(object):
+
+    def __init__(self, project, templates={}):
+        self.project = project
+        self.keywords = keyword.kwlist
+        self.templates = templates
+
+    def _find_starting_offset(self, source_code, offset):
+        current_offset = offset - 1
+        while current_offset >= 0 and (source_code[current_offset].isalnum() or
+                                       source_code[current_offset] in '_'):
+            current_offset -= 1;
+        return current_offset + 1
+
+    def _get_matching_keywords(self, starting):
+        result = []
+        for kw in self.keywords:
+            if kw.startswith(starting):
+                result.append(CompletionProposal(kw, 'keyword'))
+        return result
+
+    def add_template(self, name, definition):
+        self.templates.append(TemplateProposal(name, Template(definition)))
+
+    def _get_template_proposals(self, starting):
+        result = []
+        for name, template in self.templates.items():
+            if name.startswith(starting):
+                result.append(TemplateProposal(name, template))
+        return result
+
+    def _get_code_completions(self, source_code, offset,
+                              expression, starting, resource):
+        collector = _CodeCompletionCollector(self.project, source_code, offset,
+                                             expression, starting, resource)
+        return collector.get_code_completions()
+
+    def assist(self, source_code, offset, resource=None):
+        if offset > len(source_code):
+            return []
+        word_finder = WordRangeFinder(source_code)
+        expression, starting, starting_offset = \
+            word_finder.get_splitted_primary_before(offset)
+        completions = list(
+            self._get_code_completions(source_code, offset, expression,
+                                       starting, resource).values())
+        if expression.strip() == '' and starting.strip() != '':
+            completions.extend(self._get_matching_keywords(starting))
+            completions.extend(self._get_template_proposals(starting))
+        return completions
+
+
+class _ProposalSorter(object):
+    """Sort a list of code assist proposals"""
+
+    def __init__(self, code_assist_proposals, kindpref=None, typepref=None):
+        self.proposals = code_assist_proposals
+        if kindpref is None:
+            kindpref = ['local', 'parameter_keyword', 'global', 'attribute',
+                        'template', 'builtin', 'keyword']
+        self.kindpref = kindpref
+        if typepref is None:
+            typepref = ['class', 'function', 'variable',
+                        'parameter', 'imported', None]
+        self.typerank = dict((type, index)
+                              for index, type in enumerate(typepref))
+
+    def get_sorted_proposal_list(self):
+        """Return a list of `CodeAssistProposal`"""
+        proposals = {}
+        for proposal in self.proposals:
+            proposals.setdefault(proposal.kind, []).append(proposal)
+        result = []
+        for kind in self.kindpref:
+            kind_proposals = proposals.get(kind, [])
+            if kind != 'template':
+                kind_proposals = [proposal for proposal in kind_proposals
+                                  if proposal.type in self.typerank]
+            kind_proposals.sort(self._proposal_cmp)
+            result.extend(kind_proposals)
+        return result
+
+    def _proposal_cmp(self, proposal1, proposal2):
+        if 'template' not in (proposal1.kind, proposal2.kind) and \
+           proposal1.type != proposal2.type:
+            return cmp(self.typerank.get(proposal1.type, 100),
+                       self.typerank.get(proposal2.type, 100))
+        return self._compare_underlined_names(proposal1.name,
+                                              proposal2.name)
+
+    def _compare_underlined_names(self, name1, name2):
+        def underline_count(name):
+            result = 0
+            while result < len(name) and name[result] == '_':
+                result += 1
+            return result
+        underline_count1 = underline_count(name1)
+        underline_count2 = underline_count(name2)
+        if underline_count1 != underline_count2:
+            return cmp(underline_count1, underline_count2)
+        return cmp(name1, name2)
+
+
+class _CodeCompletionCollector(object):
+
+    def __init__(self, project, source_code,
+                 offset, expression, starting, resource):
+        self.project = project
+        self.expression = expression
+        self.starting = starting
+        self.offset = offset
+        self.pycore = self.project.get_pycore()
+        self.unchanged_source = source_code
+        self.lines = source_code.split('\n')
+        self.source_code = source_code
+        self.resource = resource
+        source_lines = SourceLinesAdapter(source_code)
+        self.lineno = source_lines.get_line_number(offset)
+        self.current_indents = self._get_line_indents(
+            source_lines.get_line(self.lineno))
+        self._comment_current_statement()
+        self.source_code = '\n'.join(self.lines)
+
+    def _get_line_indents(self, line):
+        return rope.base.codeanalyze.count_line_indents(line)
+
+    def _comment_current_statement(self):
+        range_finder = StatementRangeFinder(ArrayLinesAdapter(self.lines),
+                                            self.lineno)
+        start = range_finder.get_statement_start() - 1
+        end = range_finder.get_block_end() - 1
+        last_indents = self._get_line_indents(self.lines[start])
+        self.lines[start] = ' ' * last_indents + 'pass'
+        for line in range(start + 1, end + 1):
+            #self.lines[line] = '#' # + lines[line]
+            self.lines[line] = self.lines[start]
+        self.lines.append('\n')
+        self._fix_incomplete_try_blocks()
+
+    def _fix_incomplete_try_blocks(self):
+        block_start = self.lineno
+        last_indents = self.current_indents
+        while block_start > 0:
+            block_start = rope.base.codeanalyze.get_block_start(
+                ArrayLinesAdapter(self.lines), block_start) - 1
+            if self.lines[block_start].strip().startswith('try:'):
+                indents = self._get_line_indents(self.lines[block_start])
+                if indents > last_indents:
+                    continue
+                last_indents = indents
+                block_end = self._find_matching_deindent(block_start)
+                if not self.lines[block_end].strip().startswith('finally:') and \
+                   not self.lines[block_end].strip().startswith('except '):
+                    self.lines.insert(block_end, ' ' * indents + 'finally:')
+                    self.lines.insert(block_end + 1, ' ' * indents + '    pass')
+
+    def _find_matching_deindent(self, line_number):
+        indents = self._get_line_indents(self.lines[line_number])
+        current_line = line_number + 1
+        while current_line < len(self.lines):
+            line = self.lines[current_line]
+            if not line.strip().startswith('#') and not line.strip() == '':
+                # HACK: We should have used logical lines here
+                if self._get_line_indents(self.lines[current_line]) <= indents:
+                    return current_line
+            current_line += 1
+        return len(self.lines) - 1
+
+    def _get_dotted_completions(self, module_scope, holding_scope):
+        result = {}
+        pyname_finder = ScopeNameFinder(module_scope.pyobject)
+        found_pyname = pyname_finder.get_pyname_in_scope(holding_scope,
+                                                         self.expression)
+        if found_pyname is not None:
+            element = found_pyname.get_object()
+            for name, pyname in element.get_attributes().items():
+                if name.startswith(self.starting) or self.starting == '':
+                    result[name] = CompletionProposal(
+                        name, 'attribute', self._get_pyname_type(pyname))
+        return result
+
+    def _get_undotted_completions(self, scope, result, propagated=False):
+        if scope.parent != None:
+            self._get_undotted_completions(scope.parent, result,
+                                           propagated=True)
+        if propagated:
+            names = scope.get_propagated_names()
+        else:
+            names = scope.get_names()
+        for name, pyname in names.items():
+            if name.startswith(self.starting):
+                kind = 'local'
+                if isinstance(pyname, builtins.BuiltinName):
+                    kind = 'builtin'
+                elif scope.get_kind() == 'Module':
+                    kind = 'global'
+                result[name] = CompletionProposal(
+                    name, kind, self._get_pyname_type(pyname))
+
+    def _get_pyname_type(self, pyname):
+        if isinstance(pyname, (pynames.AssignedName, pynames.UnboundName)):
+            return 'variable'
+        if isinstance(pyname, pynames.ImportedName) or \
+           isinstance(pyname, pynames.ImportedModule):
+            return 'imported'
+        if isinstance(pyname, pynames.ParameterName):
+            return 'parameter'
+        if isinstance(pyname, builtins.BuiltinName) or \
+           isinstance(pyname, pynames.DefinedName):
+            pyobject = pyname.get_object()
+            if isinstance(pyobject, pyobjects.AbstractFunction):
+                return 'function'
+            if isinstance(pyobject, pyobjects.AbstractClass):
+                return 'class'
+
+    def get_code_completions(self):
+        module_scope = _get_pymodule(self.pycore, self.source_code,
+                                    self.resource).get_scope()
+        result = {}
+        inner_scope = module_scope.get_inner_scope_for_line(self.lineno,
+                                                            self.current_indents)
+        if self.expression.strip() != '':
+            result.update(self._get_dotted_completions(module_scope,
+                                                       inner_scope))
+        else:
+            result.update(self._get_keyword_parameters(module_scope.pyobject,
+                                                       inner_scope))
+            self._get_undotted_completions(inner_scope, result)
+        return result
+
+    def _get_keyword_parameters(self, pymodule, scope):
+        offset = self.offset
+        if offset == 0:
+            return {}
+        word_finder = WordRangeFinder(self.unchanged_source)
+        lines = SourceLinesAdapter(self.unchanged_source)
+        lineno = lines.get_line_number(offset)
+        stop_line = StatementRangeFinder(lines, lineno).get_statement_start()
+        stop = lines.get_line_start(stop_line)
+        if word_finder.is_on_function_call_keyword(offset - 1, stop):
+            name_finder = ScopeNameFinder(pymodule)
+            function_parens = word_finder.find_parens_start_from_inside(offset - 1, stop)
+            primary = word_finder.get_primary_at(function_parens - 1)
+            try:
+                function_pyname = ScopeNameFinder.get_pyname_in_scope(scope,
+                                                                      primary)
+            except BadIdentifierError, e:
+                return {}
+            if function_pyname is not None:
+                pyobject = function_pyname.get_object()
+                if isinstance(pyobject, pyobjects.AbstractFunction):
+                    pass
+                elif isinstance(pyobject, pyobjects.AbstractClass) and \
+                     '__init__' in pyobject.get_attributes():
+                    pyobject = pyobject.get_attribute('__init__').get_object()
+                elif '__call__' in pyobject.get_attributes():
+                    pyobject = pyobject.get_attribute('__call__').get_object()
+                if isinstance(pyobject, pyobjects.AbstractFunction):
+                    param_names = []
+                    param_names.extend(
+                        pyobject.get_param_names(special_args=False))
+                    result = {}
+                    for name in param_names:
+                        if name.startswith(self.starting):
+                            result[name + '='] = CompletionProposal(
+                                name + '=', 'parameter_keyword')
+                    return result
+        return {}
+
+
+def _get_pymodule(pycore, source_code, resource):
+    if resource and resource.exists() and source_code == resource.read():
+        return pycore.resource_to_pyobject(resource)
+    return pycore.get_string_module(source_code, resource=resource)
+
+
+class PyDocExtractor(object):
+
+    def get_doc(self, pyobject):
+        if isinstance(pyobject, pyobjects.AbstractFunction):
+            return self._get_function_docstring(pyobject)
+        elif isinstance(pyobject, pyobjects.AbstractClass):
+            return self._get_class_docstring(pyobject)
+        elif isinstance(pyobject, pyobjects.AbstractModule):
+            return self._trim_docstring(pyobject.get_doc())
+        return None
+
+    def _get_class_docstring(self, pyclass):
+        contents = self._trim_docstring(pyclass.get_doc(), 2)
+        supers = [super.get_name() for super in pyclass.get_superclasses()]
+        doc = 'class %s(%s):\n\n' % (pyclass.get_name(), ', '.join(supers)) + contents
+
+        if '__init__' in pyclass.get_attributes():
+            init = pyclass.get_attribute('__init__').get_object()
+            if isinstance(init, pyobjects.AbstractFunction):
+                doc += '\n\n' + self._get_single_function_docstring(init)
+        return doc
+
+    def _get_function_docstring(self, pyfunction):
+        functions = [pyfunction]
+        if self._is_method(pyfunction):
+            functions.extend(self._get_super_methods(pyfunction.parent,
+                                                     pyfunction.get_name()))
+        return '\n\n'.join([self._get_single_function_docstring(function)
+                            for function in functions])
+
+    def _is_method(self, pyfunction):
+        return isinstance(pyfunction, pyobjects.PyFunction) and \
+               isinstance(pyfunction.parent, pyobjects.PyClass)
+
+    def _get_single_function_docstring(self, pyfunction):
+        signature = self._get_function_signature(pyfunction)
+        if self._is_method(pyfunction):
+            signature = pyfunction.parent.get_name() + '.' + signature
+            self._get_super_methods(pyfunction.parent, pyfunction.get_name())
+        return signature + ':\n\n' + self._trim_docstring(pyfunction.get_doc(),
+                                                         indents=2)
+
+    def _get_super_methods(self, pyclass, name):
+        result = []
+        for super_class in pyclass.get_superclasses():
+            if name in super_class.get_attributes():
+                function = super_class.get_attribute(name).get_object()
+                if isinstance(function, pyobjects.AbstractFunction):
+                    result.append(function)
+            result.extend(self._get_super_methods(super_class, name))
+        return result
+
+    def _get_function_signature(self, pyfunction):
+        if isinstance(pyfunction, pyobjects.PyFunction):
+            info = functionutils.DefinitionInfo.read(pyfunction)
+            return info.to_string()
+        else:
+            return '%s(%s)' % (pyfunction.get_name(),
+                               ', '.join(pyfunction.get_param_names()))
+
+    def _trim_docstring(self, docstring, indents=0):
+        """The sample code from :PEP:`257`"""
+        if not docstring:
+            return ''
+        # Convert tabs to spaces (following normal Python rules)
+        # and split into a list of lines:
+        lines = docstring.expandtabs().splitlines()
+        # Determine minimum indentation (first line doesn't count):
+        indent = sys.maxint
+        for line in lines[1:]:
+            stripped = line.lstrip()
+            if stripped:
+                indent = min(indent, len(line) - len(stripped))
+        # Remove indentation (first line is special):
+        trimmed = [lines[0].strip()]
+        if indent < sys.maxint:
+            for line in lines[1:]:
+                trimmed.append(line[indent:].rstrip())
+        # Strip off trailing and leading blank lines:
+        while trimmed and not trimmed[-1]:
+            trimmed.pop()
+        while trimmed and not trimmed[0]:
+            trimmed.pop(0)
+        # Return a single string:
+        return '\n'.join((' ' * indents + line for line in trimmed))

rope/contrib/generate.py

+from rope.base import change, codeanalyze, pyobjects, exceptions, pynames
+from rope.refactor import sourceutils, importutils, functionutils, suites
+
+
+def create_generate(kind, project, resource, offset):
+    """A factory for creating `Generate` objects
+
+    `kind` can be 'variable', 'function', 'class', 'module' or
+    'package'.
+
+    """
+    generate = eval('Generate' + kind.title())
+    return generate(project, resource, offset)
+
+
+class _Generate(object):
+
+    def __init__(self, project, resource, offset):
+        self.project = project
+        self.resource = resource
+        self.info = self._generate_info(project, resource, offset)
+        self.name = self.info.get_name()
+        self._check_exceptional_conditions()
+
+    def _generate_info(self, project, resource, offset):
+        return _GenerationInfo(project.pycore, resource, offset)
+
+    def _check_exceptional_conditions(self):
+        if self.info.element_already_exists():
+            raise exceptions.RefactoringError(
+                'Element <%s> already exists.' % self.name)
+        if not self.info.primary_is_found():
+            raise exceptions.RefactoringError(
+                'Cannot determine the scope <%s> should be defined in.' % self.name)
+
+    def get_changes(self):
+        changes = change.ChangeSet('Generate %s <%s>' %
+                                   (self._get_element_kind(), self.name))
+        indents = self.info.get_scope_indents()
+        blanks = self.info.get_blank_lines()
+        base_definition = sourceutils.fix_indentation(self._get_element(), indents)
+        definition = '\n' * blanks[0] + base_definition + '\n' * blanks[1]
+
+        resource = self.info.get_insertion_resource()
+        start, end = self.info.get_insertion_offsets()
+
+        collector = sourceutils.ChangeCollector(resource.read())
+        collector.add_change(start, end, definition)
+        changes.add_change(change.ChangeContents(
+                           resource, collector.get_changed()))
+        return changes
+
+    def get_location(self):
+        return (self.info.get_insertion_resource(),
+                self.info.get_insertion_lineno())
+
+    def _get_element_kind(self):
+        raise NotImplementedError()
+
+    def _get_element(self):
+        raise NotImplementedError()
+
+
+class GenerateFunction(_Generate):
+
+    def _generate_info(self, project, resource, offset):
+        return _FunctionGenerationInfo(project.pycore, resource, offset)
+
+    def _get_element(self):
+        decorator = ''
+        args = []
+        if self.info.is_static_method():
+            decorator = '@staticmethod\n'
+        if self.info.is_method() or self.info.is_constructor() or \
+           self.info.is_instance():
+            args.append('self')
+        args.extend(self.info.get_passed_args())
+        definition = '%sdef %s(%s):\n    pass\n' % (decorator, self.name,
+                                                    ', '.join(args))
+        return definition
+
+    def _get_element_kind(self):
+        return 'Function'
+
+
+class GenerateVariable(_Generate):
+
+    def _get_element(self):
+        return '%s = None\n' % self.name
+
+    def _get_element_kind(self):
+        return 'Variable'
+
+
+class GenerateClass(_Generate):
+
+    def _get_element(self):
+        return 'class %s(object):\n    pass\n' % self.name
+
+    def _get_element_kind(self):
+        return 'Class'
+
+
+class GenerateModule(_Generate):
+
+    def get_changes(self):
+        package = self.info.get_package()
+        changes = change.ChangeSet('Generate Module <%s>' % self.name)
+        new_resource = self.project.get_file('%s/%s.py' % (package.path, self.name))
+        if new_resource.exists():
+            raise exceptions.RefactoringError(
+                'Module <%s> already exists' % new_resource.path)
+        changes.add_change(change.CreateResource(new_resource))
+        changes.add_change(_add_import_to_module(
+                           self.project.get_pycore(), self.resource, new_resource))
+        return changes
+
+    def get_location(self):
+        package = self.info.get_package()
+        return (package.get_child('%s.py' % self.name) , 1)
+
+
+class GeneratePackage(_Generate):
+
+    def get_changes(self):
+        package = self.info.get_package()
+        changes = change.ChangeSet('Generate Package <%s>' % self.name)
+        new_resource = self.project.get_folder('%s/%s' % (package.path, self.name))
+        if new_resource.exists():
+            raise exceptions.RefactoringError(
+                'Package <%s> already exists' % new_resource.path)
+        changes.add_change(change.CreateResource(new_resource))
+        changes.add_change(_add_import_to_module(
+                           self.project.get_pycore(), self.resource, new_resource))
+        child = self.project.get_folder(package.path + '/' + self.name)
+        changes.add_change(change.CreateFile(child, '__init__.py'))
+        return changes
+
+    def get_location(self):
+        package = self.info.get_package()
+        child = package.get_child(self.name)
+        return (child.get_child('__init__.py') , 1)
+
+
+def _add_import_to_module(pycore, resource, imported):
+    pymodule = pycore.resource_to_pyobject(resource)
+    import_tools = importutils.ImportTools(pycore)
+    module_imports = import_tools.get_module_imports(pymodule)
+    module_name = importutils.get_module_name(pycore, imported)
+    new_import = importutils.NormalImport(((module_name, None), ))
+    module_imports.add_import(new_import)
+    return change.ChangeContents(resource, module_imports.get_changed_source())
+
+
+class _GenerationInfo(object):
+
+    def __init__(self, pycore, resource, offset):
+        self.pycore = pycore
+        self.resource = resource
+        self.offset = offset
+        self.source_pymodule = self.pycore.resource_to_pyobject(resource)
+        finder = codeanalyze.ScopeNameFinder(self.source_pymodule)
+        self.primary, self.pyname = finder.get_primary_and_pyname_at(offset)
+        self._init_fields()
+
+    def _init_fields(self):
+        self.source_scope = self._get_source_scope()
+        self.goal_scope = self._get_goal_scope()
+        self.goal_pymodule = self._get_goal_module(self.goal_scope)
+
+    def _get_goal_scope(self):
+        if self.primary is None:
+            return self._get_source_scope()
+        pyobject = self.primary.get_object()
+        if isinstance(pyobject, pyobjects.PyDefinedObject):
+            return pyobject.get_scope()
+        elif isinstance(pyobject.get_type(), pyobjects.PyClass):
+            return pyobject.get_type().get_scope()
+
+    def _get_goal_module(self, scope):
+        if scope is None:
+            return
+        while scope.parent is not None:
+            scope = scope.parent
+        return scope.pyobject
+
+    def _get_source_scope(self):
+        module_scope = self.source_pymodule.get_scope()
+        lineno = self.source_pymodule.lines.get_line_number(self.offset)
+        return module_scope.get_inner_scope_for_line(lineno)
+
+    def get_insertion_lineno(self):
+        lines = self.goal_pymodule.lines
+        if self.goal_scope == self.source_scope:
+            line_finder = codeanalyze.LogicalLineFinder(lines)
+            lineno = lines.get_line_number(self.offset)
+            lineno = line_finder.get_logical_line_in(lineno)[0]
+            root = suites.ast_suite_tree(self.goal_scope.pyobject.get_ast())
+            suite = root.find_suite(lineno)
+            indents = sourceutils.get_indents(lines, lineno)
+            while self.get_scope_indents() < indents:
+                lineno = suite.get_start()
+                indents = sourceutils.get_indents(lines, lineno)
+                suite = suite.parent
+            return lineno
+        else:
+            return min(self.goal_scope.get_end() + 1, lines.length())
+
+    def get_insertion_resource(self):
+        return self.goal_pymodule.get_resource()
+
+    def get_insertion_offsets(self):
+        if self.goal_scope.get_kind() == 'Class':
+            start, end = sourceutils.get_body_region(self.goal_scope.pyobject)
+            if self.goal_pymodule.source_code[start:end].strip() == 'pass':
+                return start, end
+        lines = self.goal_pymodule.lines
+        start = lines.get_line_start(self.get_insertion_lineno())
+        return (start, start)
+
+    def get_scope_indents(self):
+        if self.goal_scope.get_kind() == 'Module':
+            return 0
+        return sourceutils.get_indents(self.goal_pymodule.lines,
+                                       self.goal_scope.get_start()) + 4
+
+    def get_blank_lines(self):
+        if self.goal_scope.get_kind() == 'Module':
+            base_blanks = 2
+            if self.goal_pymodule.source_code.strip() == '':
+                base_blanks = 0
+        if self.goal_scope.get_kind() == 'Class':
+            base_blanks = 1
+        if self.goal_scope.get_kind() == 'Function':
+            base_blanks = 0
+        if self.goal_scope == self.source_scope:
+            return (0, base_blanks)
+        return (base_blanks, 0)
+
+    def get_package(self):
+        primary = self.primary
+        if self.primary is None:
+            return self.pycore.get_source_folders()[0]
+        if isinstance(primary.get_object(), pyobjects.PyPackage):
+            return primary.get_object().get_resource()
+        raise exceptions.RefactoringError(
+            'A module/package can be only created in a package.')
+
+    def primary_is_found(self):
+        return self.goal_scope is not None
+
+    def element_already_exists(self):
+        if self.pyname is None or isinstance(self.pyname, pynames.UnboundName):
+            return False
+        return True
+
+    def get_name(self):
+        return codeanalyze.get_name_at(self.resource, self.offset)
+
+
+class _FunctionGenerationInfo(_GenerationInfo):
+
+    def _get_goal_scope(self):
+        if self.is_constructor():
+            return self.pyname.get_object().get_scope()
+        if self.is_instance():
+            return self.pyname.get_object().get_type().get_scope()
+        if self.primary is None:
+            return self._get_source_scope()
+        pyobject = self.primary.get_object()
+        if isinstance(pyobject, pyobjects.PyDefinedObject):
+            return pyobject.get_scope()
+        elif isinstance(pyobject.get_type(), pyobjects.PyClass):
+            return pyobject.get_type().get_scope()
+
+    def element_already_exists(self):
+        if self.pyname is None or isinstance(self.pyname, pynames.UnboundName):
+            return False
+        if self.get_name() not in self.goal_scope.get_names():
+            return False
+        return True
+
+    def is_static_method(self):
+        return self.primary is not None and \
+               isinstance(self.primary.get_object(), pyobjects.PyClass)
+
+    def is_method(self):
+        return self.primary is not None and \
+               isinstance(self.primary.get_object().get_type(), pyobjects.PyClass)
+
+    def is_constructor(self):
+        return self.pyname is not None and \
+               isinstance(self.pyname.get_object(), pyobjects.PyClass)
+
+    def is_instance(self):
+        if self.pyname is None:
+            return False
+        pyobject = self.pyname.get_object()
+        return isinstance(pyobject.get_type(), pyobjects.PyClass)
+
+    def get_name(self):
+        if self.is_constructor():
+            return '__init__'
+        if self.is_instance():
+            return '__call__'
+        return codeanalyze.get_name_at(self.resource, self.offset)
+
+    def get_passed_args(self):
+        result = []
+        source = self.source_pymodule.source_code
+        finder = codeanalyze.WordRangeFinder(source)
+        if finder.is_a_function_being_called(self.offset):
+            start, end = finder.get_primary_range(self.offset)
+            parens_start, parens_end = finder.get_word_parens_range(end - 1)
+            call = source[start:parens_end]
+            parser = functionutils._FunctionParser(call, False)
+            args, keywords = parser.get_parameters()
+            for arg in args:
+                if self._is_id(arg):
+                    result.append(arg)
+                else:
+                    result.append('arg%d' % len(result))
+            for name, value in keywords:
+                result.append(name)
+        return result
+
+    def _is_id(self, arg):
+        def id_or_underline(c):
+            return c.isalpha() or c == '_'
+        for c in arg:
+            if not id_or_underline(c) and not c.isdigit():
+                return False
+        return id_or_underline(arg[0])

rope/ide/__init__.py

-"""rope IDE tools package
-
-This package contains modules that can be used in IDEs
-but do not depend on the UI.  So these modules will be used
-by `rope.ui` modules.
-
-"""

rope/ide/codeassist.py

-import keyword
-import re
-import sys
-
-import rope.base.codeanalyze
-from rope.base import pyobjects, pynames, taskhandle, builtins
-from rope.base.codeanalyze import (StatementRangeFinder, ArrayLinesAdapter,
-                                   WordRangeFinder, ScopeNameFinder,
-                                   SourceLinesAdapter, BadIdentifierError)
-from rope.refactor import occurrences, functionutils
-
-
-def code_assist(project, source_code, offset, resource=None, templates={}):
-    """Return python code completions as a list of `CodeAssistProposal`\s
-
-    `resource` is a `rope.base.resources.Resource` object.  If
-    provided things relative imports are handled.
-
-    `templates` should be a dictionary of template name to `Template`
-    objects.  The matches are returned as `TemplateProposal`
-    instances.
-
-    """
-    assist = _PythonCodeAssist(project, templates=templates)
-    return assist.assist(source_code, offset, resource)
-
-
-def starting_offset(source_code, offset):
-    """Return the offset in which the completion should be inserted
-
-    Usually code assist proposals should be inserted like::
-
-        completion = proposal.name # not for templates
-        result = (source_code[:starting_offset] +
-                  completion + source_code[offset:])
-
-    Where starting_offset is the offset returned by this function.
-
-    """
-    word_finder = WordRangeFinder(source_code)
-    expression, starting, starting_offset = \
-        word_finder.get_splitted_primary_before(offset)
-    return starting_offset
-
-
-def get_doc(project, source_code, offset, resource=None):
-    """Get the pydoc"""
-    pymodule = _get_pymodule(project.pycore, source_code, resource)
-    scope_finder = ScopeNameFinder(pymodule)
-    element = scope_finder.get_pyname_at(offset)
-    if element is None:
-        return None
-    pyobject = element.get_object()
-    return PyDocExtractor().get_doc(pyobject)
-
-
-def get_definition_location(project, source_code, offset, resource=None):
-    """Return a (`rope.base.resources.Resource`, lineno) tuple"""
-    pymodule = project.pycore.get_string_module(source_code, resource)
-    scope_finder = ScopeNameFinder(pymodule)
-    element = scope_finder.get_pyname_at(offset)
-    if element is not None:
-        module, lineno = element.get_definition_location()
-        if module is not None:
-            return module.get_module().get_resource(), lineno
-    return (None, None)
-
-
-def find_occurrences(project, resource, offset, unsure=False,
-                     task_handle=taskhandle.NullTaskHandle()):
-    """Return a list of `Location`\s
-
-    if `unsure` is `True`, possible matches are returned, too.
-
-    """
-    name = rope.base.codeanalyze.get_name_at(resource, offset)
-    pyname = rope.base.codeanalyze.get_pyname_at(project.get_pycore(),
-                                                 resource, offset)
-    finder = occurrences.FilteredFinder(
-        project.get_pycore(), name, [pyname], unsure=unsure)
-    files = project.get_pycore().get_python_files()
-    job_set = task_handle.create_jobset('Finding Occurrences',
-                                        count=len(files))
-    result = []
-    for resource in files:
-        job_set.started_job('Working On <%s>' % resource.path)
-        for occurrence in finder.find_occurrences(resource):
-            location = Location()
-            location.resource = resource
-            location.offset = occurrence.get_word_range()[0]
-            location.unsure = occurrence.is_unsure()
-            result.append(location)
-        job_set.finished_job()
-    return result
-
-
-class Location(object):
-
-    resource = None
-    offset = None
-    unsure = False
-
-
-class CodeAssistProposal(object):
-    """The base class for proposals reported by `code_assist`
-
-    The `kind` instance variable shows the kind of the proposal and
-    can be 'global', 'local', 'builtin', 'attribute', 'keyword',
-    'parameter_keyword' and 'template'.
-
-    """
-
-    def __init__(self, name, kind):
-        self.name = name
-        self.kind = kind
-
-
-class CompletionProposal(CodeAssistProposal):
-    """A completion proposal
-
-    The `type` instance variable shows the type of the proposal and
-    can be 'variable', 'class', 'function', 'imported' , 'paramter'
-    and `None`.
-
-    """
-
-    def __init__(self, name, kind, type=None):
-        super(CompletionProposal, self).__init__(name, kind)
-        self.type = type
-
-    def __str__(self):
-        return '%s (%s, %s)' % (self.name, self.kind, self.type)
-
-    def __repr__(self):
-        return str(self)
-
-
-class TemplateProposal(CodeAssistProposal):
-    """A template proposal
-
-    The `template` attribute is a `Template` object.
-    """
-
-    def __init__(self, name, template):
-        super(TemplateProposal, self).__init__(name, 'template')
-        self.template = template
-
-
-class Template(object):
-    """Templates that are used in code assists
-
-    Variables in templates are in ``${variable}`` format. To put a
-    dollar sign in the template put $$.  To specify cursor position
-    use ${cursor}.
-
-    """
-
-    def __init__(self, template):
-        self.template = template
-
-    var_pattern = re.compile(r'((?<=[^\$])|^)\${(?P<variable>\w+)}')
-
-    def variables(self):
-        """Get template variables
-
-        Return the list of variables sorted by their order of
-        occurence in the template.
-
-        """
-        result = []
-        for match in self.var_pattern.finditer(self.template):
-            new_var = match.group('variable')
-            if new_var not in result and new_var != 'cursor':
-                result.append(new_var)
-        return result
-
-    def _substitute(self, input_string, mapping):
-        import string
-        single_dollar = re.compile('((?<=[^\$])|^)\$((?=[^{\$])|$)')
-        template = single_dollar.sub('$$', input_string)
-        t = string.Template(template)
-        return t.substitute(mapping, cursor='')
-
-    def substitute(self, mapping):
-        return self._substitute(self.template, mapping)
-
-    def get_cursor_location(self, mapping):
-        cursor_index = len(self.template)
-        for match in self.var_pattern.finditer(self.template):
-            new_var = match.group('variable')
-            if new_var == 'cursor':
-                cursor_index = match.start('variable') - 2
-        new_template = self.template[0:cursor_index]
-        start = len(self._substitute(new_template, mapping))
-        return start
-
-
-def sorted_proposals(proposals, kindpref=None, typepref=None):
-    """Sort a list of proposals
-
-    Return a sorted list of the given `CodeAssistProposal`\s.
-
-    `typepref` can be a list of proposal kinds.  Defaults to ``['local',
-    'parameter_keyword', 'global', 'attribute', 'template', 'builtin',
-    'keyword']``.
-
-    `typepref` can be a list of proposal types.  Defaults to
-    ``['class', 'function', 'variable', 'parameter', 'imported',
-    None]``.  (`None` stands for completions with no type like
-    keywords.)
-
-    """
-    sorter = _ProposalSorter(proposals, kindpref, typepref)
-    return sorter.get_sorted_proposal_list()
-
-
-def default_templates():
-    templates = {}
-    templates['main'] = Template("if __name__ == '__main__':\n    ${cursor}\n")
-    test_case_template = \
-        ('import unittest\n\n\n'
-         'class ${TestClass}(unittest.TestCase):\n\n'
-         '    def setUp(self):\n        super(${TestClass}, self).setUp()\n\n'
-         '    def tearDown(self):\n        super(${TestClass}, self).tearDown()\n\n'
-         '    def test_trivial_case${cursor}(self):\n        pass\n\n\n'
-         'if __name__ == \'__main__\':\n'
-         '    unittest.main()\n')
-    templates['testcase'] = Template(test_case_template)
-    templates['hash'] = Template('\n    def __hash__(self):\n' +
-                                 '        return 1${cursor}\n')
-    templates['eq'] = Template('\n    def __eq__(self, obj):\n' +
-                               '        ${cursor}return obj is self\n')
-    templates['super'] = Template('super(${class}, self)')
-    return templates
-
-
-class _PythonCodeAssist(object):
-
-    def __init__(self, project, templates={}):
-        self.project = project
-        self.keywords = keyword.kwlist
-        self.templates = templates
-
-    def _find_starting_offset(self, source_code, offset):
-        current_offset = offset - 1
-        while current_offset >= 0 and (source_code[current_offset].isalnum() or
-                                       source_code[current_offset] in '_'):
-            current_offset -= 1;
-        return current_offset + 1
-
-    def _get_matching_keywords(self, starting):
-        result = []
-        for kw in self.keywords:
-            if kw.startswith(starting):
-                result.append(CompletionProposal(kw, 'keyword'))
-        return result
-
-    def add_template(self, name, definition):
-        self.templates.append(TemplateProposal(name, Template(definition)))
-
-    def _get_template_proposals(self, starting):
-        result = []
-        for name, template in self.templates.items():
-            if name.startswith(starting):
-                result.append(TemplateProposal(name, template))
-        return result
-
-    def _get_code_completions(self, source_code, offset,
-                              expression, starting, resource):
-        collector = _CodeCompletionCollector(self.project, source_code, offset,
-                                             expression, starting, resource)
-        return collector.get_code_completions()
-
-    def assist(self, source_code, offset, resource=None):
-        if offset > len(source_code):
-            return []
-        word_finder = WordRangeFinder(source_code)
-        expression, starting, starting_offset = \
-            word_finder.get_splitted_primary_before(offset)
-        completions = list(
-            self._get_code_completions(source_code, offset, expression,
-                                       starting, resource).values())
-        if expression.strip() == '' and starting.strip() != '':
-            completions.extend(self._get_matching_keywords(starting))
-            completions.extend(self._get_template_proposals(starting))
-        return completions
-
-
-class _ProposalSorter(object):
-    """Sort a list of code assist proposals"""
-
-    def __init__(self, code_assist_proposals, kindpref=None, typepref=None):
-        self.proposals = code_assist_proposals
-        if kindpref is None:
-            kindpref = ['local', 'parameter_keyword', 'global', 'attribute',
-                        'template', 'builtin', 'keyword']
-        self.kindpref = kindpref
-        if typepref is None:
-            typepref = ['class', 'function', 'variable',
-                        'parameter', 'imported', None]
-        self.typerank = dict((type, index)
-                              for index, type in enumerate(typepref))
-
-    def get_sorted_proposal_list(self):
-        """Return a list of `CodeAssistProposal`"""
-        proposals = {}
-        for proposal in self.proposals:
-            proposals.setdefault(proposal.kind, []).append(proposal)
-        result = []
-        for kind in self.kindpref:
-            kind_proposals = proposals.get(kind, [])
-            if kind != 'template':
-                kind_proposals = [proposal for proposal in kind_proposals
-                                  if proposal.type in self.typerank]
-            kind_proposals.sort(self._proposal_cmp)
-            result.extend(kind_proposals)
-        return result
-
-    def _proposal_cmp(self, proposal1, proposal2):
-        if 'template' not in (proposal1.kind, proposal2.kind) and \
-           proposal1.type != proposal2.type:
-            return cmp(self.typerank.get(proposal1.type, 100),
-                       self.typerank.get(proposal2.type, 100))
-        return self._compare_underlined_names(proposal1.name,
-                                              proposal2.name)
-
-    def _compare_underlined_names(self, name1, name2):
-        def underline_count(name):
-            result = 0
-            while result < len(name) and name[result] == '_':
-                result += 1
-            return result
-        underline_count1 = underline_count(name1)
-        underline_count2 = underline_count(name2)
-        if underline_count1 != underline_count2:
-            return cmp(underline_count1, underline_count2)
-        return cmp(name1, name2)
-
-
-class _CodeCompletionCollector(object):
-
-    def __init__(self, project, source_code,
-                 offset, expression, starting, resource):
-        self.project = project
-        self.expression = expression
-        self.starting = starting
-        self.offset = offset
-        self.pycore = self.project.get_pycore()
-        self.unchanged_source = source_code
-        self.lines = source_code.split('\n')
-        self.source_code = source_code
-        self.resource = resource
-        source_lines = SourceLinesAdapter(source_code)
-        self.lineno = source_lines.get_line_number(offset)
-        self.current_indents = self._get_line_indents(
-            source_lines.get_line(self.lineno))
-        self._comment_current_statement()
-        self.source_code = '\n'.join(self.lines)
-
-    def _get_line_indents(self, line):
-        return rope.base.codeanalyze.count_line_indents(line)
-
-    def _comment_current_statement(self):
-        range_finder = StatementRangeFinder(ArrayLinesAdapter(self.lines),
-                                            self.lineno)
-        start = range_finder.get_statement_start() - 1
-        end = range_finder.get_block_end() - 1
-        last_indents = self._get_line_indents(self.lines[start])
-        self.lines[start] = ' ' * last_indents + 'pass'
-        for line in range(start + 1, end + 1):
-            #self.lines[line] = '#' # + lines[line]
-            self.lines[line] = self.lines[start]
-        self.lines.append('\n')
-        self._fix_incomplete_try_blocks()
-
-    def _fix_incomplete_try_blocks(self):
-        block_start = self.lineno
-        last_indents = self.current_indents
-        while block_start > 0:
-            block_start = rope.base.codeanalyze.get_block_start(
-                ArrayLinesAdapter(self.lines), block_start) - 1
-            if self.lines[block_start].strip().startswith('try:'):
-                indents = self._get_line_indents(self.lines[block_start])
-                if indents > last_indents:
-                    continue
-                last_indents = indents
-                block_end = self._find_matching_deindent(block_start)
-                if not self.lines[block_end].strip().startswith('finally:') and \
-                   not self.lines[block_end].strip().startswith('except '):
-                    self.lines.insert(block_end, ' ' * indents + 'finally:')
-                    self.lines.insert(block_end + 1, ' ' * indents + '    pass')
-
-    def _find_matching_deindent(self, line_number):
-        indents = self._get_line_indents(self.lines[line_number])
-        current_line = line_number + 1
-        while current_line < len(self.lines):
-            line = self.lines[current_line]
-            if not line.strip().startswith('#') and not line.strip() == '':
-                # HACK: We should have used logical lines here
-                if self._get_line_indents(self.lines[current_line]) <= indents:
-                    return current_line
-            current_line += 1
-        return len(self.lines) - 1
-
-    def _get_dotted_completions(self, module_scope, holding_scope):
-        result = {}
-        pyname_finder = ScopeNameFinder(module_scope.pyobject)
-        found_pyname = pyname_finder.get_pyname_in_scope(holding_scope,
-                                                         self.expression)
-        if found_pyname is not None:
-            element = found_pyname.get_object()
-            for name, pyname in element.get_attributes().items():
-                if name.startswith(self.starting) or self.starting == '':
-                    result[name] = CompletionProposal(
-                        name, 'attribute', self._get_pyname_type(pyname))
-        return result
-
-    def _get_undotted_completions(self, scope, result, propagated=False):
-        if scope.parent != None:
-            self._get_undotted_completions(scope.parent, result,
-                                           propagated=True)
-        if propagated:
-            names = scope.get_propagated_names()
-        else:
-            names = scope.get_names()
-        for name, pyname in names.items():
-            if name.startswith(self.starting):
-                kind = 'local'
-                if isinstance(pyname, builtins.BuiltinName):
-                    kind = 'builtin'
-                elif scope.get_kind() == 'Module':
-                    kind = 'global'
-                result[name] = CompletionProposal(
-                    name, kind, self._get_pyname_type(pyname))
-
-    def _get_pyname_type(self, pyname):
-        if isinstance(pyname, (pynames.AssignedName, pynames.UnboundName)):
-            return 'variable'
-        if isinstance(pyname, pynames.ImportedName) or \
-           isinstance(pyname, pynames.ImportedModule):
-            return 'imported'
-        if isinstance(pyname, pynames.ParameterName):
-            return 'parameter'
-        if isinstance(pyname, builtins.BuiltinName) or \
-           isinstance(pyname, pynames.DefinedName):
-            pyobject = pyname.get_object()
-            if isinstance(pyobject, pyobjects.AbstractFunction):
-                return 'function'
-            if isinstance(pyobject, pyobjects.AbstractClass):
-                return 'class'
-
-    def get_code_completions(self):
-        module_scope = _get_pymodule(self.pycore, self.source_code,
-                                    self.resource).get_scope()
-        result = {}
-        inner_scope = module_scope.get_inner_scope_for_line(self.lineno,
-                                                            self.current_indents)
-        if self.expression.strip() != '':
-            result.update(self._get_dotted_completions(module_scope,
-                                                       inner_scope))
-        else:
-            result.update(self._get_keyword_parameters(module_scope.pyobject,
-                                                       inner_scope))
-            self._get_undotted_completions(inner_scope, result)
-        return result
-
-    def _get_keyword_parameters(self, pymodule, scope):
-        offset = self.offset
-        if offset == 0:
-            return {}
-        word_finder = WordRangeFinder(self.unchanged_source)
-        lines = SourceLinesAdapter(self.unchanged_source)
-        lineno = lines.get_line_number(offset)
-        stop_line = StatementRangeFinder(lines, lineno).get_statement_start()
-        stop = lines.get_line_start(stop_line)
-        if word_finder.is_on_function_call_keyword(offset - 1, stop):
-            name_finder = ScopeNameFinder(pymodule)
-            function_parens = word_finder.find_parens_start_from_inside(offset - 1, stop)
-            primary = word_finder.get_primary_at(function_parens - 1)
-            try:
-                function_pyname = ScopeNameFinder.get_pyname_in_scope(scope,
-                                                                      primary)
-            except BadIdentifierError, e:
-                return {}
-            if function_pyname is not None:
-                pyobject = function_pyname.get_object()
-                if isinstance(pyobject, pyobjects.AbstractFunction):
-                    pass
-                elif isinstance(pyobject, pyobjects.AbstractClass) and \
-                     '__init__' in pyobject.get_attributes():
-                    pyobject = pyobject.get_attribute('__init__').get_object()
-                elif '__call__' in pyobject.get_attributes():
-                    pyobject = pyobject.get_attribute('__call__').get_object()
-                if isinstance(pyobject, pyobjects.AbstractFunction):
-                    param_names = []
-                    param_names.extend(
-                        pyobject.get_param_names(special_args=False))
-                    result = {}
-                    for name in param_names:
-                        if name.startswith(self.starting):
-                            result[name + '='] = CompletionProposal(
-                                name + '=', 'parameter_keyword')
-                    return result
-        return {}
-
-
-def _get_pymodule(pycore, source_code, resource):
-    if resource and resource.exists() and source_code == resource.read():
-        return pycore.resource_to_pyobject(resource)
-    return pycore.get_string_module(source_code, resource=resource)
-
-
-class PyDocExtractor(object):
-
-    def get_doc(self, pyobject):
-        if isinstance(pyobject, pyobjects.AbstractFunction):
-            return self._get_function_docstring(pyobject)
-        elif isinstance(pyobject, pyobjects.AbstractClass):
-            return self._get_class_docstring(pyobject)
-        elif isinstance(pyobject, pyobjects.AbstractModule):
-            return self._trim_docstring(pyobject.get_doc())
-        return None
-
-    def _get_class_docstring(self, pyclass):
-        contents = self._trim_docstring(pyclass.get_doc(), 2)
-        supers = [super.get_name() for super in pyclass.get_superclasses()]
-        doc = 'class %s(%s):\n\n' % (pyclass.get_name(), ', '.join(supers)) + contents
-
-        if '__init__' in pyclass.get_attributes():
-            init = pyclass.get_attribute('__init__').get_object()
-            if isinstance(init, pyobjects.AbstractFunction):
-                doc += '\n\n' + self._get_single_function_docstring(init)
-        return doc
-
-    def _get_function_docstring(self, pyfunction):
-        functions = [pyfunction]
-        if self._is_method(pyfunction):
-            functions.extend(self._get_super_methods(pyfunction.parent,
-                                                     pyfunction.get_name()))
-        return '\n\n'.join([self._get_single_function_docstring(function)
-                            for function in functions])
-
-    def _is_method(self, pyfunction):
-        return isinstance(pyfunction, pyobjects.PyFunction) and \
-               isinstance(pyfunction.parent, pyobjects.PyClass)
-
-    def _get_single_function_docstring(self, pyfunction):
-        signature = self._get_function_signature(pyfunction)
-        if self._is_method(pyfunction):
-            signature = pyfunction.parent.get_name() + '.' + signature
-            self._get_super_methods(pyfunction.parent, pyfunction.get_name())
-        return signature + ':\n\n' + self._trim_docstring(pyfunction.get_doc(),
-                                                         indents=2)
-
-    def _get_super_methods(self, pyclass, name):
-        result = []
-        for super_class in pyclass.get_superclasses():
-            if name in super_class.get_attributes():
-                function = super_class.get_attribute(name).get_object()
-                if isinstance(function, pyobjects.AbstractFunction):
-                    result.append(function)
-            result.extend(self._get_super_methods(super_class, name))
-        return result
-
-    def _get_function_signature(self, pyfunction):
-        if isinstance(pyfunction, pyobjects.PyFunction):
-            info = functionutils.DefinitionInfo.read(pyfunction)
-            return info.to_string()
-        else:
-            return '%s(%s)' % (pyfunction.get_name(),
-                               ', '.join(pyfunction.get_param_names()))
-
-    def _trim_docstring(self, docstring, indents=0):
-        """The sample code from :PEP:`257`"""
-        if not docstring:
-            return ''
-        # Convert tabs to spaces (following normal Python rules)
-        # and split into a list of lines:
-        lines = docstring.expandtabs().splitlines()
-        # Determine minimum indentation (first line doesn't count):
-        indent = sys.maxint
-        for line in lines[1:]:
-            stripped = line.lstrip()
-            if stripped:
-                indent = min(indent, len(line) - len(stripped))
-        # Remove indentation (first line is special):
-        trimmed = [lines[0].strip()]
-        if indent < sys.maxint:
-            for line in lines[1:]:
-                trimmed.append(line[indent:].rstrip())
-        # Strip off trailing and leading blank lines:
-        while trimmed and not trimmed[-1]:
-            trimmed.pop()
-        while trimmed and not trimmed[0]:
-            trimmed.pop(0)
-        # Return a single string:
-        return '\n'.join((' ' * indents + line for line in trimmed))

rope/ide/generate.py

-from rope.base import change, codeanalyze, pyobjects, exceptions, pynames
-from rope.refactor import sourceutils, importutils, functionutils, suites
-
-
-def create_generate(kind, project, resource, offset):
-    """A factory for creating `Generate` objects
-
-    `kind` can be 'variable', 'function', 'class', 'module' or
-    'package'.
-
-    """
-    generate = eval('Generate' + kind.title())
-    return generate(project, resource, offset)
-
-
-class _Generate(object):
-
-    def __init__(self, project, resource, offset):
-        self.project = project
-        self.resource = resource
-        self.info = self._generate_info(project, resource, offset)
-        self.name = self.info.get_name()
-        self._check_exceptional_conditions()
-
-    def _generate_info(self, project, resource, offset):
-        return _GenerationInfo(project.pycore, resource, offset)
-
-    def _check_exceptional_conditions(self):
-        if self.info.element_already_exists():
-            raise exceptions.RefactoringError(
-                'Element <%s> already exists.' % self.name)
-        if not self.info.primary_is_found():
-            raise exceptions.RefactoringError(
-                'Cannot determine the scope <%s> should be defined in.' % self.name)
-
-    def get_changes(self):
-        changes = change.ChangeSet('Generate %s <%s>' %
-                                   (self._get_element_kind(), self.name))
-        indents = self.info.get_scope_indents()
-        blanks = self.info.get_blank_lines()
-        base_definition = sourceutils.fix_indentation(self._get_element(), indents)
-        definition = '\n' * blanks[0] + base_definition + '\n' * blanks[1]
-
-        resource = self.info.get_insertion_resource()
-        start, end = self.info.get_insertion_offsets()
-
-        collector = sourceutils.ChangeCollector(resource.read())
-        collector.add_change(start, end, definition)
-        changes.add_change(change.ChangeContents(
-                           resource, collector.get_changed()))
-        return changes
-
-    def get_location(self):
-        return (self.info.get_insertion_resource(),
-                self.info.get_insertion_lineno())
-
-    def _get_element_kind(self):
-        raise NotImplementedError()
-
-    def _get_element(self):
-        raise NotImplementedError()
-
-
-class GenerateFunction(_Generate):
-
-    def _generate_info(self, project, resource, offset):
-        return _FunctionGenerationInfo(project.pycore, resource, offset)
-
-    def _get_element(self):
-        decorator = ''
-        args = []
-        if self.info.is_static_method():
-            decorator = '@staticmethod\n'
-        if self.info.is_method() or self.info.is_constructor() or \
-           self.info.is_instance():
-            args.append('self')
-        args.extend(self.info.get_passed_args())
-        definition = '%sdef %s(%s):\n    pass\n' % (decorator, self.name,
-                                                    ', '.join(args))
-        return definition
-
-    def _get_element_kind(self):
-        return 'Function'
-
-
-class GenerateVariable(_Generate):
-
-    def _get_element(self):
-        return '%s = None\n' % self.name
-
-    def _get_element_kind(self):
-        return 'Variable'
-
-
-class GenerateClass(_Generate):
-
-    def _get_element(self):
-        return 'class %s(object):\n    pass\n' % self.name
-
-    def _get_element_kind(self):
-        return 'Class'
-
-
-class GenerateModule(_Generate):
-
-    def get_changes(self):
-        package = self.info.get_package()
-        changes = change.ChangeSet('Generate Module <%s>' % self.name)
-        new_resource = self.project.get_file('%s/%s.py' % (package.path, self.name))
-        if new_resource.exists():
-            raise exceptions.RefactoringError(
-                'Module <%s> already exists' % new_resource.path)
-        changes.add_change(change.CreateResource(new_resource))
-        changes.add_change(_add_import_to_module(
-                           self.project.get_pycore(), self.resource, new_resource))
-        return changes
-
-    def get_location(self):
-        package = self.info.get_package()
-        return (package.get_child('%s.py' % self.name) , 1)
-
-
-class GeneratePackage(_Generate):
-
-    def get_changes(self):
-        package = self.info.get_package()
-        changes = change.ChangeSet('Generate Package <%s>' % self.name)
-        new_resource = self.project.get_folder('%s/%s' % (package.path, self.name))
-        if new_resource.exists():
-            raise exceptions.RefactoringError(
-                'Package <%s> already exists' % new_resource.path)
-        changes.add_change(change.CreateResource(new_resource))
-        changes.add_change(_add_import_to_module(
-                           self.project.get_pycore(), self.resource, new_resource))
-        child = self.project.get_folder(package.path + '/' + self.name)
-        changes.add_change(change.CreateFile(child, '__init__.py'))
-        return changes
-
-    def get_location(self):
-        package = self.info.get_package()
-        child = package.get_child(self.name)
-        return (child.get_child('__init__.py') , 1)
-
-
-def _add_import_to_module(pycore, resource, imported):
-    pymodule = pycore.resource_to_pyobject(resource)
-    import_tools = importutils.ImportTools(pycore)
-    module_imports = import_tools.get_module_imports(pymodule)
-    module_name = importutils.get_module_name(pycore, imported)
-    new_import = importutils.NormalImport(((module_name, None), ))
-    module_imports.add_import(new_import)
-    return change.ChangeContents(resource, module_imports.get_changed_source())
-
-
-class _GenerationInfo(object):
-
-    def __init__(self, pycore, resource, offset):
-        self.pycore = pycore
-        self.resource = resource
-        self.offset = offset
-        self.source_pymodule = self.pycore.resource_to_pyobject(resource)
-        finder = codeanalyze.ScopeNameFinder(self.source_pymodule)
-        self.primary, self.pyname = finder.get_primary_and_pyname_at(offset)
-        self._init_fields()
-
-    def _init_fields(self):
-        self.source_scope = self._get_source_scope()
-        self.goal_scope = self._get_goal_scope()
-        self.goal_pymodule = self._get_goal_module(self.goal_scope)
-
-    def _get_goal_scope(self):
-        if self.primary is None:
-            return self._get_source_scope()
-        pyobject = self.primary.get_object()
-        if isinstance(pyobject, pyobjects.PyDefinedObject):
-            return pyobject.get_scope()
-        elif isinstance(pyobject.get_type(), pyobjects.PyClass):
-            return pyobject.get_type().get_scope()
-
-    def _get_goal_module(self, scope):
-        if scope is None:
-            return
-        while scope.parent is not None:
-            scope = scope.parent
-        return scope.pyobject
-
-    def _get_source_scope(self):
-        module_scope = self.source_pymodule.get_scope()
-        lineno = self.source_pymodule.lines.get_line_number(self.offset)
-        return module_scope.get_inner_scope_for_line(lineno)
-
-    def get_insertion_lineno(self):
-        lines = self.goal_pymodule.lines
-        if self.goal_scope == self.source_scope:
-            line_finder = codeanalyze.LogicalLineFinder(lines)
-            lineno = lines.get_line_number(self.offset)
-            lineno = line_finder.get_logical_line_in(lineno)[0]
-            root = suites.ast_suite_tree(self.goal_scope.pyobject.get_ast())
-            suite = root.find_suite(lineno)
-            indents = sourceutils.get_indents(lines, lineno)
-            while self.get_scope_indents() < indents:
-                lineno = suite.get_start()
-                indents = sourceutils.get_indents(lines, lineno)
-                suite = suite.parent
-            return lineno
-        else:
-            return min(self.goal_scope.get_end() + 1, lines.length())
-
-    def get_insertion_resource(self):
-        return self.goal_pymodule.get_resource()
-
-    def get_insertion_offsets(self):
-        if self.goal_scope.get_kind() == 'Class':
-            start, end = sourceutils.get_body_region(self.goal_scope.pyobject)
-            if self.goal_pymodule.source_code[start:end].strip() == 'pass':
-                return start, end
-        lines = self.goal_pymodule.lines
-        start = lines.get_line_start(self.get_insertion_lineno())
-        return (start, start)
-
-    def get_scope_indents(self):
-        if self.goal_scope.get_kind() == 'Module':
-            return 0
-        return sourceutils.get_indents(self.goal_pymodule.lines,
-                                       self.goal_scope.get_start()) + 4
-
-    def get_blank_lines(self):
-        if self.goal_scope.get_kind() == 'Module':
-            base_blanks = 2
-            if self.goal_pymodule.source_code.strip() == '':
-                base_blanks = 0
-        if self.goal_scope.get_kind() == 'Class':
-            base_blanks = 1
-        if self.goal_scope.get_kind() == 'Function':
-            base_blanks = 0
-        if self.goal_scope == self.source_scope:
-            return (0, base_blanks)
-        return (base_blanks, 0)
-
-    def get_package(self):
-        primary = self.primary
-        if self.primary is None:
-            return self.pycore.get_source_folders()[0]
-        if isinstance(primary.get_object(), pyobjects.PyPackage):
-            return primary.get_object().get_resource()
-        raise exceptions.RefactoringError(
-            'A module/package can be only created in a package.')
-
-    def primary_is_found(self):
-        return self.goal_scope is not None
-
-    def element_already_exists(self):
-        if self.pyname is None or isinstance(self.pyname, pynames.UnboundName):
-            return False
-        return True
-
-    def get_name(self):
-        return codeanalyze.get_name_at(self.resource, self.offset)
-
-
-class _FunctionGenerationInfo(_GenerationInfo):
-
-    def _get_goal_scope(self):
-        if self.is_constructor():
-            return self.pyname.get_object().get_scope()
-        if self.is_instance():
-            return self.pyname.get_object().get_type().get_scope()
-        if self.primary is None:
-            return self._get_source_scope()
-        pyobject = self.primary.get_object()
-        if isinstance(pyobject, pyobjects.PyDefinedObject):
-            return pyobject.get_scope()
-        elif isinstance(pyobject.get_type(), pyobjects.PyClass):
-            return pyobject.get_type().get_scope()
-
-    def element_already_exists(self):
-        if self.pyname is None or isinstance(self.pyname, pynames.UnboundName):
-            return False
-        if self.get_name() not in self.goal_scope.get_names():
-            return False
-        return True
-
-    def is_static_method(self):
-        return self.primary is not None and \
-               isinstance(self.primary.get_object(), pyobjects.PyClass)
-
-    def is_method(self):
-        return self.primary is not None and \
-               isinstance(self.primary.get_object().get_type(), pyobjects.PyClass)
-
-    def is_constructor(self):
-        return self.pyname is not None and \
-               isinstance(self.pyname.get_object(), pyobjects.PyClass)
-
-    def is_instance(self):
-        if self.pyname is None:
-            return False
-        pyobject = self.pyname.get_object()
-        return isinstance(pyobject.get_type(), pyobjects.PyClass)
-
-    def get_name(self):
-        if self.is_constructor():
-            return '__init__'
-        if self.is_instance():
-            return '__call__'
-        return codeanalyze.get_name_at(self.resource, self.offset)
-
-    def get_passed_args(self):
-        result = []
-        source = self.source_pymodule.source_code
-        finder = codeanalyze.WordRangeFinder(source)
-        if finder.is_a_function_being_called(self.offset):
-            start, end = finder.get_primary_range(self.offset)
-            parens_start, parens_end = finder.get_word_parens_range(end - 1)
-            call = source[start:parens_end]
-            parser = functionutils._FunctionParser(call, False)
-            args, keywords = parser.get_parameters()
-            for arg in args:
-                if self._is_id(arg):
-                    result.append(arg)
-                else:
-                    result.append('arg%d' % len(result))
-            for name, value in keywords:
-                result.append(name)
-        return result
-
-    def _is_id(self, arg):
-        def id_or_underline(c):
-            return c.isalpha() or c == '_'
-        for c in arg:
-            if not id_or_underline(c) and not c.isdigit():
-                return False
-        return id_or_underline(arg[0])

ropeide/editingtools.py

-import rope.ide.codeassist
+import rope.contrib.codeassist
 import rope.refactor.sourceutils
 import ropeide.highlighter
 import ropeide.indenter

ropeide/editor.py

 import Tkinter
 from Tkinter import END, TclError, SEL_FIRST, SEL, SEL_LAST, INSERT, Toplevel, Text
 
-import rope.ide.codeassist
+import rope.contrib.codeassist
 import ropeide.editingtools
 import ropeide.searcher
 import ropeide.tkhelpers

ropeide/sourceactions.py

 import ScrolledText
 import Tkinter
 
-import rope.ide.codeassist
+import rope.contrib.codeassist
 import ropeide.core
 import ropeide.testview
 from rope.base import codeanalyze
-from rope.ide import generate
+from rope.contrib import generate
 from ropeide import spelldialog, registers
 from ropeide.actionhelpers import ConfirmEditorsAreSaved, StoppableTaskRunner
 from ropeide.extension import SimpleAction
 
     def entry_to_string(self, proposal):
         mode = '  '
-        if isinstance(proposal, rope.ide.codeassist.TemplateProposal):
+        if isinstance(proposal, rope.contrib.codeassist.TemplateProposal):
             mode = 'T_'
-        if isinstance(proposal, rope.ide.codeassist.CompletionProposal):
+        if isinstance(proposal, rope.contrib.codeassist.CompletionProposal):
             if proposal.type is None:
                 mode = proposal.kind[0].upper() + '_'
             else:
         self.toplevel.destroy()
 
     def selected(self, selected):
-        if isinstance(selected, rope.ide.codeassist.TemplateProposal):
+        if isinstance(selected, rope.contrib.codeassist.TemplateProposal):
             _get_template_information(self.editor, selected, self.start_offset)
         else:
             self.editor.text.delete('0.0 +%dc' % self.start_offset,
         editor = context.get_active_editor().get_editor()
         source = editor.get_text()
         offset = editor.get_current_offset()
-        result = rope.ide.codeassist.code_assist(
+        result = rope.contrib.codeassist.code_assist(
             context.project, source, offset, context.resource,
             templates=self._get_templates(context))
-        proposals = rope.ide.codeassist.sorted_proposals(result)
-        start_offset = rope.ide.codeassist.starting_offset(source, offset)
+        proposals = rope.contrib.codeassist.sorted_proposals(result)
+        start_offset = rope.contrib.codeassist.starting_offset(source, offset)
         toplevel = Tkinter.Toplevel()
         toplevel.title('Code Assist Proposals')
         handle = _CompletionListHandle(editor, toplevel, start_offset)
 
     def _get_templates(self, context):
         if self._templates is None:
-            templates = rope.ide.codeassist.default_templates()
+            templates = rope.contrib.codeassist.default_templates()
             for name, definition in (context.core.get_prefs().
                                      get('templates', [])):
-                templates[name] = rope.ide.codeassist.Template(definition)
+                templates[name] = rope.contrib.codeassist.Template(definition)
             self._templates = templates
         return self._templates
 
 
 def do_goto_definition(context):
     editor = context.get_active_editor().get_editor()
-    resource, lineno = rope.ide.codeassist.get_definition_location(
+    resource, lineno = rope.contrib.codeassist.get_definition_location(
         context.project, editor.get_text(),
         editor.get_current_offset(), context.resource)
     if resource is not None:
     if not context.get_active_editor():
         return
     editor = context.get_active_editor().get_editor()
-    doc = rope.ide.codeassist.get_doc(
+    doc = rope.contrib.codeassist.get_doc(
         context.project, editor.get_text(),
         editor.get_current_offset(), context.resource)
     if doc is not None:
     resource = context.resource
     offset = context.editor.get_current_offset()
     def calculate(handle):
-        return rope.ide.codeassist.find_occurrences(
+        return rope.contrib.codeassist.find_occurrences(
             context.project, resource, offset,
             unsure=True, task_handle=handle)
     result = StoppableTaskRunner(calculate, title='Finding Occurrences')()
 import rope.refactor.move
 import rope.refactor.rename
 from rope.base import project, libutils
-from rope.ide import codeassist
+from rope.contrib import codeassist
 
 
 class interactive(object):

ropetest/ide/codeassisttest.py

 import unittest
 
 from rope.base import exceptions
-from rope.ide.codeassist import \
+from rope.contrib.codeassist import \
      (Template, get_definition_location, get_doc,
       find_occurrences, code_assist, sorted_proposals, starting_offset)
 from ropetest import testutils

ropetest/ide/generatetest.py

 import unittest
 
 from rope.base import exceptions
-from rope.ide import generate
+from rope.contrib import generate
 from ropetest import testutils
 
 

tools/ropeide_setup.py

         os.mkdir('scripts')
     shutil.copy('ropeide.py', 'scripts/ropeide')
     # copying docs
-    if not os.path.exists('rope/docs'):
-        os.mkdir('rope/docs')
+    if not os.path.exists('ropeide/docs'):
+        os.mkdir('ropeide/docs')
     docs = ['README.txt', 'COPYING']
     docs.extend(glob.glob('docs/*.txt'))
     for name in docs:
-        shutil.copy(name, 'rope/docs/')
+        shutil.copy(name, 'ropeide/docs/')
 
 def remove_temps():
     if os.path.exists('scripts'):
         shutil.rmtree('scripts')
-    if os.path.exists('rope/docs'):
-        shutil.rmtree('rope/docs')
+    if os.path.exists('ropeide/docs'):
+        shutil.rmtree('ropeide/docs')
 
 classifiers=[
     'Development Status :: 4 - Beta',
           author='Ali Gholami Rudi',
           author_email='aligrudi@users.sourceforge.net',
           url='http://rope.sf.net/',
-          packages=['rope', 'rope.base', 'rope.base.oi', 'rope.refactor',
-                    'rope.refactor.importutils', 'rope.ide', 'ropeide',
-                    'ropemacs'],
-          package_data={'rope': ['docs/COPYING', 'docs/*.txt']},
+          packages=['ropeide'],
+          package_data={'ropeide': ['docs/COPYING', 'docs/*.txt']},
           scripts=['scripts/ropeide'],
           license='GNU GPL',
           classifiers=classifiers)