222 changed files with 1077 additions and 2104 deletions
-
11elpa/elpy-1.35.0/elpy-pkg.el
-
297elpa/elpy-1.35.0/elpy-refactor.el
-
381elpa/elpy-1.35.0/elpy/refactor.py
-
545elpa/elpy-1.35.0/elpy/tests/test_refactor.py
-
7elpa/elpy-1.35.0/snippets/python-mode/__abs__
-
7elpa/elpy-1.35.0/snippets/python-mode/__add__
-
7elpa/elpy-1.35.0/snippets/python-mode/__and__
-
7elpa/elpy-1.35.0/snippets/python-mode/__bool__
-
7elpa/elpy-1.35.0/snippets/python-mode/__call__
-
7elpa/elpy-1.35.0/snippets/python-mode/__cmp__
-
7elpa/elpy-1.35.0/snippets/python-mode/__coerce__
-
7elpa/elpy-1.35.0/snippets/python-mode/__complex__
-
7elpa/elpy-1.35.0/snippets/python-mode/__contains__
-
7elpa/elpy-1.35.0/snippets/python-mode/__del__
-
7elpa/elpy-1.35.0/snippets/python-mode/__delattr__
-
7elpa/elpy-1.35.0/snippets/python-mode/__delete__
-
7elpa/elpy-1.35.0/snippets/python-mode/__delitem__
-
7elpa/elpy-1.35.0/snippets/python-mode/__div__
-
7elpa/elpy-1.35.0/snippets/python-mode/__divmod__
-
9elpa/elpy-1.35.0/snippets/python-mode/__enter__
-
7elpa/elpy-1.35.0/snippets/python-mode/__eq__
-
7elpa/elpy-1.35.0/snippets/python-mode/__exit__
-
7elpa/elpy-1.35.0/snippets/python-mode/__float__
-
7elpa/elpy-1.35.0/snippets/python-mode/__floordiv__
-
7elpa/elpy-1.35.0/snippets/python-mode/__ge__
-
7elpa/elpy-1.35.0/snippets/python-mode/__get__
-
7elpa/elpy-1.35.0/snippets/python-mode/__getattr__
-
7elpa/elpy-1.35.0/snippets/python-mode/__getattribute__
-
7elpa/elpy-1.35.0/snippets/python-mode/__getitem__
-
7elpa/elpy-1.35.0/snippets/python-mode/__gt__
-
7elpa/elpy-1.35.0/snippets/python-mode/__hash__
-
7elpa/elpy-1.35.0/snippets/python-mode/__hex__
-
7elpa/elpy-1.35.0/snippets/python-mode/__iadd__
-
7elpa/elpy-1.35.0/snippets/python-mode/__iand__
-
7elpa/elpy-1.35.0/snippets/python-mode/__idiv__
-
7elpa/elpy-1.35.0/snippets/python-mode/__ifloordiv__
-
7elpa/elpy-1.35.0/snippets/python-mode/__ilshift__
-
7elpa/elpy-1.35.0/snippets/python-mode/__imod__
-
7elpa/elpy-1.35.0/snippets/python-mode/__imul__
-
7elpa/elpy-1.35.0/snippets/python-mode/__index__
-
10elpa/elpy-1.35.0/snippets/python-mode/__init__
-
7elpa/elpy-1.35.0/snippets/python-mode/__instancecheck__
-
7elpa/elpy-1.35.0/snippets/python-mode/__int__
-
7elpa/elpy-1.35.0/snippets/python-mode/__invert__
-
7elpa/elpy-1.35.0/snippets/python-mode/__ior__
-
7elpa/elpy-1.35.0/snippets/python-mode/__ipow__
-
7elpa/elpy-1.35.0/snippets/python-mode/__irshift__
-
7elpa/elpy-1.35.0/snippets/python-mode/__isub__
-
7elpa/elpy-1.35.0/snippets/python-mode/__iter__
-
7elpa/elpy-1.35.0/snippets/python-mode/__itruediv__
-
7elpa/elpy-1.35.0/snippets/python-mode/__ixor__
-
7elpa/elpy-1.35.0/snippets/python-mode/__le__
-
7elpa/elpy-1.35.0/snippets/python-mode/__len__
-
7elpa/elpy-1.35.0/snippets/python-mode/__long__
-
7elpa/elpy-1.35.0/snippets/python-mode/__lshift__
-
7elpa/elpy-1.35.0/snippets/python-mode/__lt__
-
7elpa/elpy-1.35.0/snippets/python-mode/__mod__
-
7elpa/elpy-1.35.0/snippets/python-mode/__mul__
-
7elpa/elpy-1.35.0/snippets/python-mode/__ne__
-
7elpa/elpy-1.35.0/snippets/python-mode/__neg__
-
10elpa/elpy-1.35.0/snippets/python-mode/__new__
-
7elpa/elpy-1.35.0/snippets/python-mode/__nonzero__
-
7elpa/elpy-1.35.0/snippets/python-mode/__oct__
-
7elpa/elpy-1.35.0/snippets/python-mode/__or__
-
7elpa/elpy-1.35.0/snippets/python-mode/__pos__
-
7elpa/elpy-1.35.0/snippets/python-mode/__pow__
-
7elpa/elpy-1.35.0/snippets/python-mode/__radd__
-
7elpa/elpy-1.35.0/snippets/python-mode/__rand__
-
7elpa/elpy-1.35.0/snippets/python-mode/__rdivmod__
-
7elpa/elpy-1.35.0/snippets/python-mode/__repr__
-
7elpa/elpy-1.35.0/snippets/python-mode/__reversed__
-
7elpa/elpy-1.35.0/snippets/python-mode/__rfloordiv__
-
7elpa/elpy-1.35.0/snippets/python-mode/__rlshift__
-
7elpa/elpy-1.35.0/snippets/python-mode/__rmod__
-
7elpa/elpy-1.35.0/snippets/python-mode/__rmul__
-
7elpa/elpy-1.35.0/snippets/python-mode/__ror__
-
7elpa/elpy-1.35.0/snippets/python-mode/__rpow__
-
7elpa/elpy-1.35.0/snippets/python-mode/__rrshift__
-
7elpa/elpy-1.35.0/snippets/python-mode/__rshift__
-
7elpa/elpy-1.35.0/snippets/python-mode/__rsub__
-
7elpa/elpy-1.35.0/snippets/python-mode/__rtruediv__
-
7elpa/elpy-1.35.0/snippets/python-mode/__rxor__
-
7elpa/elpy-1.35.0/snippets/python-mode/__set__
-
7elpa/elpy-1.35.0/snippets/python-mode/__setattr__
-
7elpa/elpy-1.35.0/snippets/python-mode/__setitem__
-
7elpa/elpy-1.35.0/snippets/python-mode/__slots__
-
7elpa/elpy-1.35.0/snippets/python-mode/__str__
-
7elpa/elpy-1.35.0/snippets/python-mode/__sub__
-
7elpa/elpy-1.35.0/snippets/python-mode/__subclasscheck__
-
7elpa/elpy-1.35.0/snippets/python-mode/__truediv__
-
7elpa/elpy-1.35.0/snippets/python-mode/__unicode__
-
7elpa/elpy-1.35.0/snippets/python-mode/__xor__
-
2elpa/elpy-20210328.1852/elpy-autoloads.el
-
2elpa/elpy-20210328.1852/elpy-django.el
-
17elpa/elpy-20210328.1852/elpy-pkg.el
-
0elpa/elpy-20210328.1852/elpy-profile.el
-
320elpa/elpy-20210328.1852/elpy-refactor.el
-
41elpa/elpy-20210328.1852/elpy-rpc.el
-
6elpa/elpy-20210328.1852/elpy-shell.el
-
22elpa/elpy-20210328.1852/elpy.el
@ -1,11 +0,0 @@ |
|||
(define-package "elpy" "1.35.0" "Emacs Python Development Environment" |
|||
'((company "0.9.2") |
|||
(emacs "24.4") |
|||
(highlight-indentation "0.5.0") |
|||
(pyvenv "1.3") |
|||
(yasnippet "0.8.0") |
|||
(s "1.11.0")) |
|||
:commit "4666c16ef362d4f99053bbc0856d8c65121e1825") |
|||
;; Local Variables: |
|||
;; no-byte-compile: t |
|||
;; End: |
|||
@ -1,297 +0,0 @@ |
|||
;;; elpy-refactor.el --- Refactoring mode for Elpy |
|||
|
|||
;; Copyright (C) 2013-2019 Jorgen Schaefer |
|||
|
|||
;; Author: Jorgen Schaefer <contact@jorgenschaefer.de> |
|||
;; URL: https://github.com/jorgenschaefer/elpy |
|||
|
|||
;; This program is free software; you can redistribute it and/or |
|||
;; modify it under the terms of the GNU General Public License |
|||
;; as published by the Free Software Foundation; either version 3 |
|||
;; of the License, or (at your option) 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. |
|||
|
|||
;; You should have received a copy of the GNU General Public License |
|||
;; along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
|
|||
;;; Commentary: |
|||
|
|||
;; This file provides an interface, including a major mode, to use |
|||
;; refactoring options provided by the Rope library. |
|||
|
|||
;;; Code: |
|||
|
|||
;; We require elpy, but elpy loads us, so we shouldn't load it back. |
|||
;; (require 'elpy) |
|||
|
|||
(defvar elpy-refactor-changes nil |
|||
"Changes that will be commited on \\[elpy-refactor-commit].") |
|||
(make-variable-buffer-local 'elpy-refactor-current-changes) |
|||
|
|||
(defvar elpy-refactor-window-configuration nil |
|||
"The old window configuration. Will be restored after commit.") |
|||
(make-variable-buffer-local 'elpy-refactor-window-configuration) |
|||
|
|||
(make-obsolete |
|||
'elpy-refactor |
|||
"Refactoring has been unstable and flakey, support will be dropped in the future." |
|||
"elpy 1.5.0") |
|||
(defun elpy-refactor () |
|||
"Run the Elpy refactoring interface for Python code." |
|||
(interactive) |
|||
(save-some-buffers) |
|||
(let* ((selection (elpy-refactor-select |
|||
(elpy-refactor-rpc-get-options))) |
|||
(method (car selection)) |
|||
(args (cdr selection))) |
|||
(when method |
|||
(elpy-refactor-create-change-buffer |
|||
(elpy-refactor-rpc-get-changes method args))))) |
|||
|
|||
(defun elpy-refactor-select (options) |
|||
"Show the user the refactoring options and let her choose one. |
|||
|
|||
Depending on the chosen option, ask the user for further |
|||
arguments and build the argument. |
|||
|
|||
Return a cons cell of the name of the option and the arg list |
|||
created." |
|||
(let ((buf (get-buffer-create "*Elpy Refactor*")) |
|||
(pos (vector (1- (point)) |
|||
(ignore-errors |
|||
(1- (region-beginning))) |
|||
(ignore-errors |
|||
(1- (region-end))))) |
|||
(inhibit-read-only t) |
|||
(options (sort options |
|||
(lambda (a b) |
|||
(let ((cata (cdr (assq 'category a))) |
|||
(catb (cdr (assq 'category b)))) |
|||
(if (equal cata catb) |
|||
(string< (cdr (assq 'description a)) |
|||
(cdr (assq 'description b))) |
|||
(string< cata catb)))))) |
|||
(key ?a) |
|||
last-category |
|||
option-alist) |
|||
(with-current-buffer buf |
|||
(erase-buffer) |
|||
(dolist (option options) |
|||
(let ((category (cdr (assq 'category option))) |
|||
(description (cdr (assq 'description option))) |
|||
(name (cdr (assq 'name option))) |
|||
(args (cdr (assq 'args option)))) |
|||
(unless (equal category last-category) |
|||
(when last-category |
|||
(insert "\n")) |
|||
(insert (propertize category 'face 'bold) "\n") |
|||
(setq last-category category)) |
|||
(insert " (" key ") " description "\n") |
|||
(setq option-alist (cons (list key name args) |
|||
option-alist)) |
|||
(setq key (1+ key)))) |
|||
(let ((window-conf (current-window-configuration))) |
|||
(unwind-protect |
|||
(progn |
|||
(with-selected-window (display-buffer buf) |
|||
(goto-char (point-min))) |
|||
(fit-window-to-buffer (get-buffer-window buf)) |
|||
(let* ((key (read-key "Refactoring action? ")) |
|||
(entry (cdr (assoc key option-alist)))) |
|||
(kill-buffer buf) |
|||
(cons (car entry) ; name |
|||
(elpy-refactor-build-arguments (cadr entry) |
|||
pos)))) |
|||
(set-window-configuration window-conf)))))) |
|||
|
|||
(defun elpy-refactor-build-arguments (args pos) |
|||
"Translate an argument list specification to an argument list. |
|||
|
|||
POS is a vector of three elements, the current offset, the offset |
|||
of the beginning of the region, and the offset of the end of the |
|||
region. |
|||
|
|||
ARGS is a list of triples, each triple containing the name of an |
|||
argument (ignored), the type of the argument, and a possible |
|||
prompt string. |
|||
|
|||
Available types: |
|||
|
|||
offset - The offset in the buffer, (1- (point)) |
|||
start_offset - Offset of the beginning of the region |
|||
end_offset - Offset of the end of the region |
|||
string - A free-form string |
|||
filename - A non-existing file name |
|||
directory - An existing directory name |
|||
boolean - A boolean question" |
|||
(mapcar (lambda (arg) |
|||
(let ((type (cadr arg)) |
|||
(prompt (cl-caddr arg))) |
|||
(cond |
|||
((equal type "offset") |
|||
(aref pos 0)) |
|||
((equal type "start_offset") |
|||
(aref pos 1)) |
|||
((equal type "end_offset") |
|||
(aref pos 2)) |
|||
((equal type "string") |
|||
(read-from-minibuffer prompt)) |
|||
((equal type "filename") |
|||
(expand-file-name |
|||
(read-file-name prompt))) |
|||
((equal type "directory") |
|||
(expand-file-name |
|||
(read-directory-name prompt))) |
|||
((equal type "boolean") |
|||
(y-or-n-p prompt))))) |
|||
args)) |
|||
|
|||
(defun elpy-refactor-create-change-buffer (changes) |
|||
"Show the user a buffer of changes. |
|||
|
|||
The user can review the changes and confirm them with |
|||
\\[elpy-refactor-commit]." |
|||
(unless changes |
|||
(error "No changes for this refactoring action.")) |
|||
(with-current-buffer (get-buffer-create "*Elpy Refactor*") |
|||
(elpy-refactor-mode) |
|||
(setq elpy-refactor-changes changes |
|||
elpy-refactor-window-configuration (current-window-configuration)) |
|||
(let ((inhibit-read-only t)) |
|||
(erase-buffer) |
|||
(elpy-refactor-insert-changes changes)) |
|||
(select-window (display-buffer (current-buffer))) |
|||
(goto-char (point-min)))) |
|||
|
|||
(defun elpy-refactor-insert-changes (changes) |
|||
"Format and display the changes described in CHANGES." |
|||
(insert (propertize "Use C-c C-c to apply the following changes." |
|||
'face 'bold) |
|||
"\n\n") |
|||
(dolist (change changes) |
|||
(let ((action (cdr (assq 'action change)))) |
|||
(cond |
|||
((equal action "change") |
|||
(insert (cdr (assq 'diff change)) |
|||
"\n")) |
|||
((equal action "create") |
|||
(let ((type (cdr (assq 'type change)))) |
|||
(if (equal type "file") |
|||
(insert "+++ " (cdr (assq 'file change)) "\n" |
|||
"Create file " (cdr (assq 'file change)) "\n" |
|||
"\n") |
|||
(insert "+++ " (cdr (assq 'path change)) "\n" |
|||
"Create directory " (cdr (assq 'path change)) "\n" |
|||
"\n")))) |
|||
((equal action "move") |
|||
(insert "--- " (cdr (assq 'source change)) "\n" |
|||
"+++ " (cdr (assq 'destination change)) "\n" |
|||
"Rename " (cdr (assq 'type change)) "\n" |
|||
"\n")) |
|||
((equal action "delete") |
|||
(let ((type (cdr (assq 'type change)))) |
|||
(if (equal type "file") |
|||
(insert "--- " (cdr (assq 'file change)) "\n" |
|||
"Delete file " (cdr (assq 'file change)) "\n" |
|||
"\n") |
|||
(insert "--- " (cdr (assq 'path change)) "\n" |
|||
"Delete directory " (cdr (assq 'path change)) "\n" |
|||
"\n")))))))) |
|||
|
|||
(defvar elpy-refactor-mode-map |
|||
(let ((map (make-sparse-keymap))) |
|||
(define-key map (kbd "C-c C-c") 'elpy-refactor-commit) |
|||
(define-key map (kbd "q") 'bury-buffer) |
|||
(define-key map (kbd "h") 'describe-mode) |
|||
(define-key map (kbd "?") 'describe-mode) |
|||
map) |
|||
"The key map for `elpy-refactor-mode'.") |
|||
|
|||
(define-derived-mode elpy-refactor-mode diff-mode "Elpy Refactor" |
|||
"Mode to display refactoring actions and ask confirmation from the user. |
|||
|
|||
\\{elpy-refactor-mode-map}" |
|||
:group 'elpy |
|||
(view-mode 1)) |
|||
|
|||
(defun elpy-refactor-commit () |
|||
"Commit the changes in the current buffer." |
|||
(interactive) |
|||
(unless elpy-refactor-changes |
|||
(error "No changes to commit.")) |
|||
;; Restore the window configuration as the first thing so that |
|||
;; changes below are visible to the user. Especially the point |
|||
;; change in possible buffer changes. |
|||
(set-window-configuration elpy-refactor-window-configuration) |
|||
(dolist (change elpy-refactor-changes) |
|||
(let ((action (cdr (assq 'action change)))) |
|||
(cond |
|||
((equal action "change") |
|||
(with-current-buffer (find-file-noselect (cdr (assq 'file change))) |
|||
;; This would break for save-excursion as the buffer is |
|||
;; truncated, so all markets now point to position 1. |
|||
(let ((old-point (point))) |
|||
(undo-boundary) |
|||
(erase-buffer) |
|||
(insert (cdr (assq 'contents change))) |
|||
(undo-boundary) |
|||
(goto-char old-point)))) |
|||
((equal action "create") |
|||
(if (equal (cdr (assq 'type change)) |
|||
"file") |
|||
(find-file-noselect (cdr (assq 'file change))) |
|||
(make-directory (cdr (assq 'path change))))) |
|||
((equal action "move") |
|||
(let* ((source (cdr (assq 'source change))) |
|||
(dest (cdr (assq 'destination change))) |
|||
(buf (get-file-buffer source))) |
|||
(when buf |
|||
(with-current-buffer buf |
|||
(setq buffer-file-name dest) |
|||
(rename-buffer (file-name-nondirectory dest) t))) |
|||
(rename-file source dest))) |
|||
((equal action "delete") |
|||
(if (equal (cdr (assq 'type change)) "file") |
|||
(let ((name (cdr (assq 'file change)))) |
|||
(when (y-or-n-p (format "Really delete %s? " name)) |
|||
(delete-file name t))) |
|||
(let ((name (cdr (assq 'directory change)))) |
|||
(when (y-or-n-p (format "Really delete %s? " name)) |
|||
(delete-directory name nil t)))))))) |
|||
(kill-buffer (current-buffer))) |
|||
|
|||
(defun elpy-refactor-rpc-get-options () |
|||
"Get a list of refactoring options from the Elpy RPC." |
|||
(if (use-region-p) |
|||
(elpy-rpc "get_refactor_options" |
|||
(list (buffer-file-name) |
|||
(1- (region-beginning)) |
|||
(1- (region-end)))) |
|||
(elpy-rpc "get_refactor_options" |
|||
(list (buffer-file-name) |
|||
(1- (point)))))) |
|||
|
|||
(defun elpy-refactor-rpc-get-changes (method args) |
|||
"Get a list of changes from the Elpy RPC after applying METHOD with ARGS." |
|||
(elpy-rpc "refactor" |
|||
(list (buffer-file-name) |
|||
method args))) |
|||
|
|||
(defun elpy-refactor-options (option) |
|||
"Show available refactor options and let user choose one." |
|||
(interactive "c[i]: importmagic-fixup [p]: autopep8-fix-code [r]: refactor") |
|||
(let ((choice (char-to-string option))) |
|||
(cond |
|||
((string-equal choice "i") |
|||
(elpy-importmagic-fixup)) |
|||
((string-equal choice "p") |
|||
(elpy-autopep8-fix-code)) |
|||
((string-equal choice "r") |
|||
(elpy-refactor))))) |
|||
|
|||
(provide 'elpy-refactor) |
|||
;;; elpy-refactor.el ends here |
|||
@ -1,381 +0,0 @@ |
|||
"""Refactoring methods for elpy. |
|||
|
|||
This interfaces directly with rope, regardless of the backend used, |
|||
because the other backends don't really offer refactoring choices. |
|||
Once Jedi is similarly featureful as Rope we can try and offer both. |
|||
|
|||
|
|||
# Too complex: |
|||
|
|||
- Restructure: Interesting, but too complex, and needs deep Rope |
|||
knowledge to do well. |
|||
|
|||
- ChangeSignature: Slightly less complex interface, but still to |
|||
complex, requiring a large effort for the benefit. |
|||
|
|||
|
|||
# Too useless: |
|||
|
|||
I could not get these to work in any useful fashion. I might be doing |
|||
something wrong. |
|||
|
|||
- ExtractVariable does not replace the code extracted with the |
|||
variable, making it a glorified copy&paste function. Emacs can do |
|||
better than this interface by itself. |
|||
|
|||
- EncapsulateField: Getter/setter methods are outdated, this should be |
|||
using properties. |
|||
|
|||
- IntroduceFactory: Inserts a trivial method to the current class. |
|||
Cute. |
|||
|
|||
- IntroduceParameter: Introduces a parameter correctly, but does not |
|||
replace the old code with the parameter. So it just edits the |
|||
argument list and adds a shiny default. |
|||
|
|||
- LocalToField: Seems to just add "self." in front of all occurrences |
|||
of a variable in the local scope. |
|||
|
|||
- MethodObject: This turns the current method into a callable |
|||
class/object. Not sure what that would be good for. |
|||
|
|||
|
|||
# Can't even get to work: |
|||
|
|||
- ImportOrganizer expand_star_imports, handle_long_imports, |
|||
relatives_to_absolutes: Seem not to do anything. |
|||
|
|||
- create_move: I was not able to figure out what it would like to see |
|||
as its attrib argument. |
|||
|
|||
""" |
|||
|
|||
import os |
|||
|
|||
from elpy.rpc import Fault |
|||
|
|||
try: |
|||
from rope.base.exceptions import RefactoringError |
|||
from rope.base.project import Project |
|||
from rope.base.libutils import path_to_resource |
|||
from rope.base import change as rope_change |
|||
from rope.base import worder |
|||
from rope.refactor.importutils import ImportOrganizer |
|||
from rope.refactor.topackage import ModuleToPackage |
|||
from rope.refactor.rename import Rename |
|||
from rope.refactor.move import create_move |
|||
from rope.refactor.inline import create_inline |
|||
from rope.refactor.extract import ExtractMethod |
|||
from rope.refactor.usefunction import UseFunction |
|||
ROPE_AVAILABLE = True |
|||
except ImportError: |
|||
ROPE_AVAILABLE = False |
|||
|
|||
|
|||
def options(description, **kwargs): |
|||
"""Decorator to set some options on a method.""" |
|||
def set_notes(function): |
|||
function.refactor_notes = {'name': function.__name__, |
|||
'category': "Miscellaneous", |
|||
'description': description, |
|||
'doc': getattr(function, '__doc__', |
|||
''), |
|||
'args': []} |
|||
function.refactor_notes.update(kwargs) |
|||
return function |
|||
return set_notes |
|||
|
|||
|
|||
class Refactor(object): |
|||
"""The main refactoring interface. |
|||
|
|||
Once initialized, the first call should be to get_refactor_options |
|||
to get a list of refactoring options at a given position. The |
|||
returned value will also list any additional options required. |
|||
|
|||
Once you picked one, you can call get_changes to get the actual |
|||
refactoring changes. |
|||
|
|||
""" |
|||
def __init__(self, project_root, filename): |
|||
self.project_root = project_root |
|||
if not ROPE_AVAILABLE: |
|||
raise Fault('rope not installed, cannot refactor code.', |
|||
code=400) |
|||
if not os.path.exists(project_root): |
|||
raise Fault( |
|||
"cannot do refactoring without a local project root", |
|||
code=400 |
|||
) |
|||
self.project = Project(project_root, ropefolder=None) |
|||
self.resource = path_to_resource(self.project, filename) |
|||
|
|||
def get_refactor_options(self, start, end=None): |
|||
"""Return a list of options for refactoring at the given position. |
|||
|
|||
If `end` is also given, refactoring on a region is assumed. |
|||
|
|||
Each option is a dictionary of key/value pairs. The value of |
|||
the key 'name' is the one to be used for get_changes. |
|||
|
|||
The key 'args' contains a list of additional arguments |
|||
required for get_changes. |
|||
|
|||
""" |
|||
result = [] |
|||
for symbol in dir(self): |
|||
if not symbol.startswith("refactor_"): |
|||
continue |
|||
method = getattr(self, symbol) |
|||
if not method.refactor_notes.get('available', True): |
|||
continue |
|||
category = method.refactor_notes['category'] |
|||
if end is not None and category != 'Region': |
|||
continue |
|||
if end is None and category == 'Region': |
|||
continue |
|||
is_on_symbol = self._is_on_symbol(start) |
|||
if not is_on_symbol and category in ('Symbol', 'Method'): |
|||
continue |
|||
requires_import = method.refactor_notes.get('only_on_imports', |
|||
False) |
|||
if requires_import and not self._is_on_import_statement(start): |
|||
continue |
|||
result.append(method.refactor_notes) |
|||
return result |
|||
|
|||
def _is_on_import_statement(self, offset): |
|||
"Does this offset point to an import statement?" |
|||
data = self.resource.read() |
|||
bol = data.rfind("\n", 0, offset) + 1 |
|||
eol = data.find("\n", 0, bol) |
|||
if eol == -1: |
|||
eol = len(data) |
|||
line = data[bol:eol] |
|||
line = line.strip() |
|||
if line.startswith("import ") or line.startswith("from "): |
|||
return True |
|||
else: |
|||
return False |
|||
|
|||
def _is_on_symbol(self, offset): |
|||
"Is this offset on a symbol?" |
|||
if not ROPE_AVAILABLE: |
|||
return False |
|||
data = self.resource.read() |
|||
if offset >= len(data): |
|||
return False |
|||
if data[offset] != '_' and not data[offset].isalnum(): |
|||
return False |
|||
word = worder.get_name_at(self.resource, offset) |
|||
if word: |
|||
return True |
|||
else: |
|||
return False |
|||
|
|||
def get_changes(self, name, *args): |
|||
"""Return a list of changes for the named refactoring action. |
|||
|
|||
Changes are dictionaries describing a single action to be |
|||
taken for the refactoring to be successful. |
|||
|
|||
A change has an action and possibly a type. In the description |
|||
below, the action is before the slash and the type after it. |
|||
|
|||
change: Change file contents |
|||
- file: The path to the file to change |
|||
- contents: The new contents for the file |
|||
- Diff: A unified diff showing the changes introduced |
|||
|
|||
create/file: Create a new file |
|||
- file: The file to create |
|||
|
|||
create/directory: Create a new directory |
|||
- path: The directory to create |
|||
|
|||
move/file: Rename a file |
|||
- source: The path to the source file |
|||
- destination: The path to the destination file name |
|||
|
|||
move/directory: Rename a directory |
|||
- source: The path to the source directory |
|||
- destination: The path to the destination directory name |
|||
|
|||
delete/file: Delete a file |
|||
- file: The file to delete |
|||
|
|||
delete/directory: Delete a directory |
|||
- path: The directory to delete |
|||
|
|||
""" |
|||
if not name.startswith("refactor_"): |
|||
raise ValueError("Bad refactoring name {0}".format(name)) |
|||
method = getattr(self, name) |
|||
if not method.refactor_notes.get('available', True): |
|||
raise RuntimeError("Method not available") |
|||
return method(*args) |
|||
|
|||
@options("Convert from x import y to import x.y as y", category="Imports", |
|||
args=[("offset", "offset", None)], |
|||
only_on_imports=True, |
|||
available=ROPE_AVAILABLE) |
|||
def refactor_froms_to_imports(self, offset): |
|||
"""Converting imports of the form "from ..." to "import ...".""" |
|||
refactor = ImportOrganizer(self.project) |
|||
changes = refactor.froms_to_imports(self.resource, offset) |
|||
return translate_changes(changes) |
|||
|
|||
@options("Reorganize and clean up", category="Imports", |
|||
available=ROPE_AVAILABLE) |
|||
def refactor_organize_imports(self): |
|||
"""Clean up and organize imports.""" |
|||
refactor = ImportOrganizer(self.project) |
|||
changes = refactor.organize_imports(self.resource) |
|||
return translate_changes(changes) |
|||
|
|||
@options("Convert the current module into a package", category="Module", |
|||
available=ROPE_AVAILABLE) |
|||
def refactor_module_to_package(self): |
|||
"""Convert the current module into a package.""" |
|||
refactor = ModuleToPackage(self.project, self.resource) |
|||
return self._get_changes(refactor) |
|||
|
|||
@options("Rename symbol at point", category="Symbol", |
|||
args=[("offset", "offset", None), |
|||
("new_name", "string", "Rename to: "), |
|||
("in_hierarchy", "boolean", |
|||
"Rename in super-/subclasses as well? "), |
|||
("docs", "boolean", |
|||
"Replace occurences in docs and strings? ") |
|||
], |
|||
available=ROPE_AVAILABLE) |
|||
def refactor_rename_at_point(self, offset, new_name, in_hierarchy, docs): |
|||
"""Rename the symbol at point.""" |
|||
try: |
|||
refactor = Rename(self.project, self.resource, offset) |
|||
except RefactoringError as e: |
|||
raise Fault(str(e), code=400) |
|||
return self._get_changes(refactor, new_name, |
|||
in_hierarchy=in_hierarchy, docs=docs) |
|||
|
|||
@options("Rename current module", category="Module", |
|||
args=[("new_name", "string", "Rename to: ")], |
|||
available=ROPE_AVAILABLE) |
|||
def refactor_rename_current_module(self, new_name): |
|||
"""Rename the current module.""" |
|||
refactor = Rename(self.project, self.resource, None) |
|||
return self._get_changes(refactor, new_name) |
|||
|
|||
@options("Move the current module to a different package", |
|||
category="Module", |
|||
args=[("new_name", "directory", "Destination package: ")], |
|||
available=ROPE_AVAILABLE) |
|||
def refactor_move_module(self, new_name): |
|||
"""Move the current module.""" |
|||
refactor = create_move(self.project, self.resource) |
|||
resource = path_to_resource(self.project, new_name) |
|||
return self._get_changes(refactor, resource) |
|||
|
|||
@options("Inline function call at point", category="Symbol", |
|||
args=[("offset", "offset", None), |
|||
("only_this", "boolean", "Only this occurrence? ")], |
|||
available=ROPE_AVAILABLE) |
|||
def refactor_create_inline(self, offset, only_this): |
|||
"""Inline the function call at point.""" |
|||
refactor = create_inline(self.project, self.resource, offset) |
|||
if only_this: |
|||
return self._get_changes(refactor, remove=False, only_current=True) |
|||
else: |
|||
return self._get_changes(refactor, remove=True, only_current=False) |
|||
|
|||
@options("Extract current region as a method", category="Region", |
|||
args=[("start", "start_offset", None), |
|||
("end", "end_offset", None), |
|||
("name", "string", "Method name: "), |
|||
("make_global", "boolean", "Create global method? ")], |
|||
available=ROPE_AVAILABLE) |
|||
def refactor_extract_method(self, start, end, name, |
|||
make_global): |
|||
"""Extract region as a method.""" |
|||
refactor = ExtractMethod(self.project, self.resource, start, end) |
|||
return self._get_changes( |
|||
refactor, name, similar=True, global_=make_global |
|||
) |
|||
|
|||
@options("Use the function at point wherever possible", category="Method", |
|||
args=[("offset", "offset", None)], |
|||
available=ROPE_AVAILABLE) |
|||
def refactor_use_function(self, offset): |
|||
"""Use the function at point wherever possible.""" |
|||
try: |
|||
refactor = UseFunction(self.project, self.resource, offset) |
|||
except RefactoringError as e: |
|||
raise Fault( |
|||
'Refactoring error: {}'.format(e), |
|||
code=400 |
|||
) |
|||
return self._get_changes(refactor) |
|||
|
|||
def _get_changes(self, refactor, *args, **kwargs): |
|||
try: |
|||
changes = refactor.get_changes(*args, **kwargs) |
|||
except Exception as e: |
|||
raise Fault("Error during refactoring: {}".format(e), |
|||
code=400) |
|||
return translate_changes(changes) |
|||
|
|||
|
|||
def translate_changes(initial_change): |
|||
"""Translate rope.base.change.Change instances to dictionaries. |
|||
|
|||
See Refactor.get_changes for an explanation of the resulting |
|||
dictionary. |
|||
|
|||
""" |
|||
agenda = [initial_change] |
|||
result = [] |
|||
while agenda: |
|||
change = agenda.pop(0) |
|||
if isinstance(change, rope_change.ChangeSet): |
|||
agenda.extend(change.changes) |
|||
elif isinstance(change, rope_change.ChangeContents): |
|||
result.append({'action': 'change', |
|||
'file': change.resource.real_path, |
|||
'contents': change.new_contents, |
|||
'diff': change.get_description()}) |
|||
elif isinstance(change, rope_change.CreateFile): |
|||
result.append({'action': 'create', |
|||
'type': 'file', |
|||
'file': change.resource.real_path}) |
|||
elif isinstance(change, rope_change.CreateFolder): |
|||
result.append({'action': 'create', |
|||
'type': 'directory', |
|||
'path': change.resource.real_path}) |
|||
elif isinstance(change, rope_change.MoveResource): |
|||
result.append({'action': 'move', |
|||
'type': ('directory' |
|||
if change.new_resource.is_folder() |
|||
else 'file'), |
|||
'source': change.resource.real_path, |
|||
'destination': change.new_resource.real_path}) |
|||
elif isinstance(change, rope_change.RemoveResource): |
|||
if change.resource.is_folder(): |
|||
result.append({'action': 'delete', |
|||
'type': 'directory', |
|||
'path': change.resource.real_path}) |
|||
else: |
|||
result.append({'action': 'delete', |
|||
'type': 'file', |
|||
'file': change.resource.real_path}) |
|||
return result |
|||
|
|||
|
|||
class FakeResource(object): |
|||
"""A fake resource in case Rope is absence.""" |
|||
|
|||
def __init__(self, filename): |
|||
self.real_path = filename |
|||
|
|||
def read(self): |
|||
with open(self.real_path) as f: |
|||
return f.read() |
|||
@ -1,545 +0,0 @@ |
|||
import unittest |
|||
import tempfile |
|||
import shutil |
|||
import os |
|||
import mock |
|||
import sys |
|||
|
|||
from elpy import refactor |
|||
from textwrap import dedent |
|||
|
|||
|
|||
class RefactorTestCase(unittest.TestCase): |
|||
def setUp(self): |
|||
self.project_root = tempfile.mkdtemp(prefix="test-refactor-root") |
|||
self.addCleanup(shutil.rmtree, self.project_root, |
|||
ignore_errors=True) |
|||
|
|||
def create_file(self, name, contents=""): |
|||
filename = os.path.join(self.project_root, name) |
|||
contents = dedent(contents) |
|||
offset = contents.find("_|_") |
|||
if offset > -1: |
|||
contents = contents[:offset] + contents[offset + 3:] |
|||
with open(filename, "w") as f: |
|||
f.write(contents) |
|||
return filename, offset |
|||
|
|||
def assertSourceEqual(self, first, second, msg=None): |
|||
"""Fail if the two objects are unequal, ignoring indentation.""" |
|||
self.assertEqual(dedent(first), dedent(second), msg=msg) |
|||
|
|||
|
|||
class TestGetRefactorOptions(RefactorTestCase): |
|||
def test_should_only_return_importsmodule_if_not_on_symbol(self): |
|||
filename, offset = self.create_file("foo.py", |
|||
"""\ |
|||
import foo |
|||
_|_""") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
options = ref.get_refactor_options(offset) |
|||
self.assertTrue(all(opt['category'] in ('Imports', |
|||
'Module') |
|||
for opt in options)) |
|||
filename, offset = self.create_file("foo.py", |
|||
"""\ |
|||
_|_ |
|||
import foo""") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
options = ref.get_refactor_options(offset) |
|||
self.assertTrue(all(opt['category'] in ('Imports', |
|||
'Module') |
|||
for opt in options)) |
|||
|
|||
def test_should_return_all_if_on_symbol(self): |
|||
filename, offset = self.create_file("foo.py", |
|||
"import _|_foo") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
options = ref.get_refactor_options(offset) |
|||
self.assertTrue(all(opt['category'] in ('Imports', |
|||
'Method', |
|||
'Module', |
|||
'Symbol') |
|||
for opt in options)) |
|||
|
|||
def test_should_return_only_region_if_endoffset(self): |
|||
filename, offset = self.create_file("foo.py", |
|||
"import foo") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
options = ref.get_refactor_options(offset, 5) |
|||
self.assertTrue(all(opt['category'] == 'Region' |
|||
for opt in options)) |
|||
|
|||
@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") |
|||
def test_should_treat_from_import_special(self): |
|||
filename, offset = self.create_file("foo.py", |
|||
"""\ |
|||
import foo |
|||
_|_""") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
options = ref.get_refactor_options(offset) |
|||
self.assertFalse(any(opt['name'] == "refactor_froms_to_imports" |
|||
for opt in options)) |
|||
filename, offset = self.create_file("foo.py", |
|||
"imp_|_ort foo") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
options = ref.get_refactor_options(offset) |
|||
self.assertTrue(any(opt['name'] == "refactor_froms_to_imports" |
|||
for opt in options)) |
|||
|
|||
|
|||
class TestGetChanges(RefactorTestCase): |
|||
def test_should_fail_if_method_is_not_refactoring(self): |
|||
filename, offset = self.create_file("foo.py") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
self.assertRaises(ValueError, ref.get_changes, "bad_name") |
|||
|
|||
def test_should_return_method_results(self): |
|||
filename, offset = self.create_file("foo.py") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
with mock.patch.object(ref, 'refactor_extract_method') as test: |
|||
test.return_value = "Meep!" |
|||
self.assertEqual(ref.get_changes("refactor_extract_method", |
|||
1, 2), |
|||
"Meep!") |
|||
test.assert_called_with(1, 2) |
|||
|
|||
|
|||
@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") |
|||
class TestIsOnSymbol(RefactorTestCase): |
|||
def test_should_find_symbol(self): |
|||
filename, offset = self.create_file("test.py", "__B_|_AR = 100") |
|||
r = refactor.Refactor(self.project_root, filename) |
|||
self.assertTrue(r._is_on_symbol(offset)) |
|||
|
|||
# Issue #111 |
|||
def test_should_find_symbol_with_underscores(self): |
|||
filename, offset = self.create_file("test.py", "_|___BAR = 100") |
|||
r = refactor.Refactor(self.project_root, filename) |
|||
self.assertTrue(r._is_on_symbol(offset)) |
|||
|
|||
def test_should_not_find_weird_places(self): |
|||
filename, offset = self.create_file("test.py", "hello = _|_ 1 + 1") |
|||
r = refactor.Refactor(self.project_root, filename) |
|||
self.assertFalse(r._is_on_symbol(offset)) |
|||
|
|||
|
|||
@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") |
|||
class TestFromsToImports(RefactorTestCase): |
|||
def test_should_refactor(self): |
|||
filename, offset = self.create_file( |
|||
"foo.py", |
|||
"""\ |
|||
_|_from datetime import datetime |
|||
|
|||
d = datetime(2013, 4, 7) |
|||
""") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
(change,) = ref.get_changes("refactor_froms_to_imports", offset) |
|||
self.assertEqual(change['action'], 'change') |
|||
self.assertEqual(change['file'], filename) |
|||
self.assertSourceEqual(change['contents'], |
|||
"""\ |
|||
import datetime |
|||
|
|||
d = datetime.datetime(2013, 4, 7) |
|||
""") |
|||
|
|||
|
|||
@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") |
|||
class TestOrganizeImports(RefactorTestCase): |
|||
def test_should_refactor(self): |
|||
filename, offset = self.create_file( |
|||
"foo.py", |
|||
"""\ |
|||
import unittest, base64 |
|||
import datetime, json |
|||
|
|||
obj = json.dumps(23) |
|||
unittest.TestCase() |
|||
""") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
(change,) = ref.get_changes("refactor_organize_imports") |
|||
self.assertEqual(change['action'], 'change') |
|||
self.assertEqual(change['file'], filename) |
|||
self.assertSourceEqual(change['contents'], |
|||
"""\ |
|||
import json |
|||
import unittest |
|||
|
|||
|
|||
obj = json.dumps(23) |
|||
unittest.TestCase() |
|||
""") |
|||
|
|||
|
|||
@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") |
|||
class TestModuleToPackage(RefactorTestCase): |
|||
def test_should_refactor(self): |
|||
filename, offset = self.create_file( |
|||
"foo.py", |
|||
"_|_import os\n") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
changes = ref.refactor_module_to_package() |
|||
a, b, c = changes |
|||
# Not sure why the a change is there. It's a CHANGE that |
|||
# changes nothing... |
|||
self.assertEqual(a['diff'], '') |
|||
|
|||
self.assertEqual(b['action'], 'create') |
|||
self.assertEqual(b['type'], 'directory') |
|||
self.assertEqual(b['path'], os.path.join(self.project_root, "foo")) |
|||
|
|||
self.assertEqual(c['action'], 'move') |
|||
self.assertEqual(c['type'], 'file') |
|||
self.assertEqual(c['source'], os.path.join(self.project_root, |
|||
"foo.py")) |
|||
self.assertEqual(c['destination'], os.path.join(self.project_root, |
|||
"foo/__init__.py")) |
|||
|
|||
|
|||
@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") |
|||
class TestRenameAtPoint(RefactorTestCase): |
|||
def test_should_refactor(self): |
|||
filename, offset = self.create_file( |
|||
"foo.py", |
|||
"""\ |
|||
class Foo(object): |
|||
def _|_foo(self): |
|||
return 5 |
|||
|
|||
def bar(self): |
|||
return self.foo() |
|||
""") |
|||
file2, offset2 = self.create_file( |
|||
"bar.py", |
|||
"""\ |
|||
import foo |
|||
|
|||
|
|||
x = foo.Foo() |
|||
x.foo()""") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
first, second = ref.refactor_rename_at_point(offset, "frob", |
|||
in_hierarchy=False, |
|||
docs=False) |
|||
if first['file'] == filename: |
|||
a, b = first, second |
|||
else: |
|||
a, b = second, first |
|||
self.assertEqual(a['action'], 'change') |
|||
self.assertEqual(a['file'], filename) |
|||
self.assertSourceEqual(a['contents'], |
|||
"""\ |
|||
class Foo(object): |
|||
def frob(self): |
|||
return 5 |
|||
|
|||
def bar(self): |
|||
return self.frob() |
|||
""") |
|||
self.assertEqual(b['action'], 'change') |
|||
self.assertEqual(b['file'], file2) |
|||
self.assertSourceEqual(b['contents'], |
|||
"""\ |
|||
import foo |
|||
|
|||
|
|||
x = foo.Foo() |
|||
x.frob()""") |
|||
|
|||
def test_should_refactor_in_hierarchy(self): |
|||
filename, offset = self.create_file( |
|||
"foo.py", |
|||
"""\ |
|||
class Foo(object): |
|||
def _|_foo(self): |
|||
return 5 |
|||
|
|||
def bar(self): |
|||
return self.foo() |
|||
|
|||
class Bar(Foo): |
|||
def foo(self): |
|||
return 42 |
|||
|
|||
class Baz(object): |
|||
def foo(self): |
|||
return 42 |
|||
""") |
|||
file2, offset2 = self.create_file( |
|||
"bar.py", |
|||
"""\ |
|||
import foo |
|||
|
|||
|
|||
x, y, z = foo.Foo(), foo.Bar(), foo.Baz() |
|||
x.foo() |
|||
y.foo() |
|||
z.foo()""") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
first, second = ref.refactor_rename_at_point(offset, "frob", |
|||
in_hierarchy=True, |
|||
docs=False) |
|||
if first['file'] == filename: |
|||
a, b = first, second |
|||
else: |
|||
a, b = second, first |
|||
self.assertEqual(a['action'], 'change') |
|||
self.assertEqual(a['file'], filename) |
|||
self.assertSourceEqual(a['contents'], |
|||
"""\ |
|||
class Foo(object): |
|||
def frob(self): |
|||
return 5 |
|||
|
|||
def bar(self): |
|||
return self.frob() |
|||
|
|||
class Bar(Foo): |
|||
def frob(self): |
|||
return 42 |
|||
|
|||
class Baz(object): |
|||
def foo(self): |
|||
return 42 |
|||
""") |
|||
self.assertEqual(b['action'], 'change') |
|||
self.assertEqual(b['file'], file2) |
|||
self.assertSourceEqual(b['contents'], |
|||
"""\ |
|||
import foo |
|||
|
|||
|
|||
x, y, z = foo.Foo(), foo.Bar(), foo.Baz() |
|||
x.frob() |
|||
y.frob() |
|||
z.foo()""") |
|||
|
|||
def test_should_refactor_in_docstrings(self): |
|||
filename, offset = self.create_file( |
|||
"foo.py", |
|||
"""\ |
|||
class Foo(object): |
|||
"Frobnicate the foo" |
|||
def _|_foo(self): |
|||
return 5 |
|||
|
|||
print("I'm an unrelated foo") |
|||
""") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
(change,) = ref.refactor_rename_at_point(offset, "frob", |
|||
in_hierarchy=False, |
|||
docs=True) |
|||
self.assertEqual(change['action'], 'change') |
|||
self.assertEqual(change['file'], filename) |
|||
self.assertSourceEqual(change['contents'], |
|||
"""\ |
|||
class Foo(object): |
|||
"Frobnicate the frob" |
|||
def frob(self): |
|||
return 5 |
|||
|
|||
print("I'm an unrelated foo") |
|||
""") |
|||
|
|||
|
|||
@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") |
|||
class TestRenameCurrentModule(RefactorTestCase): |
|||
def test_should_refactor(self): |
|||
filename, offset = self.create_file( |
|||
"foo.py", |
|||
"_|_import os\n") |
|||
file2, offset = self.create_file( |
|||
"bar.py", |
|||
"""\ |
|||
_|_import foo |
|||
foo.os |
|||
""") |
|||
dest = os.path.join(self.project_root, "frob.py") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
a, b = ref.refactor_rename_current_module("frob") |
|||
|
|||
self.assertEqual(a['action'], 'change') |
|||
self.assertEqual(a['file'], file2) |
|||
self.assertEqual(a['contents'], |
|||
"import frob\n" |
|||
"frob.os\n") |
|||
|
|||
self.assertEqual(b['action'], 'move') |
|||
self.assertEqual(b['type'], 'file') |
|||
self.assertEqual(b['source'], filename) |
|||
self.assertEqual(b['destination'], dest) |
|||
|
|||
|
|||
@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") |
|||
class TestMoveModule(RefactorTestCase): |
|||
def test_should_refactor(self): |
|||
filename, offset = self.create_file( |
|||
"foo.py", |
|||
"_|_import os\n") |
|||
file2, offset = self.create_file( |
|||
"bar.py", |
|||
"""\ |
|||
_|_import foo |
|||
foo.os |
|||
""") |
|||
dest = os.path.join(self.project_root, "frob") |
|||
os.mkdir(dest) |
|||
with open(os.path.join(dest, "__init__.py"), "w") as f: |
|||
f.write("") |
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
a, b = ref.refactor_move_module(dest) |
|||
|
|||
self.assertEqual(a['action'], 'change') |
|||
self.assertEqual(a['file'], file2) |
|||
self.assertSourceEqual(a['contents'], |
|||
"""\ |
|||
import frob.foo |
|||
frob.foo.os |
|||
""") |
|||
|
|||
self.assertEqual(b['action'], 'move') |
|||
self.assertEqual(b['type'], 'file') |
|||
self.assertEqual(b['source'], filename) |
|||
self.assertEqual(b['destination'], |
|||
os.path.join(dest, "foo.py")) |
|||
|
|||
|
|||
@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") |
|||
class TestCreateInline(RefactorTestCase): |
|||
def setUp(self): |
|||
super(TestCreateInline, self).setUp() |
|||
self.filename, self.offset = self.create_file( |
|||
"foo.py", |
|||
"""\ |
|||
def add(a, b): |
|||
return a + b |
|||
|
|||
x = _|_add(2, 3) |
|||
y = add(17, 4) |
|||
""") |
|||
|
|||
def test_should_refactor_single_occurrenc(self): |
|||
ref = refactor.Refactor(self.project_root, self.filename) |
|||
(change,) = ref.refactor_create_inline(self.offset, True) |
|||
|
|||
self.assertEqual(change['action'], 'change') |
|||
self.assertEqual(change['file'], self.filename) |
|||
self.assertSourceEqual(change['contents'], |
|||
"""\ |
|||
def add(a, b): |
|||
return a + b |
|||
|
|||
x = 2 + 3 |
|||
y = add(17, 4) |
|||
""") |
|||
|
|||
def test_should_refactor_all_occurrencs(self): |
|||
ref = refactor.Refactor(self.project_root, self.filename) |
|||
(change,) = ref.refactor_create_inline(self.offset, False) |
|||
|
|||
self.assertEqual(change['action'], 'change') |
|||
self.assertEqual(change['file'], self.filename) |
|||
self.assertSourceEqual(change['contents'], |
|||
"""\ |
|||
x = 2 + 3 |
|||
y = 17 + 4 |
|||
""") |
|||
|
|||
|
|||
@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") |
|||
class TestExtractMethod(RefactorTestCase): |
|||
def setUp(self): |
|||
super(TestExtractMethod, self).setUp() |
|||
self.filename, self.offset = self.create_file( |
|||
"foo.py", |
|||
"""\ |
|||
class Foo(object): |
|||
def spaghetti(self, a, b): |
|||
_|_x = a + 5 |
|||
y = b + 23 |
|||
return y |
|||
""") |
|||
|
|||
@unittest.skipIf(sys.version_info >= (3, 5), "Python 3.5 not supported") |
|||
def test_should_refactor_local(self): |
|||
ref = refactor.Refactor(self.project_root, self.filename) |
|||
(change,) = ref.refactor_extract_method(self.offset, 104, |
|||
"calc", False) |
|||
self.assertEqual(change['action'], 'change') |
|||
self.assertEqual(change['file'], self.filename) |
|||
expected = """\ |
|||
class Foo(object): |
|||
def spaghetti(self, a, b): |
|||
return self.calc(a, b) |
|||
|
|||
def calc(self, a, b): |
|||
x = a + 5 |
|||
y = b + 23 |
|||
return y |
|||
""" |
|||
expected2 = expected.replace("return self.calc(a, b)", |
|||
"return self.calc(b, a)") |
|||
expected2 = expected2.replace("def calc(self, a, b)", |
|||
"def calc(self, b, a)") |
|||
# This is silly, but it's what we got. |
|||
if change['contents'] == dedent(expected2): |
|||
self.assertSourceEqual(change['contents'], expected2) |
|||
else: |
|||
self.assertSourceEqual(change['contents'], expected) |
|||
|
|||
@unittest.skipIf(sys.version_info >= (3, 5), "Python 3.5 not supported") |
|||
def test_should_refactor_global(self): |
|||
ref = refactor.Refactor(self.project_root, self.filename) |
|||
(change,) = ref.refactor_extract_method(self.offset, 104, |
|||
"calc", True) |
|||
self.assertEqual(change['action'], 'change') |
|||
self.assertEqual(change['file'], self.filename) |
|||
expected = """\ |
|||
class Foo(object): |
|||
def spaghetti(self, a, b): |
|||
return calc(a, b) |
|||
|
|||
def calc(a, b): |
|||
x = a + 5 |
|||
y = b + 23 |
|||
return y |
|||
""" |
|||
expected2 = expected.replace("return calc(a, b)", |
|||
"return calc(b, a)") |
|||
expected2 = expected2.replace("def calc(a, b)", |
|||
"def calc(b, a)") |
|||
if change['contents'] == dedent(expected2): |
|||
self.assertSourceEqual(change['contents'], expected2) |
|||
else: |
|||
self.assertSourceEqual(change['contents'], expected) |
|||
|
|||
|
|||
@unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") |
|||
class TestUseFunction(RefactorTestCase): |
|||
def test_should_refactor(self): |
|||
filename, offset = self.create_file( |
|||
"foo.py", |
|||
"""\ |
|||
def _|_add_and_multiply(a, b, c): |
|||
temp = a + b |
|||
return temp * c |
|||
|
|||
f = 1 + 2 |
|||
g = f * 3 |
|||
""") |
|||
|
|||
ref = refactor.Refactor(self.project_root, filename) |
|||
(change,) = ref.refactor_use_function(offset) |
|||
|
|||
self.assertEqual(change['action'], 'change') |
|||
self.assertEqual(change['file'], filename) |
|||
self.assertSourceEqual(change['contents'], |
|||
"""\ |
|||
def add_and_multiply(a, b, c): |
|||
temp = a + b |
|||
return temp * c |
|||
|
|||
g = add_and_multiply(1, 2, 3) |
|||
""") |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __abs__ |
|||
# key: __abs__ |
|||
# group: Special methods |
|||
# -- |
|||
def __abs__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __add__ |
|||
# key: __add__ |
|||
# group: Special methods |
|||
# -- |
|||
def __add__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __and__ |
|||
# key: __and__ |
|||
# group: Special methods |
|||
# -- |
|||
def __and__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __bool__ |
|||
# key: __bool__ |
|||
# group: Special methods |
|||
# -- |
|||
def __bool__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __call__ |
|||
# key: __call__ |
|||
# group: Special methods |
|||
# -- |
|||
def __call__(self, ${1:*args}): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __cmp__ |
|||
# key: __cmp__ |
|||
# group: Special methods |
|||
# -- |
|||
def __cmp__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __coerce__ |
|||
# key: __coerce__ |
|||
# group: Special methods |
|||
# -- |
|||
def __coerce__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __complex__ |
|||
# key: __complex__ |
|||
# group: Special methods |
|||
# -- |
|||
def __complex__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __contains__ |
|||
# key: __contains__ |
|||
# group: Special methods |
|||
# -- |
|||
def __contains__(self, item): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __del__ |
|||
# key: __del__ |
|||
# group: Special methods |
|||
# -- |
|||
def __del__(self): |
|||
$0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __delattr__ |
|||
# key: __delattr__ |
|||
# group: Special methods |
|||
# -- |
|||
def __delattr__(self, name): |
|||
$0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __delete__ |
|||
# key: __delete__ |
|||
# group: Special methods |
|||
# -- |
|||
def __delete__(self, instance): |
|||
$0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __delitem__ |
|||
# key: __delitem__ |
|||
# group: Special methods |
|||
# -- |
|||
def __delitem__(self, key): |
|||
$0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __div__ |
|||
# key: __div__ |
|||
# group: Special methods |
|||
# -- |
|||
def __div__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __divmod__ |
|||
# key: __divmod__ |
|||
# group: Special methods |
|||
# -- |
|||
def __divmod__(self, other): |
|||
return $0 |
|||
@ -1,9 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __enter__ |
|||
# key: __enter__ |
|||
# group: Special methods |
|||
# -- |
|||
def __enter__(self): |
|||
$0 |
|||
|
|||
return self |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __eq__ |
|||
# key: __eq__ |
|||
# group: Special methods |
|||
# -- |
|||
def __eq__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __exit__ |
|||
# key: __exit__ |
|||
# group: Special methods |
|||
# -- |
|||
def __exit__(self, exc_type, exc_value, traceback): |
|||
$0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __float__ |
|||
# key: __float__ |
|||
# group: Special methods |
|||
# -- |
|||
def __float__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __floordiv__ |
|||
# key: __floordiv__ |
|||
# group: Special methods |
|||
# -- |
|||
def __floordiv__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __ge__ |
|||
# key: __ge__ |
|||
# group: Special methods |
|||
# -- |
|||
def __ge__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __get__ |
|||
# key: __get__ |
|||
# group: Special methods |
|||
# -- |
|||
def __get__(self, instance, owner): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __getattr__ |
|||
# key: __getattr__ |
|||
# group: Special methods |
|||
# -- |
|||
def __getattr__(self, name): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __getattribute__ |
|||
# key: __getattribute__ |
|||
# group: Special methods |
|||
# -- |
|||
def __getattribute__(self, name): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __getitem__ |
|||
# key: __getitem__ |
|||
# group: Special methods |
|||
# -- |
|||
def __getitem__(self, key): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __gt__ |
|||
# key: __gt__ |
|||
# group: Special methods |
|||
# -- |
|||
def __gt__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __hash__ |
|||
# key: __hash__ |
|||
# group: Special methods |
|||
# -- |
|||
def __hash__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __hex__ |
|||
# key: __hex__ |
|||
# group: Special methods |
|||
# -- |
|||
def __hex__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __iadd__ |
|||
# key: __iadd__ |
|||
# group: Special methods |
|||
# -- |
|||
def __iadd__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __iand__ |
|||
# key: __iand__ |
|||
# group: Special methods |
|||
# -- |
|||
def __iand__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __idiv__ |
|||
# key: __idiv__ |
|||
# group: Special methods |
|||
# -- |
|||
def __idiv__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __ifloordiv__ |
|||
# key: __ifloordiv__ |
|||
# group: Special methods |
|||
# -- |
|||
def __ifloordiv__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __ilshift__ |
|||
# key: __ilshift__ |
|||
# group: Special methods |
|||
# -- |
|||
def __ilshift__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __imod__ |
|||
# key: __imod__ |
|||
# group: Special methods |
|||
# -- |
|||
def __imod__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __imul__ |
|||
# key: __imul__ |
|||
# group: Special methods |
|||
# -- |
|||
def __imul__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __index__ |
|||
# key: __index__ |
|||
# group: Special methods |
|||
# -- |
|||
def __index__(self): |
|||
return $0 |
|||
@ -1,10 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __init__ with assignment |
|||
# key: __init__ |
|||
# group: Special methods |
|||
# -- |
|||
def __init__(self${1:, args}): |
|||
"""$2 |
|||
|
|||
""" |
|||
${1:$(elpy-snippet-init-assignments yas-text)} |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __instancecheck__ |
|||
# key: __instancecheck__ |
|||
# group: Special methods |
|||
# -- |
|||
def __instancecheck__(self, instance): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __int__ |
|||
# key: __int__ |
|||
# group: Special methods |
|||
# -- |
|||
def __int__(self): |
|||
$0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __invert__ |
|||
# key: __invert__ |
|||
# group: Special methods |
|||
# -- |
|||
def __invert__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __ior__ |
|||
# key: __ior__ |
|||
# group: Special methods |
|||
# -- |
|||
def __ior__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __ipow__ |
|||
# key: __ipow__ |
|||
# group: Special methods |
|||
# -- |
|||
def __ipow__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __irshift__ |
|||
# key: __irshift__ |
|||
# group: Special methods |
|||
# -- |
|||
def __irshift__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __isub__ |
|||
# key: __isub__ |
|||
# group: Special methods |
|||
# -- |
|||
def __isub__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __iter__ |
|||
# key: __iter__ |
|||
# group: Special methods |
|||
# -- |
|||
def __iter__(self): |
|||
$0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __itruediv__ |
|||
# key: __itruediv__ |
|||
# group: Special methods |
|||
# -- |
|||
def __itruediv__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __ixor__ |
|||
# key: __ixor__ |
|||
# group: Special methods |
|||
# -- |
|||
def __ixor__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __le__ |
|||
# key: __le__ |
|||
# group: Special methods |
|||
# -- |
|||
def __le__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __len__ |
|||
# key: __len__ |
|||
# group: Special methods |
|||
# -- |
|||
def __len__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __long__ |
|||
# key: __long__ |
|||
# group: Special methods |
|||
# -- |
|||
def __long__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __lshift__ |
|||
# key: __lshift__ |
|||
# group: Special methods |
|||
# -- |
|||
def __lshift__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __lt__ |
|||
# key: __lt__ |
|||
# group: Special methods |
|||
# -- |
|||
def __lt__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __mod__ |
|||
# key: __mod__ |
|||
# group: Special methods |
|||
# -- |
|||
def __mod__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __mul__ |
|||
# key: __mul__ |
|||
# group: Special methods |
|||
# -- |
|||
def __mul__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __ne__ |
|||
# key: __ne__ |
|||
# group: Special methods |
|||
# -- |
|||
def __ne__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __neg__ |
|||
# key: __neg__ |
|||
# group: Special methods |
|||
# -- |
|||
def __neg__(self): |
|||
return $0 |
|||
@ -1,10 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __new__ |
|||
# key: __new__ |
|||
# group: Special methods |
|||
# -- |
|||
def __new__(cls${1:, args}): |
|||
"""$2 |
|||
|
|||
""" |
|||
${1:$(elpy-snippet-init-assignments yas-text)} |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __nonzero__ |
|||
# key: __nonzero__ |
|||
# group: Special methods |
|||
# -- |
|||
def __nonzero__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __oct__ |
|||
# key: __oct__ |
|||
# group: Special methods |
|||
# -- |
|||
def __oct__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __or__ |
|||
# key: __or__ |
|||
# group: Special methods |
|||
# -- |
|||
def __or__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __pos__ |
|||
# key: __pos__ |
|||
# group: Special methods |
|||
# -- |
|||
def __pos__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __pow__ |
|||
# key: __pow__ |
|||
# group: Special methods |
|||
# -- |
|||
def __pow__(self, other, modulo=None): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __radd__ |
|||
# key: __radd__ |
|||
# group: Special methods |
|||
# -- |
|||
def __radd__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __rand__ |
|||
# key: __rand__ |
|||
# group: Special methods |
|||
# -- |
|||
def __rand__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __rdivmod__ |
|||
# key: __rdivmod__ |
|||
# group: Special methods |
|||
# -- |
|||
def __rdivmod__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __repr__ |
|||
# key: __repr__ |
|||
# group: Special methods |
|||
# -- |
|||
def __repr__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __reversed__ |
|||
# key: __reversed__ |
|||
# group: Special methods |
|||
# -- |
|||
def __reversed__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __rfloordiv__ |
|||
# key: __rfloordiv__ |
|||
# group: Special methods |
|||
# -- |
|||
def __rfloordiv__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __rlshift__ |
|||
# key: __rlshift__ |
|||
# group: Special methods |
|||
# -- |
|||
def __rlshift__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __rmod__ |
|||
# key: __rmod__ |
|||
# group: Special methods |
|||
# -- |
|||
def __rmod__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __rmul__ |
|||
# key: __rmul__ |
|||
# group: Special methods |
|||
# -- |
|||
def __rmul__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __ror__ |
|||
# key: __ror__ |
|||
# group: Special methods |
|||
# -- |
|||
def __ror__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __rpow__ |
|||
# key: __rpow__ |
|||
# group: Special methods |
|||
# -- |
|||
def __rpow__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __rrshift__ |
|||
# key: __rrshift__ |
|||
# group: Special methods |
|||
# -- |
|||
def __rrshift__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __rshift__ |
|||
# key: __rshift__ |
|||
# group: Special methods |
|||
# -- |
|||
def __rshift__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __rsub__ |
|||
# key: __rsub__ |
|||
# group: Special methods |
|||
# -- |
|||
def __rsub__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __rtruediv__ |
|||
# key: __rtruediv__ |
|||
# group: Special methods |
|||
# -- |
|||
def __rtruediv__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __rxor__ |
|||
# key: __rxor__ |
|||
# group: Special methods |
|||
# -- |
|||
def __rxor__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __set__ |
|||
# key: __set__ |
|||
# group: Special methods |
|||
# -- |
|||
def __set__(self, instance, value): |
|||
$0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __setattr__ |
|||
# key: __setattr__ |
|||
# group: Special methods |
|||
# -- |
|||
def __setattr__(self, name, value): |
|||
$0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __setitem__ |
|||
# key: __setitem__ |
|||
# group: Special methods |
|||
# -- |
|||
def __setitem__(self, key, value): |
|||
$0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __slots__ |
|||
# key: __slots__ |
|||
# group: Class attributes |
|||
# -- |
|||
__slots__ = ($1) |
|||
$0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __str__ |
|||
# key: __str__ |
|||
# group: Special methods |
|||
# -- |
|||
def __str__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __sub__ |
|||
# key: __sub__ |
|||
# group: Special methods |
|||
# -- |
|||
def __sub__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __subclasscheck__ |
|||
# key: __subclasscheck__ |
|||
# group: Special methods |
|||
# -- |
|||
def __subclasscheck__(self, instance): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __truediv__ |
|||
# key: __truediv__ |
|||
# group: Special methods |
|||
# -- |
|||
def __truediv__(self, other): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __unicode__ |
|||
# key: __unicode__ |
|||
# group: Special methods |
|||
# -- |
|||
def __unicode__(self): |
|||
return $0 |
|||
@ -1,7 +0,0 @@ |
|||
# -*- mode: snippet -*- |
|||
# name: __xor__ |
|||
# key: __xor__ |
|||
# group: Special methods |
|||
# -- |
|||
def __xor__(self, other): |
|||
return $0 |
|||
@ -0,0 +1,17 @@ |
|||
(define-package "elpy" "20210328.1852" "Emacs Python Development Environment" |
|||
'((company "0.9.2") |
|||
(emacs "24.4") |
|||
(highlight-indentation "0.5.0") |
|||
(pyvenv "1.3") |
|||
(yasnippet "0.8.0") |
|||
(s "1.11.0")) |
|||
:commit "2203597e1254eba345d6873daa40c7b9d144931c" :authors |
|||
'(("Jorgen Schaefer <contact@jorgenschaefer.de>, Gaby Launay" . "gaby.launay@protonmail.com")) |
|||
:maintainer |
|||
'("Jorgen Schaefer <contact@jorgenschaefer.de>, Gaby Launay" . "gaby.launay@protonmail.com") |
|||
:keywords |
|||
'("python" "ide" "languages" "tools") |
|||
:url "https://github.com/jorgenschaefer/elpy") |
|||
;; Local Variables: |
|||
;; no-byte-compile: t |
|||
;; End: |
|||
@ -0,0 +1,320 @@ |
|||
;;; elpy-refactor.el --- Refactoring mode for Elpy |
|||
|
|||
;; Copyright (C) 2020 Gaby Launay |
|||
|
|||
;; Author: Gaby Launay <gaby.launay@protonmail.com> |
|||
;; URL: https://github.com/jorgenschaefer/elpy |
|||
|
|||
;; This program is free software; you can redistribute it and/or |
|||
;; modify it under the terms of the GNU General Public License |
|||
;; as published by the Free Software Foundation; either version 3 |
|||
;; of the License, or (at your option) 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. |
|||
|
|||
;; You should have received a copy of the GNU General Public License |
|||
;; along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
|
|||
;;; Commentary: |
|||
|
|||
;; This file provides an interface, including a major mode, to use |
|||
;; refactoring options provided by the Jedi library. |
|||
|
|||
;;; Code: |
|||
|
|||
;; We require elpy, but elpy loads us, so we shouldn't load it back. |
|||
;; (require 'elpy) |
|||
(require 'diff-mode) |
|||
|
|||
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
;;; Refactor mode (for applying diffs) |
|||
|
|||
(defvar elpy-refactor--saved-window-configuration nil |
|||
"Saved windows configuration, so that we can restore it after `elpy-refactor' has done its thing.") |
|||
|
|||
(defvar elpy-refactor--saved-pos nil |
|||
"Line and column number of the position we were at before starting refactoring.") |
|||
|
|||
(defvar elpy-refactor--modified-buffers '() |
|||
"Keep track of the buffers modified by the current refactoring sessions.") |
|||
|
|||
(defun elpy-refactor--apply-diff (proj-path diff) |
|||
"Apply DIFF, looking for the files in PROJ-PATH." |
|||
(let ((current-line (line-number-at-pos (point))) |
|||
(current-col (- (point) (line-beginning-position)))) |
|||
(with-current-buffer (get-buffer-create " *Elpy Refactor*") |
|||
(elpy-refactor-mode) |
|||
(let ((inhibit-read-only t)) |
|||
(erase-buffer) |
|||
(insert diff)) |
|||
(setq default-directory proj-path) |
|||
(goto-char (point-min)) |
|||
(elpy-refactor--apply-whole-diff)) |
|||
(condition-case nil |
|||
(progn |
|||
(goto-char (point-min)) |
|||
(forward-line (- current-line 1)) |
|||
(beginning-of-line) |
|||
(forward-char current-col)) |
|||
(error)) |
|||
)) |
|||
|
|||
(defun elpy-refactor--display-diff (proj-path diff) |
|||
"Display DIFF in a `diff-mode' window. |
|||
|
|||
DIFF files should be relative to PROJ-PATH." |
|||
(setq elpy-refactor--saved-window-configuration (current-window-configuration) |
|||
elpy-refactor--saved-pos (list (line-number-at-pos (point) t) |
|||
(- (point) (line-beginning-position))) |
|||
elpy-refactor--modified-buffers '()) |
|||
(with-current-buffer (get-buffer-create "*Elpy Refactor*") |
|||
(elpy-refactor-mode) |
|||
(let ((inhibit-read-only t)) |
|||
(erase-buffer) |
|||
(insert (propertize |
|||
(substitute-command-keys |
|||
(concat |
|||
"\\[diff-file-next] and \\[diff-file-prev] -- Move between files\n" |
|||
"\\[diff-hunk-next] and \\[diff-hunk-prev] -- Move between hunks\n" |
|||
"\\[diff-split-hunk] -- Split the current hunk at point\n" |
|||
"\\[elpy-refactor--apply-hunk] -- Apply the current hunk\n" |
|||
"\\[diff-kill-hunk] -- Kill the current hunk\n" |
|||
"\\[elpy-refactor--apply-whole-diff] -- Apply the whole diff\n" |
|||
"\\[elpy-refactor--quit] -- Quit\n")) |
|||
'face 'bold) |
|||
"\n\n") |
|||
(align-regexp (point-min) (point-max) "\\(\\s-*\\) -- ") |
|||
(goto-char (point-min)) |
|||
(while (search-forward " -- " nil t) |
|||
(replace-match " " nil t)) |
|||
(goto-char (point-max)) |
|||
(insert diff)) |
|||
(setq default-directory proj-path) |
|||
(goto-char (point-min)) |
|||
(if (diff--some-hunks-p) |
|||
(progn |
|||
(select-window (display-buffer (current-buffer))) |
|||
(diff-hunk-next)) |
|||
;; quit if not diff at all... |
|||
(message "No differences to validate") |
|||
(kill-buffer (current-buffer))))) |
|||
|
|||
(defvar elpy-refactor-mode-map |
|||
(let ((map (make-sparse-keymap))) |
|||
(define-key map (kbd "C-c C-c") 'elpy-refactor--apply-hunk) |
|||
(define-key map (kbd "C-c C-a") 'elpy-refactor--apply-whole-diff) |
|||
(define-key map (kbd "C-c C-x") 'diff-kill-hunk) |
|||
(define-key map (kbd "q") 'elpy-refactor--quit) |
|||
(define-key map (kbd "C-c C-k") 'elpy-refactor--quit) |
|||
(define-key map (kbd "h") 'describe-mode) |
|||
(define-key map (kbd "?") 'describe-mode) |
|||
map) |
|||
"The key map for `elpy-refactor-mode'.") |
|||
|
|||
(define-derived-mode elpy-refactor-mode diff-mode "Elpy Refactor" |
|||
"Mode to display refactoring actions and ask confirmation from the user. |
|||
|
|||
\\{elpy-refactor-mode-map}" |
|||
:group 'elpy |
|||
(view-mode 1)) |
|||
|
|||
(defun elpy-refactor--apply-hunk () |
|||
"Apply the current hunk." |
|||
(interactive) |
|||
(save-excursion |
|||
(diff-apply-hunk)) |
|||
;; keep track of modified buffers |
|||
(let ((buf (find-buffer-visiting (diff-find-file-name)))) |
|||
(when buf |
|||
(add-to-list 'elpy-refactor--modified-buffers buf))) |
|||
;; |
|||
(diff-hunk-kill) |
|||
(unless (diff--some-hunks-p) |
|||
(elpy-refactor--quit))) |
|||
|
|||
(defun elpy-refactor--apply-whole-diff () |
|||
"Apply the whole diff and quit." |
|||
(interactive) |
|||
(goto-char (point-min)) |
|||
(diff-hunk-next) |
|||
(while (diff--some-hunks-p) |
|||
(let ((buf (find-buffer-visiting (diff-find-file-name)))) |
|||
(when buf |
|||
(add-to-list 'elpy-refactor--modified-buffers buf))) |
|||
(condition-case nil |
|||
(progn |
|||
(save-excursion |
|||
(diff-apply-hunk)) |
|||
(diff-hunk-kill)) |
|||
(error (diff-hunk-next)))) ;; if a hunk fail, switch to the next one |
|||
;; quit |
|||
(elpy-refactor--quit)) |
|||
|
|||
(defun elpy-refactor--quit () |
|||
"Quit the refactoring session." |
|||
(interactive) |
|||
;; save modified buffers |
|||
(dolist (buf elpy-refactor--modified-buffers) |
|||
(with-current-buffer buf |
|||
(basic-save-buffer))) |
|||
(setq elpy-refactor--modified-buffers '()) |
|||
;; kill refactoring buffer |
|||
(kill-buffer (current-buffer)) |
|||
;; Restore window configuration |
|||
(when elpy-refactor--saved-window-configuration |
|||
(set-window-configuration elpy-refactor--saved-window-configuration) |
|||
(setq elpy-refactor--saved-window-configuration nil)) |
|||
;; Restore cursor position |
|||
(when elpy-refactor--saved-pos |
|||
(goto-char (point-min)) |
|||
(forward-line (- (car elpy-refactor--saved-pos) 1)) |
|||
(forward-char (car (cdr elpy-refactor--saved-pos))) |
|||
(setq elpy-refactor--saved-pos nil))) |
|||
|
|||
|
|||
|
|||
;;;;;;;;;;;;;;;;; |
|||
;; User functions |
|||
|
|||
(defun elpy-refactor-rename (new-name &optional dontask) |
|||
"Rename the symbol at point to NEW-NAME. |
|||
|
|||
With a prefix argument (or if DONTASK is non-nil), |
|||
do not display the diff before applying." |
|||
(interactive (list |
|||
(let ((old-name (thing-at-point 'symbol))) |
|||
(if (or (not old-name) |
|||
(not (elpy-refactor--is-valid-symbol-p old-name))) |
|||
(error "No symbol at point") |
|||
(read-string |
|||
(format "New name for '%s': " |
|||
(thing-at-point 'symbol))))))) |
|||
(unless (and new-name |
|||
(elpy-refactor--is-valid-symbol-p new-name)) |
|||
(error "'%s' is not a valid python symbol")) |
|||
(message "Gathering occurences of '%s'..." |
|||
(thing-at-point 'symbol)) |
|||
(let* ((elpy-rpc-timeout 10) ;; refactoring can be long... |
|||
(diff (elpy-rpc-get-rename-diff new-name)) |
|||
(proj-path (alist-get 'project_path diff)) |
|||
(success (alist-get 'success diff)) |
|||
(diff (alist-get 'diff diff))) |
|||
(cond ((not success) |
|||
(error "Refactoring failed for some reason")) |
|||
((string= success "Not available") |
|||
(error "This functionnality needs jedi > 0.17.0, please update")) |
|||
((or dontask current-prefix-arg) |
|||
(message "Replacing '%s' with '%s'..." |
|||
(thing-at-point 'symbol) |
|||
new-name) |
|||
(elpy-refactor--apply-diff proj-path diff) |
|||
(message "Done")) |
|||
(t |
|||
(elpy-refactor--display-diff proj-path diff))))) |
|||
|
|||
(defun elpy-refactor-extract-variable (new-name) |
|||
"Extract the current region to a new variable NEW-NAME." |
|||
(interactive "sNew name: ") |
|||
(let ((beg (if (region-active-p) |
|||
(region-beginning) |
|||
(car (or (bounds-of-thing-at-point 'symbol) |
|||
(error "No symbol at point"))))) |
|||
(end (if (region-active-p) |
|||
(region-end) |
|||
(cdr (bounds-of-thing-at-point 'symbol))))) |
|||
(when (or (elpy-refactor--is-valid-symbol-p new-name) |
|||
(y-or-n-p "'%s' does not appear to be a valid python symbol. Are you sure you want to use it? ")) |
|||
(let* ((line-beg (save-excursion |
|||
(goto-char beg) |
|||
(line-number-at-pos))) |
|||
(line-end (save-excursion |
|||
(goto-char end) |
|||
(line-number-at-pos))) |
|||
(col-beg (save-excursion |
|||
(goto-char beg) |
|||
(- (point) (line-beginning-position)))) |
|||
(col-end (save-excursion |
|||
(goto-char end) |
|||
(- (point) (line-beginning-position)))) |
|||
(diff (elpy-rpc-get-extract-variable-diff |
|||
new-name line-beg line-end col-beg col-end)) |
|||
(proj-path (alist-get 'project_path diff)) |
|||
(success (alist-get 'success diff)) |
|||
(diff (alist-get 'diff diff))) |
|||
(cond ((not success) |
|||
(error "We could not extract the selection as a variable")) |
|||
((string= success "Not available") |
|||
(error "This functionnality needs jedi > 0.17.0, please update")) |
|||
(t |
|||
(deactivate-mark) |
|||
(elpy-refactor--apply-diff proj-path diff))))))) |
|||
|
|||
(defun elpy-refactor-extract-function (new-name) |
|||
"Extract the current region to a new function NEW-NAME." |
|||
(interactive "sNew function name: ") |
|||
(unless (region-active-p) |
|||
(error "No selection")) |
|||
(when (or (elpy-refactor--is-valid-symbol-p new-name) |
|||
(y-or-n-p "'%s' does not appear to be a valid python symbol. Are you sure you want to use it? ")) |
|||
(let* ((line-beg (save-excursion |
|||
(goto-char (region-beginning)) |
|||
(line-number-at-pos))) |
|||
(line-end (save-excursion |
|||
(goto-char (region-end)) |
|||
(line-number-at-pos))) |
|||
(col-beg (save-excursion |
|||
(goto-char (region-beginning)) |
|||
(- (point) (line-beginning-position)))) |
|||
(col-end (save-excursion |
|||
(goto-char (region-end)) |
|||
(- (point) (line-beginning-position)))) |
|||
(diff (elpy-rpc-get-extract-function-diff |
|||
new-name line-beg line-end col-beg col-end)) |
|||
(proj-path (alist-get 'project_path diff)) |
|||
(success (alist-get 'success diff)) |
|||
(diff (alist-get 'diff diff))) |
|||
(cond ((not success) |
|||
(error "We could not extract the selection as a function")) |
|||
((string= success "Not available") |
|||
(error "This functionnality needs jedi > 0.17.0, please update")) |
|||
(t |
|||
(deactivate-mark) |
|||
(elpy-refactor--apply-diff proj-path diff)))))) |
|||
|
|||
(defun elpy-refactor-inline () |
|||
"Inline the variable at point." |
|||
(interactive) |
|||
(let* ((diff (elpy-rpc-get-inline-diff)) |
|||
(proj-path (alist-get 'project_path diff)) |
|||
(success (alist-get 'success diff)) |
|||
(diff (alist-get 'diff diff))) |
|||
(cond ((not success) |
|||
(error "We could not inline the variable '%s'" |
|||
(thing-at-point 'symbol))) |
|||
((string= success "Not available") |
|||
(error "This functionnality needs jedi > 0.17.0, please update")) |
|||
(t |
|||
(elpy-refactor--apply-diff proj-path diff))))) |
|||
|
|||
|
|||
;;;;;;;;;;;; |
|||
;; Utilities |
|||
|
|||
(defun elpy-refactor--is-valid-symbol-p (symbol) |
|||
"Return t if SYMBOL is a valid python symbol." |
|||
(eq 0 (string-match "^[a-zA-Z_][a-zA-Z0-9_]*$" symbol))) |
|||
|
|||
;;;;;;;;;;;; |
|||
;; Compatibility |
|||
(unless (fboundp 'diff--some-hunks-p) |
|||
(defun diff--some-hunks-p () |
|||
(save-excursion |
|||
(goto-char (point-min)) |
|||
(re-search-forward diff-hunk-header-re nil t)))) |
|||
|
|||
(provide 'elpy-refactor) |
|||
;;; elpy-refactor.el ends here |
|||
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue