davidhalter / jedi

Awesome autocompletion, static analysis and refactoring library for python
http://jedi.readthedocs.io
Other
5.81k stars 508 forks source link

Refactoring with the new parser #667

Closed davidhalter closed 4 years ago

davidhalter commented 8 years ago

Note: #103 Discussed a lot of things about introducing refactoring. Now that we're a step further, let's discuss the API. This writeup is the result of someone wanting to contribute towards refactoring.

Refactoring

It's been a while, but since I've been ask how to tackle the issue, let's start simple:

  1. Use the latest branch (currently linter)
  2. Small of initial refactorings.
  3. Specify the API is unfortunately the first thing we need to do. Very happy for feedback there.
  4. Testing

The first and easiest refactorings that I'd like to see are:

One of the next steps could be (or basically anything else):

I'm suggesting the following API (note that Script is given a pointer (line / column index) as well as the path of the file::

def rename(script: jedi.Script, new_name: str) -> Refactoring:

def extract(script: jedi.Script, new_name: str, end: Tuple[int, int]): -> Refactoring
    """
    The starting position is the line/column input of jedi.Script and the
    `end` position the last place, that you want to exclude in your
    exctract logic.
    """

# Doesn't need anything additional, since it just deletes:
def inline(script: jedi.Script -> Refactoring):

Of course the bigger work is defining the return class::

class Refactoring():
    def __init__(self, whatever_we_need_its_private):
        pass

    def get_changed_files(self):

    def get_renames(self) -> List[Tuple[str, str]]:
        """
        Files can be renamed in a refactoring.
        """

    def diff(self) -> str:

class RefactoringError(Exception):
    def __init__(self, message: str):

Please critize it! I'm very happy for all the feedback, especially if you don't like it. Annotations will of course be removed (and changed to docstrings), because we have to maintain Python 2/3 compatibility.

Testing

Since I have previously started a bit of work on refactoring, I think it would be very beneficial to use that test suite. There's a small list of tests already in test/refactor/* (renaming, extract/inline variable). I'm very happy to give pointers about how to use them. Currently they are disabled. Just enable the switch again in test/test_integration.py.

Issues

Currently there's only one known issue (will add to this, if there's more): The parser does error recovery. For now just ignore this, but when we're pushing this to the users, we need to take care of it.

Hints

If you are implementing all of this, you have to use the parser tree's jedi.parser.tree logic. If you think that doing something requires a lot of code, you're probably wrong (or the code should just be part of the tree).

Generally a refactoring has 3 steps:

The module jedi/refactor.py has a very similar class structure as the one proposed above, but it doesn't work. You can throw away basically anything. It can give you an idea how the structure would look like, but the code is crap. THROW IT AWAY.

Please let me know if you need any help to start your work.

davidhalter commented 4 years ago

This was finally implemented. Now the following methods exist in the refactoring branch: inline, extract_variable, extract_function and rename.

For various reasons, there is not going to be more than that for now. If you want some other refactoring methods (or even better you're testing this), please create new issues.

Will be part of 0.17.0.