k now... emacs>vim
parent
d701704955
commit
df3a90ee6b
|
@ -0,0 +1,77 @@
|
|||
;; load emacs 24's package system. Add MELPA repository.
|
||||
|
||||
;; Added by Package.el. This must come before configurations of
|
||||
;; installed packages. Don't delete this line. If you don't want it,
|
||||
;; just comment it out by adding a semicolon to the start of the line.
|
||||
;; You may delete these explanatory comments.
|
||||
(package-initialize)
|
||||
|
||||
(when (>= emacs-major-version 24)
|
||||
(require 'package)
|
||||
(add-to-list
|
||||
'package-archives
|
||||
;; '("melpa" . "http://stable.melpa.org/packages/") ; many packages won't show if using stable
|
||||
'("melpa" . "http://melpa.milkbox.net/packages/")
|
||||
'("elpy" . "http://jorgenschaefer.github.io/packages/")
|
||||
))
|
||||
(custom-set-variables
|
||||
;; custom-set-variables was added by Custom.
|
||||
;; If you edit it by hand, you could mess it up, so be careful.
|
||||
;; Your init file should contain only one such instance.
|
||||
;; If there is more than one, they won't work right.
|
||||
'(custom-safe-themes
|
||||
(quote
|
||||
("66881e95c0eda61d34aa7f08ebacf03319d37fe202d68ecf6a1dbfd49d664bc3" default)))
|
||||
'(inhibit-startup-screen t)
|
||||
'(package-selected-packages (quote (elpy forest-blue-theme)))
|
||||
'(pyvenv-activate "~/.virtualenv/default"))
|
||||
|
||||
;; https://github.com/Boruch-Baum
|
||||
(defun my-copy-to-xclipboard(arg)
|
||||
(interactive "P")
|
||||
(cond
|
||||
((not (use-region-p))
|
||||
(message "Nothing to yank to X-clipboard"))
|
||||
((and (not (display-graphic-p))
|
||||
(/= 0 (shell-command-on-region
|
||||
(region-beginning) (region-end) "xsel -i -b")))
|
||||
(error "Is program `xsel' installed?"))
|
||||
(t
|
||||
(when (display-graphic-p)
|
||||
(call-interactively 'clipboard-kill-ring-save))
|
||||
(message "Yanked region to X-clipboard")
|
||||
(when arg
|
||||
(kill-region (region-beginning) (region-end)))
|
||||
(deactivate-mark))))
|
||||
|
||||
(defun my-cut-to-xclipboard()
|
||||
(interactive)
|
||||
(my-copy-to-xclipboard t))
|
||||
|
||||
(defun my-paste-from-xclipboard()
|
||||
"Uses shell command `xsel -o' to paste from x-clipboard. With
|
||||
one prefix arg, pastes from X-PRIMARY, and with two prefix args,
|
||||
pastes from X-SECONDARY."
|
||||
(interactive)
|
||||
(if (display-graphic-p)
|
||||
(clipboard-yank)
|
||||
(let*
|
||||
((opt (prefix-numeric-value current-prefix-arg))
|
||||
(opt (cond
|
||||
((= 1 opt) "b")
|
||||
((= 4 opt) "p")
|
||||
((= 16 opt) "s"))))
|
||||
(insert (shell-command-to-string (concat "xsel -o -" opt))))))
|
||||
|
||||
(global-set-key (kbd "C-c C-w") 'my-cut-to-xclipboard)
|
||||
(global-set-key (kbd "C-c M-w") 'my-copy-to-xclipboard)
|
||||
(global-set-key (kbd "C-c C-y") 'my-paste-from-xclipboard)
|
||||
|
||||
(load-theme 'forest-blue t)
|
||||
(elpy-enable)
|
||||
(custom-set-faces
|
||||
;; custom-set-faces was added by Custom.
|
||||
;; If you edit it by hand, you could mess it up, so be careful.
|
||||
;; Your init file should contain only one such instance.
|
||||
;; If there is more than one, they won't work right.
|
||||
)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
|||
Good signature from 474F05837FBDEF9B GNU ELPA Signing Agent <elpasign@elpa.gnu.org> (trust undefined) created at 2017-12-06T23:10:01+0100 using DSA
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,50 @@
|
|||
;;; company-abbrev.el --- company-mode completion backend for abbrev
|
||||
|
||||
;; Copyright (C) 2009-2011, 2015 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
(require 'abbrev)
|
||||
|
||||
(defun company-abbrev-insert (match)
|
||||
"Replace MATCH with the expanded abbrev."
|
||||
(expand-abbrev))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-abbrev (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend for abbrev."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-abbrev
|
||||
'company-abbrev-insert))
|
||||
(prefix (company-grab-symbol))
|
||||
(candidates (nconc
|
||||
(delete "" (all-completions arg global-abbrev-table))
|
||||
(delete "" (all-completions arg local-abbrev-table))))
|
||||
(meta (abbrev-expansion arg))))
|
||||
|
||||
(provide 'company-abbrev)
|
||||
;;; company-abbrev.el ends here
|
Binary file not shown.
|
@ -0,0 +1,318 @@
|
|||
;;; company-autoloads.el --- automatically extracted autoloads
|
||||
;;
|
||||
;;; Code:
|
||||
(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path))))
|
||||
|
||||
;;;### (autoloads nil "company" "company.el" (23071 43281 630231
|
||||
;;;;;; 946000))
|
||||
;;; Generated autoloads from company.el
|
||||
|
||||
(autoload 'company-mode "company" "\
|
||||
\"complete anything\"; is an in-buffer completion framework.
|
||||
Completion starts automatically, depending on the values
|
||||
`company-idle-delay' and `company-minimum-prefix-length'.
|
||||
|
||||
Completion can be controlled with the commands:
|
||||
`company-complete-common', `company-complete-selection', `company-complete',
|
||||
`company-select-next', `company-select-previous'. If these commands are
|
||||
called before `company-idle-delay', completion will also start.
|
||||
|
||||
Completions can be searched with `company-search-candidates' or
|
||||
`company-filter-candidates'. These can be used while completion is
|
||||
inactive, as well.
|
||||
|
||||
The completion data is retrieved using `company-backends' and displayed
|
||||
using `company-frontends'. If you want to start a specific backend, call
|
||||
it interactively or use `company-begin-backend'.
|
||||
|
||||
By default, the completions list is sorted alphabetically, unless the
|
||||
backend chooses otherwise, or `company-transformers' changes it later.
|
||||
|
||||
regular keymap (`company-mode-map'):
|
||||
|
||||
\\{company-mode-map}
|
||||
keymap during active completions (`company-active-map'):
|
||||
|
||||
\\{company-active-map}
|
||||
|
||||
\(fn &optional ARG)" t nil)
|
||||
|
||||
(defvar global-company-mode nil "\
|
||||
Non-nil if Global Company mode is enabled.
|
||||
See the `global-company-mode' command
|
||||
for a description of this minor mode.
|
||||
Setting this variable directly does not take effect;
|
||||
either customize it (see the info node `Easy Customization')
|
||||
or call the function `global-company-mode'.")
|
||||
|
||||
(custom-autoload 'global-company-mode "company" nil)
|
||||
|
||||
(autoload 'global-company-mode "company" "\
|
||||
Toggle Company mode in all buffers.
|
||||
With prefix ARG, enable Global Company mode if ARG is positive;
|
||||
otherwise, disable it. If called from Lisp, enable the mode if
|
||||
ARG is omitted or nil.
|
||||
|
||||
Company mode is enabled in all buffers where
|
||||
`company-mode-on' would do it.
|
||||
See `company-mode' for more information on Company mode.
|
||||
|
||||
\(fn &optional ARG)" t nil)
|
||||
|
||||
(autoload 'company-manual-begin "company" "\
|
||||
|
||||
|
||||
\(fn)" t nil)
|
||||
|
||||
(autoload 'company-complete "company" "\
|
||||
Insert the common part of all candidates or the current selection.
|
||||
The first time this is called, the common part is inserted, the second
|
||||
time, or when the selection has been changed, the selected candidate is
|
||||
inserted.
|
||||
|
||||
\(fn)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-abbrev" "company-abbrev.el" (23071
|
||||
;;;;;; 43281 766896 348000))
|
||||
;;; Generated autoloads from company-abbrev.el
|
||||
|
||||
(autoload 'company-abbrev "company-abbrev" "\
|
||||
`company-mode' completion backend for abbrev.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-bbdb" "company-bbdb.el" (23071 43281
|
||||
;;;;;; 720230 455000))
|
||||
;;; Generated autoloads from company-bbdb.el
|
||||
|
||||
(autoload 'company-bbdb "company-bbdb" "\
|
||||
`company-mode' completion backend for BBDB.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORE)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-css" "company-css.el" (23071 43281
|
||||
;;;;;; 620232 111000))
|
||||
;;; Generated autoloads from company-css.el
|
||||
|
||||
(autoload 'company-css "company-css" "\
|
||||
`company-mode' completion backend for `css-mode'.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-dabbrev" "company-dabbrev.el" (23071
|
||||
;;;;;; 43281 670231 283000))
|
||||
;;; Generated autoloads from company-dabbrev.el
|
||||
|
||||
(autoload 'company-dabbrev "company-dabbrev" "\
|
||||
dabbrev-like `company-mode' completion backend.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-dabbrev-code" "company-dabbrev-code.el"
|
||||
;;;;;; (23071 43281 653564 892000))
|
||||
;;; Generated autoloads from company-dabbrev-code.el
|
||||
|
||||
(autoload 'company-dabbrev-code "company-dabbrev-code" "\
|
||||
dabbrev-like `company-mode' backend for code.
|
||||
The backend looks for all symbols in the current buffer that aren't in
|
||||
comments or strings.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-elisp" "company-elisp.el" (23071 43281
|
||||
;;;;;; 786896 17000))
|
||||
;;; Generated autoloads from company-elisp.el
|
||||
|
||||
(autoload 'company-elisp "company-elisp" "\
|
||||
`company-mode' completion backend for Emacs Lisp.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-etags" "company-etags.el" (23071 43281
|
||||
;;;;;; 636898 502000))
|
||||
;;; Generated autoloads from company-etags.el
|
||||
|
||||
(autoload 'company-etags "company-etags" "\
|
||||
`company-mode' completion backend for etags.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-files" "company-files.el" (23071 43281
|
||||
;;;;;; 676897 839000))
|
||||
;;; Generated autoloads from company-files.el
|
||||
|
||||
(autoload 'company-files "company-files" "\
|
||||
`company-mode' completion backend existing file names.
|
||||
Completions works for proper absolute and relative files paths.
|
||||
File paths with spaces are only supported inside strings.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-gtags" "company-gtags.el" (23071 43281
|
||||
;;;;;; 580232 773000))
|
||||
;;; Generated autoloads from company-gtags.el
|
||||
|
||||
(autoload 'company-gtags "company-gtags" "\
|
||||
`company-mode' completion backend for GNU Global.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-ispell" "company-ispell.el" (23071
|
||||
;;;;;; 43281 776896 183000))
|
||||
;;; Generated autoloads from company-ispell.el
|
||||
|
||||
(autoload 'company-ispell "company-ispell" "\
|
||||
`company-mode' completion backend using Ispell.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-keywords" "company-keywords.el" (23071
|
||||
;;;;;; 43281 696897 508000))
|
||||
;;; Generated autoloads from company-keywords.el
|
||||
|
||||
(autoload 'company-keywords "company-keywords" "\
|
||||
`company-mode' backend for programming language keywords.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-nxml" "company-nxml.el" (23071 43281
|
||||
;;;;;; 740230 123000))
|
||||
;;; Generated autoloads from company-nxml.el
|
||||
|
||||
(autoload 'company-nxml "company-nxml" "\
|
||||
`company-mode' completion backend for `nxml-mode'.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-oddmuse" "company-oddmuse.el" (23071
|
||||
;;;;;; 43281 603565 721000))
|
||||
;;; Generated autoloads from company-oddmuse.el
|
||||
|
||||
(autoload 'company-oddmuse "company-oddmuse" "\
|
||||
`company-mode' completion backend for `oddmuse-mode'.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-semantic" "company-semantic.el" (23071
|
||||
;;;;;; 43281 590232 608000))
|
||||
;;; Generated autoloads from company-semantic.el
|
||||
|
||||
(autoload 'company-semantic "company-semantic" "\
|
||||
`company-mode' completion backend using CEDET Semantic.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-tempo" "company-tempo.el" (23071 43281
|
||||
;;;;;; 660231 448000))
|
||||
;;; Generated autoloads from company-tempo.el
|
||||
|
||||
(autoload 'company-tempo "company-tempo" "\
|
||||
`company-mode' completion backend for tempo.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-tng" "company-tng.el" (23071 43281
|
||||
;;;;;; 730230 289000))
|
||||
;;; Generated autoloads from company-tng.el
|
||||
|
||||
(autoload 'company-tng-frontend "company-tng" "\
|
||||
When the user changes the selection at least once, this
|
||||
frontend will display the candidate in the buffer as if it's
|
||||
already there and any key outside of `company-active-map' will
|
||||
confirm the selection and finish the completion.
|
||||
|
||||
\(fn COMMAND)" nil nil)
|
||||
|
||||
(autoload 'company-tng-configure-default "company-tng" "\
|
||||
Applies the default configuration to enable company-tng.
|
||||
|
||||
\(fn)" nil nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-xcode" "company-xcode.el" (23071 43281
|
||||
;;;;;; 756896 514000))
|
||||
;;; Generated autoloads from company-xcode.el
|
||||
|
||||
(autoload 'company-xcode "company-xcode" "\
|
||||
`company-mode' completion backend for Xcode projects.
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORED)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "company-yasnippet" "company-yasnippet.el"
|
||||
;;;;;; (23071 43281 750229 958000))
|
||||
;;; Generated autoloads from company-yasnippet.el
|
||||
|
||||
(autoload 'company-yasnippet "company-yasnippet" "\
|
||||
`company-mode' backend for `yasnippet'.
|
||||
|
||||
This backend should be used with care, because as long as there are
|
||||
snippets defined for the current major mode, this backend will always
|
||||
shadow backends that come after it. Recommended usages:
|
||||
|
||||
* In a buffer-local value of `company-backends', grouped with a backend or
|
||||
several that provide actual text completions.
|
||||
|
||||
(add-hook 'js-mode-hook
|
||||
(lambda ()
|
||||
(set (make-local-variable 'company-backends)
|
||||
'((company-dabbrev-code company-yasnippet)))))
|
||||
|
||||
* After keyword `:with', grouped with other backends.
|
||||
|
||||
(push '(company-semantic :with company-yasnippet) company-backends)
|
||||
|
||||
* Not in `company-backends', just bound to a key.
|
||||
|
||||
(global-set-key (kbd \"C-c y\") 'company-yasnippet)
|
||||
|
||||
\(fn COMMAND &optional ARG &rest IGNORE)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil nil ("company-capf.el" "company-clang.el" "company-cmake.el"
|
||||
;;;;;; "company-eclim.el" "company-pkg.el" "company-template.el")
|
||||
;;;;;; (23071 43281 706897 342000))
|
||||
|
||||
;;;***
|
||||
|
||||
;; Local Variables:
|
||||
;; version-control: never
|
||||
;; no-byte-compile: t
|
||||
;; no-update-autoloads: t
|
||||
;; End:
|
||||
;;; company-autoloads.el ends here
|
|
@ -0,0 +1,61 @@
|
|||
;;; company-bbdb.el --- company-mode completion backend for BBDB in message-mode
|
||||
|
||||
;; Copyright (C) 2013-2014, 2016 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Jan Tatarik <jan.tatarik@gmail.com>
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
|
||||
(declare-function bbdb-record-get-field "bbdb")
|
||||
(declare-function bbdb-records "bbdb")
|
||||
(declare-function bbdb-dwim-mail "bbdb-com")
|
||||
(declare-function bbdb-search "bbdb-com")
|
||||
|
||||
(defgroup company-bbdb nil
|
||||
"Completion backend for BBDB."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-bbdb-modes '(message-mode)
|
||||
"Major modes in which `company-bbdb' may complete."
|
||||
:type '(repeat (symbol :tag "Major mode"))
|
||||
:package-version '(company . "0.8.8"))
|
||||
|
||||
(defun company-bbdb--candidates (arg)
|
||||
(cl-mapcan (lambda (record)
|
||||
(mapcar (lambda (mail) (bbdb-dwim-mail record mail))
|
||||
(bbdb-record-get-field record 'mail)))
|
||||
(eval '(bbdb-search (bbdb-records) arg nil arg))))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-bbdb (command &optional arg &rest ignore)
|
||||
"`company-mode' completion backend for BBDB."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-bbdb))
|
||||
(prefix (and (memq major-mode company-bbdb-modes)
|
||||
(featurep 'bbdb-com)
|
||||
(looking-back "^\\(To\\|Cc\\|Bcc\\): *.*? *\\([^,;]*\\)"
|
||||
(line-beginning-position))
|
||||
(match-string-no-properties 2)))
|
||||
(candidates (company-bbdb--candidates arg))
|
||||
(sorted t)
|
||||
(no-cache t)))
|
||||
|
||||
(provide 'company-bbdb)
|
||||
;;; company-bbdb.el ends here
|
Binary file not shown.
|
@ -0,0 +1,167 @@
|
|||
;;; company-capf.el --- company-mode completion-at-point-functions backend -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2013-2016 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defvar company--capf-cache nil)
|
||||
|
||||
(defun company--capf-data ()
|
||||
(let ((cache company--capf-cache))
|
||||
(if (and (equal (current-buffer) (car cache))
|
||||
(equal (point) (car (setq cache (cdr cache))))
|
||||
(equal (buffer-chars-modified-tick) (car (setq cache (cdr cache)))))
|
||||
(cadr cache)
|
||||
(let ((data (company--capf-data-real)))
|
||||
(setq company--capf-cache
|
||||
(list (current-buffer) (point) (buffer-chars-modified-tick) data))
|
||||
data))))
|
||||
|
||||
(defun company--capf-data-real ()
|
||||
(cl-letf* (((default-value 'completion-at-point-functions)
|
||||
;; Ignore tags-completion-at-point-function because it subverts
|
||||
;; company-etags in the default value of company-backends, where
|
||||
;; the latter comes later.
|
||||
(remove 'tags-completion-at-point-function
|
||||
(default-value 'completion-at-point-functions)))
|
||||
(completion-at-point-functions (company--capf-workaround))
|
||||
(data (run-hook-wrapped 'completion-at-point-functions
|
||||
;; Ignore misbehaving functions.
|
||||
#'completion--capf-wrapper 'optimist)))
|
||||
(when (and (consp (cdr data)) (integer-or-marker-p (nth 1 data))) data)))
|
||||
|
||||
(declare-function python-shell-get-process "python")
|
||||
|
||||
(defun company--capf-workaround ()
|
||||
;; For http://debbugs.gnu.org/cgi/bugreport.cgi?bug=18067
|
||||
(if (or (not (listp completion-at-point-functions))
|
||||
(not (memq 'python-completion-complete-at-point completion-at-point-functions))
|
||||
(python-shell-get-process))
|
||||
completion-at-point-functions
|
||||
(remq 'python-completion-complete-at-point completion-at-point-functions)))
|
||||
|
||||
(defun company-capf (command &optional arg &rest _args)
|
||||
"`company-mode' backend using `completion-at-point-functions'."
|
||||
(interactive (list 'interactive))
|
||||
(pcase command
|
||||
(`interactive (company-begin-backend 'company-capf))
|
||||
(`prefix
|
||||
(let ((res (company--capf-data)))
|
||||
(when res
|
||||
(let ((length (plist-get (nthcdr 4 res) :company-prefix-length))
|
||||
(prefix (buffer-substring-no-properties (nth 1 res) (point))))
|
||||
(cond
|
||||
((> (nth 2 res) (point)) 'stop)
|
||||
(length (cons prefix length))
|
||||
(t prefix))))))
|
||||
(`candidates
|
||||
(let ((res (company--capf-data)))
|
||||
(when res
|
||||
(let* ((table (nth 3 res))
|
||||
(pred (plist-get (nthcdr 4 res) :predicate))
|
||||
(meta (completion-metadata
|
||||
(buffer-substring (nth 1 res) (nth 2 res))
|
||||
table pred))
|
||||
(sortfun (cdr (assq 'display-sort-function meta)))
|
||||
(candidates (completion-all-completions arg table pred (length arg)))
|
||||
(last (last candidates))
|
||||
(base-size (and (numberp (cdr last)) (cdr last))))
|
||||
(when base-size
|
||||
(setcdr last nil))
|
||||
(when sortfun
|
||||
(setq candidates (funcall sortfun candidates)))
|
||||
(if (not (zerop (or base-size 0)))
|
||||
(let ((before (substring arg 0 base-size)))
|
||||
(mapcar (lambda (candidate)
|
||||
(concat before candidate))
|
||||
candidates))
|
||||
candidates)))))
|
||||
(`sorted
|
||||
(let ((res (company--capf-data)))
|
||||
(when res
|
||||
(let ((meta (completion-metadata
|
||||
(buffer-substring (nth 1 res) (nth 2 res))
|
||||
(nth 3 res) (plist-get (nthcdr 4 res) :predicate))))
|
||||
(cdr (assq 'display-sort-function meta))))))
|
||||
(`match
|
||||
;; Can't just use 0 when base-size (see above) is non-zero.
|
||||
(let ((start (if (get-text-property 0 'face arg)
|
||||
0
|
||||
(next-single-property-change 0 'face arg))))
|
||||
(when start
|
||||
;; completions-common-part comes first, but we can't just look for this
|
||||
;; value because it can be in a list.
|
||||
(or
|
||||
(let ((value (get-text-property start 'face arg)))
|
||||
(text-property-not-all start (length arg)
|
||||
'face value arg))
|
||||
(length arg)))))
|
||||
(`duplicates t)
|
||||
(`no-cache t) ;Not much can be done here, as long as we handle
|
||||
;non-prefix matches.
|
||||
(`meta
|
||||
(let ((f (plist-get (nthcdr 4 (company--capf-data)) :company-docsig)))
|
||||
(when f (funcall f arg))))
|
||||
(`doc-buffer
|
||||
(let ((f (plist-get (nthcdr 4 (company--capf-data)) :company-doc-buffer)))
|
||||
(when f (funcall f arg))))
|
||||
(`location
|
||||
(let ((f (plist-get (nthcdr 4 (company--capf-data)) :company-location)))
|
||||
(when f (funcall f arg))))
|
||||
(`annotation
|
||||
(save-excursion
|
||||
;; FIXME: `company-begin' sets `company-point' after calling
|
||||
;; `company--begin-new'. We shouldn't rely on `company-point' here,
|
||||
;; better to cache the capf-data value instead. However: we can't just
|
||||
;; save the last capf-data value in `prefix', because that command can
|
||||
;; get called more often than `candidates', and at any point in the
|
||||
;; buffer (https://github.com/company-mode/company-mode/issues/153).
|
||||
;; We could try propertizing the returned prefix string, but it's not
|
||||
;; passed to `annotation', and `company-prefix' is set only after
|
||||
;; `company--strip-duplicates' is called.
|
||||
(when company-point
|
||||
(goto-char company-point))
|
||||
(let ((f (plist-get (nthcdr 4 (company--capf-data)) :annotation-function)))
|
||||
(when f (funcall f arg)))))
|
||||
(`require-match
|
||||
(plist-get (nthcdr 4 (company--capf-data)) :company-require-match))
|
||||
(`init nil) ;Don't bother: plenty of other ways to initialize the code.
|
||||
(`post-completion
|
||||
(let* ((res (company--capf-data))
|
||||
(exit-function (plist-get (nthcdr 4 res) :exit-function))
|
||||
(table (nth 3 res))
|
||||
(pred (plist-get (nthcdr 4 res) :predicate)))
|
||||
(if exit-function
|
||||
;; Follow the example of `completion--done'.
|
||||
(funcall exit-function arg
|
||||
(if (eq (try-completion arg table pred) t)
|
||||
'finished 'sole)))))
|
||||
))
|
||||
|
||||
(provide 'company-capf)
|
||||
|
||||
;;; company-capf.el ends here
|
Binary file not shown.
|
@ -0,0 +1,333 @@
|
|||
;;; company-clang.el --- company-mode completion backend for Clang -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2009, 2011, 2013-2016 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'company-template)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defgroup company-clang nil
|
||||
"Completion backend for Clang."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-clang-executable
|
||||
(executable-find "clang")
|
||||
"Location of clang executable."
|
||||
:type 'file)
|
||||
|
||||
(defcustom company-clang-begin-after-member-access t
|
||||
"When non-nil, automatic completion will start whenever the current
|
||||
symbol is preceded by \".\", \"->\" or \"::\", ignoring
|
||||
`company-minimum-prefix-length'.
|
||||
|
||||
If `company-begin-commands' is a list, it should include `c-electric-lt-gt'
|
||||
and `c-electric-colon', for automatic completion right after \">\" and
|
||||
\":\".")
|
||||
|
||||
(defcustom company-clang-arguments nil
|
||||
"Additional arguments to pass to clang when completing.
|
||||
Prefix files (-include ...) can be selected with `company-clang-set-prefix'
|
||||
or automatically through a custom `company-clang-prefix-guesser'."
|
||||
:type '(repeat (string :tag "Argument")))
|
||||
|
||||
(defcustom company-clang-prefix-guesser 'company-clang-guess-prefix
|
||||
"A function to determine the prefix file for the current buffer."
|
||||
:type '(function :tag "Guesser function" nil))
|
||||
|
||||
(defvar company-clang-modes '(c-mode c++-mode objc-mode)
|
||||
"Major modes which clang may complete.")
|
||||
|
||||
(defcustom company-clang-insert-arguments t
|
||||
"When non-nil, insert function arguments as a template after completion."
|
||||
:type 'boolean
|
||||
:package-version '(company . "0.8.0"))
|
||||
|
||||
;; prefix ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defvar company-clang--prefix nil)
|
||||
|
||||
(defsubst company-clang--guess-pch-file (file)
|
||||
(let ((dir (directory-file-name (file-name-directory file))))
|
||||
(when (equal (file-name-nondirectory dir) "Classes")
|
||||
(setq dir (file-name-directory dir)))
|
||||
(car (directory-files dir t "\\([^.]h\\|[^h]\\).pch\\'" t))))
|
||||
|
||||
(defsubst company-clang--file-substring (file beg end)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents-literally file nil beg end)
|
||||
(buffer-string)))
|
||||
|
||||
(defun company-clang-guess-prefix ()
|
||||
"Try to guess the prefix file for the current buffer."
|
||||
;; Prefixes seem to be called .pch. Pre-compiled headers do, too.
|
||||
;; So we look at the magic number to rule them out.
|
||||
(let* ((file (company-clang--guess-pch-file buffer-file-name))
|
||||
(magic-number (and file (company-clang--file-substring file 0 4))))
|
||||
(unless (member magic-number '("CPCH" "gpch"))
|
||||
file)))
|
||||
|
||||
(defun company-clang-set-prefix (&optional prefix)
|
||||
"Use PREFIX as a prefix (-include ...) file for clang completion."
|
||||
(interactive (let ((def (funcall company-clang-prefix-guesser)))
|
||||
(unless (stringp def)
|
||||
(setq def default-directory))
|
||||
(list (read-file-name "Prefix file: "
|
||||
(when def (file-name-directory def))
|
||||
def t (when def (file-name-nondirectory def))))))
|
||||
;; TODO: pre-compile?
|
||||
(setq company-clang--prefix (and (stringp prefix)
|
||||
(file-regular-p prefix)
|
||||
prefix)))
|
||||
|
||||
;; Clean-up on exit.
|
||||
(add-hook 'kill-emacs-hook 'company-clang-set-prefix)
|
||||
|
||||
;; parsing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; TODO: Handle Pattern (syntactic hints would be neat).
|
||||
;; Do we ever see OVERLOAD (or OVERRIDE)?
|
||||
(defconst company-clang--completion-pattern
|
||||
"^COMPLETION: \\_<\\(%s[a-zA-Z0-9_:]*\\)\\(?: : \\(.*\\)$\\)?$")
|
||||
|
||||
(defconst company-clang--error-buffer-name "*clang-error*")
|
||||
|
||||
(defun company-clang--lang-option ()
|
||||
(if (eq major-mode 'objc-mode)
|
||||
(if (string= "m" (file-name-extension buffer-file-name))
|
||||
"objective-c" "objective-c++")
|
||||
(substring (symbol-name major-mode) 0 -5)))
|
||||
|
||||
(defun company-clang--parse-output (prefix _objc)
|
||||
(goto-char (point-min))
|
||||
(let ((pattern (format company-clang--completion-pattern
|
||||
(regexp-quote prefix)))
|
||||
(case-fold-search nil)
|
||||
lines match)
|
||||
(while (re-search-forward pattern nil t)
|
||||
(setq match (match-string-no-properties 1))
|
||||
(unless (equal match "Pattern")
|
||||
(save-match-data
|
||||
(when (string-match ":" match)
|
||||
(setq match (substring match 0 (match-beginning 0)))))
|
||||
(let ((meta (match-string-no-properties 2)))
|
||||
(when (and meta (not (string= match meta)))
|
||||
(put-text-property 0 1 'meta
|
||||
(company-clang--strip-formatting meta)
|
||||
match)))
|
||||
(push match lines)))
|
||||
lines))
|
||||
|
||||
(defun company-clang--meta (candidate)
|
||||
(get-text-property 0 'meta candidate))
|
||||
|
||||
(defun company-clang--annotation (candidate)
|
||||
(let ((ann (company-clang--annotation-1 candidate)))
|
||||
(if (not (and ann (string-prefix-p "(*)" ann)))
|
||||
ann
|
||||
(with-temp-buffer
|
||||
(insert ann)
|
||||
(search-backward ")")
|
||||
(let ((pt (1+ (point))))
|
||||
(re-search-forward ".\\_>" nil t)
|
||||
(delete-region pt (point)))
|
||||
(buffer-string)))))
|
||||
|
||||
(defun company-clang--annotation-1 (candidate)
|
||||
(let ((meta (company-clang--meta candidate)))
|
||||
(cond
|
||||
((null meta) nil)
|
||||
((string-match "[^:]:[^:]" meta)
|
||||
(substring meta (1+ (match-beginning 0))))
|
||||
((string-match "(anonymous)" meta) nil)
|
||||
((string-match "\\((.*)[ a-z]*\\'\\)" meta)
|
||||
(let ((paren (match-beginning 1)))
|
||||
(if (not (eq (aref meta (1- paren)) ?>))
|
||||
(match-string 1 meta)
|
||||
(with-temp-buffer
|
||||
(insert meta)
|
||||
(goto-char paren)
|
||||
(substring meta (1- (search-backward "<"))))))))))
|
||||
|
||||
(defun company-clang--strip-formatting (text)
|
||||
(replace-regexp-in-string
|
||||
"#]" " "
|
||||
(replace-regexp-in-string "[<{[]#\\|#[>}]" "" text t)
|
||||
t))
|
||||
|
||||
(defun company-clang--handle-error (res args)
|
||||
(goto-char (point-min))
|
||||
(let* ((buf (get-buffer-create company-clang--error-buffer-name))
|
||||
(cmd (concat company-clang-executable " " (mapconcat 'identity args " ")))
|
||||
(pattern (format company-clang--completion-pattern ""))
|
||||
(err (if (re-search-forward pattern nil t)
|
||||
(buffer-substring-no-properties (point-min)
|
||||
(1- (match-beginning 0)))
|
||||
;; Warn the user more aggressively if no match was found.
|
||||
(message "clang failed with error %d:\n%s" res cmd)
|
||||
(buffer-string))))
|
||||
|
||||
(with-current-buffer buf
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer)
|
||||
(insert (current-time-string)
|
||||
(format "\nclang failed with error %d:\n" res)
|
||||
cmd "\n\n")
|
||||
(insert err)
|
||||
(setq buffer-read-only t)
|
||||
(goto-char (point-min))))))
|
||||
|
||||
(defun company-clang--start-process (prefix callback &rest args)
|
||||
(let ((objc (derived-mode-p 'objc-mode))
|
||||
(buf (get-buffer-create "*clang-output*"))
|
||||
;; Looks unnecessary in Emacs 25.1 and later.
|
||||
(process-adaptive-read-buffering nil))
|
||||
(if (get-buffer-process buf)
|
||||
(funcall callback nil)
|
||||
(with-current-buffer buf
|
||||
(erase-buffer)
|
||||
(setq buffer-undo-list t))
|
||||
(let* ((process-connection-type nil)
|
||||
(process (apply #'start-file-process "company-clang" buf
|
||||
company-clang-executable args)))
|
||||
(set-process-sentinel
|
||||
process
|
||||
(lambda (proc status)
|
||||
(unless (string-match-p "hangup" status)
|
||||
(funcall
|
||||
callback
|
||||
(let ((res (process-exit-status proc)))
|
||||
(with-current-buffer buf
|
||||
(unless (eq 0 res)
|
||||
(company-clang--handle-error res args))
|
||||
;; Still try to get any useful input.
|
||||
(company-clang--parse-output prefix objc)))))))
|
||||
(unless (company-clang--auto-save-p)
|
||||
(send-region process (point-min) (point-max))
|
||||
(send-string process "\n")
|
||||
(process-send-eof process))))))
|
||||
|
||||
(defsubst company-clang--build-location (pos)
|
||||
(save-excursion
|
||||
(goto-char pos)
|
||||
(format "%s:%d:%d"
|
||||
(if (company-clang--auto-save-p) buffer-file-name "-")
|
||||
(line-number-at-pos)
|
||||
(1+ (length
|
||||
(encode-coding-region
|
||||
(line-beginning-position)
|
||||
(point)
|
||||
'utf-8
|
||||
t))))))
|
||||
|
||||
(defsubst company-clang--build-complete-args (pos)
|
||||
(append '("-fsyntax-only" "-Xclang" "-code-completion-macros")
|
||||
(unless (company-clang--auto-save-p)
|
||||
(list "-x" (company-clang--lang-option)))
|
||||
company-clang-arguments
|
||||
(when (stringp company-clang--prefix)
|
||||
(list "-include" (expand-file-name company-clang--prefix)))
|
||||
(list "-Xclang" (format "-code-completion-at=%s"
|
||||
(company-clang--build-location pos)))
|
||||
(list (if (company-clang--auto-save-p) buffer-file-name "-"))))
|
||||
|
||||
(defun company-clang--candidates (prefix callback)
|
||||
(and (company-clang--auto-save-p)
|
||||
(buffer-modified-p)
|
||||
(basic-save-buffer))
|
||||
(when (null company-clang--prefix)
|
||||
(company-clang-set-prefix (or (funcall company-clang-prefix-guesser)
|
||||
'none)))
|
||||
(apply 'company-clang--start-process
|
||||
prefix
|
||||
callback
|
||||
(company-clang--build-complete-args (point))))
|
||||
|
||||
(defun company-clang--prefix ()
|
||||
(if company-clang-begin-after-member-access
|
||||
(company-grab-symbol-cons "\\.\\|->\\|::" 2)
|
||||
(company-grab-symbol)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defconst company-clang-required-version 1.1)
|
||||
|
||||
(defvar company-clang--version nil)
|
||||
|
||||
(defun company-clang--auto-save-p ()
|
||||
(< company-clang--version 2.9))
|
||||
|
||||
(defsubst company-clang-version ()
|
||||
"Return the version of `company-clang-executable'."
|
||||
(with-temp-buffer
|
||||
(call-process company-clang-executable nil t nil "--version")
|
||||
(goto-char (point-min))
|
||||
(if (re-search-forward "clang\\(?: version \\|-\\)\\([0-9.]+\\)" nil t)
|
||||
(let ((ver (string-to-number (match-string-no-properties 1))))
|
||||
(if (> ver 100)
|
||||
(/ ver 100)
|
||||
ver))
|
||||
0)))
|
||||
|
||||
(defun company-clang (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend for Clang.
|
||||
Clang is a parser for C and ObjC. Clang version 1.1 or newer is required.
|
||||
|
||||
Additional command line arguments can be specified in
|
||||
`company-clang-arguments'. Prefix files (-include ...) can be selected
|
||||
with `company-clang-set-prefix' or automatically through a custom
|
||||
`company-clang-prefix-guesser'.
|
||||
|
||||
With Clang versions before 2.9, we have to save the buffer before
|
||||
performing completion. With Clang 2.9 and later, buffer contents are
|
||||
passed via standard input."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-clang))
|
||||
(init (when (memq major-mode company-clang-modes)
|
||||
(unless company-clang-executable
|
||||
(error "Company found no clang executable"))
|
||||
(setq company-clang--version (company-clang-version))
|
||||
(when (< company-clang--version company-clang-required-version)
|
||||
(error "Company requires clang version 1.1"))))
|
||||
(prefix (and (memq major-mode company-clang-modes)
|
||||
buffer-file-name
|
||||
company-clang-executable
|
||||
(not (company-in-string-or-comment))
|
||||
(or (company-clang--prefix) 'stop)))
|
||||
(candidates (cons :async
|
||||
(lambda (cb) (company-clang--candidates arg cb))))
|
||||
(meta (company-clang--meta arg))
|
||||
(annotation (company-clang--annotation arg))
|
||||
(post-completion (let ((anno (company-clang--annotation arg)))
|
||||
(when (and company-clang-insert-arguments anno)
|
||||
(insert anno)
|
||||
(if (string-match "\\`:[^:]" anno)
|
||||
(company-template-objc-templatify anno)
|
||||
(company-template-c-like-templatify
|
||||
(concat arg anno))))))))
|
||||
|
||||
(provide 'company-clang)
|
||||
;;; company-clang.el ends here
|
Binary file not shown.
|
@ -0,0 +1,198 @@
|
|||
;;; company-cmake.el --- company-mode completion backend for CMake
|
||||
|
||||
;; Copyright (C) 2013-2014 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Chen Bin <chenbin DOT sh AT gmail>
|
||||
;; Version: 0.2
|
||||
|
||||
;; 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:
|
||||
;;
|
||||
;; company-cmake offers completions for module names, variable names and
|
||||
;; commands used by CMake. And their descriptions.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defgroup company-cmake nil
|
||||
"Completion backend for CMake."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-cmake-executable
|
||||
(executable-find "cmake")
|
||||
"Location of cmake executable."
|
||||
:type 'file)
|
||||
|
||||
(defvar company-cmake-executable-arguments
|
||||
'("--help-command-list"
|
||||
"--help-module-list"
|
||||
"--help-variable-list")
|
||||
"The arguments we pass to cmake, separately.
|
||||
They affect which types of symbols we get completion candidates for.")
|
||||
|
||||
(defvar company-cmake--completion-pattern
|
||||
"^\\(%s[a-zA-Z0-9_<>]%s\\)$"
|
||||
"Regexp to match the candidates.")
|
||||
|
||||
(defvar company-cmake-modes '(cmake-mode)
|
||||
"Major modes in which cmake may complete.")
|
||||
|
||||
(defvar company-cmake--candidates-cache nil
|
||||
"Cache for the raw candidates.")
|
||||
|
||||
(defvar company-cmake--meta-command-cache nil
|
||||
"Cache for command arguments to retrieve descriptions for the candidates.")
|
||||
|
||||
(defun company-cmake--replace-tags (rlt)
|
||||
(setq rlt (replace-regexp-in-string
|
||||
"\\(.*?\\(IS_GNU\\)?\\)<LANG>\\(.*\\)"
|
||||
(lambda (_match)
|
||||
(mapconcat 'identity
|
||||
(if (match-beginning 2)
|
||||
'("\\1CXX\\3" "\\1C\\3" "\\1G77\\3")
|
||||
'("\\1CXX\\3" "\\1C\\3" "\\1Fortran\\3"))
|
||||
"\n"))
|
||||
rlt t))
|
||||
(setq rlt (replace-regexp-in-string
|
||||
"\\(.*\\)<CONFIG>\\(.*\\)"
|
||||
(mapconcat 'identity '("\\1DEBUG\\2" "\\1RELEASE\\2"
|
||||
"\\1RELWITHDEBINFO\\2" "\\1MINSIZEREL\\2")
|
||||
"\n")
|
||||
rlt))
|
||||
rlt)
|
||||
|
||||
(defun company-cmake--fill-candidates-cache (arg)
|
||||
"Fill candidates cache if needed."
|
||||
(let (rlt)
|
||||
(unless company-cmake--candidates-cache
|
||||
(setq company-cmake--candidates-cache (make-hash-table :test 'equal)))
|
||||
|
||||
;; If hash is empty, fill it.
|
||||
(unless (gethash arg company-cmake--candidates-cache)
|
||||
(with-temp-buffer
|
||||
(let ((res (call-process company-cmake-executable nil t nil arg)))
|
||||
(unless (zerop res)
|
||||
(message "cmake executable exited with error=%d" res)))
|
||||
(setq rlt (buffer-string)))
|
||||
(setq rlt (company-cmake--replace-tags rlt))
|
||||
(puthash arg rlt company-cmake--candidates-cache))
|
||||
))
|
||||
|
||||
(defun company-cmake--parse (prefix content cmd)
|
||||
(let ((start 0)
|
||||
(pattern (format company-cmake--completion-pattern
|
||||
(regexp-quote prefix)
|
||||
(if (zerop (length prefix)) "+" "*")))
|
||||
(lines (split-string content "\n"))
|
||||
match
|
||||
rlt)
|
||||
(dolist (line lines)
|
||||
(when (string-match pattern line)
|
||||
(let ((match (match-string 1 line)))
|
||||
(when match
|
||||
(puthash match cmd company-cmake--meta-command-cache)
|
||||
(push match rlt)))))
|
||||
rlt))
|
||||
|
||||
(defun company-cmake--candidates (prefix)
|
||||
(let (results
|
||||
cmd-opts
|
||||
str)
|
||||
|
||||
(unless company-cmake--meta-command-cache
|
||||
(setq company-cmake--meta-command-cache (make-hash-table :test 'equal)))
|
||||
|
||||
(dolist (arg company-cmake-executable-arguments)
|
||||
(company-cmake--fill-candidates-cache arg)
|
||||
(setq cmd-opts (replace-regexp-in-string "-list$" "" arg) )
|
||||
|
||||
(setq str (gethash arg company-cmake--candidates-cache))
|
||||
(when str
|
||||
(setq results (nconc results
|
||||
(company-cmake--parse prefix str cmd-opts)))))
|
||||
results))
|
||||
|
||||
(defun company-cmake--unexpand-candidate (candidate)
|
||||
(cond
|
||||
((string-match "^CMAKE_\\(C\\|CXX\\|Fortran\\)\\(_.*\\)$" candidate)
|
||||
(setq candidate (concat "CMAKE_<LANG>" (match-string 2 candidate))))
|
||||
|
||||
;; C flags
|
||||
((string-match "^\\(.*_\\)IS_GNU\\(C\\|CXX\\|G77\\)$" candidate)
|
||||
(setq candidate (concat (match-string 1 candidate) "IS_GNU<LANG>")))
|
||||
|
||||
;; C flags
|
||||
((string-match "^\\(.*_\\)OVERRIDE_\\(C\\|CXX\\|Fortran\\)$" candidate)
|
||||
(setq candidate (concat (match-string 1 candidate) "OVERRIDE_<LANG>")))
|
||||
|
||||
((string-match "^\\(.*\\)\\(_DEBUG\\|_RELEASE\\|_RELWITHDEBINFO\\|_MINSIZEREL\\)\\(.*\\)$" candidate)
|
||||
(setq candidate (concat (match-string 1 candidate)
|
||||
"_<CONFIG>"
|
||||
(match-string 3 candidate)))))
|
||||
candidate)
|
||||
|
||||
(defun company-cmake--meta (candidate)
|
||||
(let ((cmd-opts (gethash candidate company-cmake--meta-command-cache))
|
||||
result)
|
||||
(setq candidate (company-cmake--unexpand-candidate candidate))
|
||||
|
||||
;; Don't cache the documentation of every candidate (command)
|
||||
;; Cache in this case will cost too much memory.
|
||||
(with-temp-buffer
|
||||
(call-process company-cmake-executable nil t nil cmd-opts candidate)
|
||||
;; Go to the third line, trim it and return the result.
|
||||
;; Tested with cmake 2.8.9.
|
||||
(goto-char (point-min))
|
||||
(forward-line 2)
|
||||
(setq result (buffer-substring-no-properties (line-beginning-position)
|
||||
(line-end-position)))
|
||||
(setq result (replace-regexp-in-string "^[ \t\n\r]+" "" result))
|
||||
result)))
|
||||
|
||||
(defun company-cmake--doc-buffer (candidate)
|
||||
(let ((cmd-opts (gethash candidate company-cmake--meta-command-cache)))
|
||||
|
||||
(setq candidate (company-cmake--unexpand-candidate candidate))
|
||||
(with-temp-buffer
|
||||
(call-process company-cmake-executable nil t nil cmd-opts candidate)
|
||||
;; Go to the third line, trim it and return the doc buffer.
|
||||
;; Tested with cmake 2.8.9.
|
||||
(goto-char (point-min))
|
||||
(forward-line 2)
|
||||
(company-doc-buffer
|
||||
(buffer-substring-no-properties (line-beginning-position)
|
||||
(point-max))))))
|
||||
|
||||
(defun company-cmake (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend for CMake.
|
||||
CMake is a cross-platform, open-source make system."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-cmake))
|
||||
(init (when (memq major-mode company-cmake-modes)
|
||||
(unless company-cmake-executable
|
||||
(error "Company found no cmake executable"))))
|
||||
(prefix (and (memq major-mode company-cmake-modes)
|
||||
(not (company-in-string-or-comment))
|
||||
(company-grab-symbol)))
|
||||
(candidates (company-cmake--candidates arg))
|
||||
(meta (company-cmake--meta arg))
|
||||
(doc-buffer (company-cmake--doc-buffer arg))
|
||||
))
|
||||
|
||||
(provide 'company-cmake)
|
||||
;;; company-cmake.el ends here
|
Binary file not shown.
|
@ -0,0 +1,442 @@
|
|||
;;; company-css.el --- company-mode completion backend for css-mode -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2009, 2011, 2014 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
|
||||
(declare-function web-mode-language-at-pos "web-mode" (&optional pos))
|
||||
|
||||
(defconst company-css-property-alist
|
||||
;; see http://www.w3.org/TR/CSS21/propidx.html
|
||||
'(("azimuth" angle "left-side" "far-left" "left" "center-left" "center"
|
||||
"center-right" "right" "far-right" "right-side" "behind" "leftwards"
|
||||
"rightwards")
|
||||
("background" background-color background-image background-repeat
|
||||
background-attachment background-position
|
||||
background-clip background-origin background-size)
|
||||
("background-attachment" "scroll" "fixed")
|
||||
("background-color" color "transparent")
|
||||
("background-image" uri "none")
|
||||
("background-position" percentage length "left" "center" "right" percentage
|
||||
length "top" "center" "bottom" "left" "center" "right" "top" "center"
|
||||
"bottom")
|
||||
("background-repeat" "repeat" "repeat-x" "repeat-y" "no-repeat")
|
||||
("border" border-width border-style border-color)
|
||||
("border-bottom" border)
|
||||
("border-bottom-color" border-color)
|
||||
("border-bottom-style" border-style)
|
||||
("border-bottom-width" border-width)
|
||||
("border-collapse" "collapse" "separate")
|
||||
("border-color" color "transparent")
|
||||
("border-left" border)
|
||||
("border-left-color" border-color)
|
||||
("border-left-style" border-style)
|
||||
("border-left-width" border-width)
|
||||
("border-right" border)
|
||||
("border-right-color" border-color)
|
||||
("border-right-style" border-style)
|
||||
("border-right-width" border-width)
|
||||
("border-spacing" length length)
|
||||
("border-style" border-style)
|
||||
("border-top" border)
|
||||
("border-top-color" border-color)
|
||||
("border-top-style" border-style)
|
||||
("border-top-width" border-width)
|
||||
("border-width" border-width)
|
||||
("bottom" length percentage "auto")
|
||||
("caption-side" "top" "bottom")
|
||||
("clear" "none" "left" "right" "both")
|
||||
("clip" shape "auto")
|
||||
("color" color)
|
||||
("content" "normal" "none" string uri counter "attr()" "open-quote"
|
||||
"close-quote" "no-open-quote" "no-close-quote")
|
||||
("counter-increment" identifier integer "none")
|
||||
("counter-reset" identifier integer "none")
|
||||
("cue" cue-before cue-after)
|
||||
("cue-after" uri "none")
|
||||
("cue-before" uri "none")
|
||||
("cursor" uri "*" "auto" "crosshair" "default" "pointer" "move" "e-resize"
|
||||
"ne-resize" "nw-resize" "n-resize" "se-resize" "sw-resize" "s-resize"
|
||||
"w-resize" "text" "wait" "help" "progress")
|
||||
("direction" "ltr" "rtl")
|
||||
("display" "inline" "block" "list-item" "run-in" "inline-block" "table"
|
||||
"inline-table" "table-row-group" "table-header-group" "table-footer-group"
|
||||
"table-row" "table-column-group" "table-column" "table-cell"
|
||||
"table-caption" "none")
|
||||
("elevation" angle "below" "level" "above" "higher" "lower")
|
||||
("empty-cells" "show" "hide")
|
||||
("float" "left" "right" "none")
|
||||
("font" font-style font-weight font-size "/" line-height
|
||||
font-family "caption" "icon" "menu" "message-box" "small-caption"
|
||||
"status-bar" "normal" "small-caps"
|
||||
;; CSS3
|
||||
font-stretch)
|
||||
("font-family" family-name generic-family)
|
||||
("font-size" absolute-size relative-size length percentage)
|
||||
("font-style" "normal" "italic" "oblique")
|
||||
("font-weight" "normal" "bold" "bolder" "lighter" "100" "200" "300" "400"
|
||||
"500" "600" "700" "800" "900")
|
||||
("height" length percentage "auto")
|
||||
("left" length percentage "auto")
|
||||
("letter-spacing" "normal" length)
|
||||
("line-height" "normal" number length percentage)
|
||||
("list-style" list-style-type list-style-position list-style-image)
|
||||
("list-style-image" uri "none")
|
||||
("list-style-position" "inside" "outside")
|
||||
("list-style-type" "disc" "circle" "square" "decimal" "decimal-leading-zero"
|
||||
"lower-roman" "upper-roman" "lower-greek" "lower-latin" "upper-latin"
|
||||
"armenian" "georgian" "lower-alpha" "upper-alpha" "none")
|
||||
("margin" margin-width)
|
||||
("margin-bottom" margin-width)
|
||||
("margin-left" margin-width)
|
||||
("margin-right" margin-width)
|
||||
("margin-top" margin-width)
|
||||
("max-height" length percentage "none")
|
||||
("max-width" length percentage "none")
|
||||
("min-height" length percentage)
|
||||
("min-width" length percentage)
|
||||
("orphans" integer)
|
||||
("outline" outline-color outline-style outline-width)
|
||||
("outline-color" color "invert")
|
||||
("outline-style" border-style)
|
||||
("outline-width" border-width)
|
||||
("overflow" "visible" "hidden" "scroll" "auto"
|
||||
;; CSS3:
|
||||
"no-display" "no-content")
|
||||
("padding" padding-width)
|
||||
("padding-bottom" padding-width)
|
||||
("padding-left" padding-width)
|
||||
("padding-right" padding-width)
|
||||
("padding-top" padding-width)
|
||||
("page-break-after" "auto" "always" "avoid" "left" "right")
|
||||
("page-break-before" "auto" "always" "avoid" "left" "right")
|
||||
("page-break-inside" "avoid" "auto")
|
||||
("pause" time percentage)
|
||||
("pause-after" time percentage)
|
||||
("pause-before" time percentage)
|
||||
("pitch" frequency "x-low" "low" "medium" "high" "x-high")
|
||||
("pitch-range" number)
|
||||
("play-during" uri "mix" "repeat" "auto" "none")
|
||||
("position" "static" "relative" "absolute" "fixed")
|
||||
("quotes" string string "none")
|
||||
("richness" number)
|
||||
("right" length percentage "auto")
|
||||
("speak" "normal" "none" "spell-out")
|
||||
("speak-header" "once" "always")
|
||||
("speak-numeral" "digits" "continuous")
|
||||
("speak-punctuation" "code" "none")
|
||||
("speech-rate" number "x-slow" "slow" "medium" "fast" "x-fast" "faster"
|
||||
"slower")
|
||||
("stress" number)
|
||||
("table-layout" "auto" "fixed")
|
||||
("text-align" "left" "right" "center" "justify")
|
||||
("text-indent" length percentage)
|
||||
("text-transform" "capitalize" "uppercase" "lowercase" "none")
|
||||
("top" length percentage "auto")
|
||||
("unicode-bidi" "normal" "embed" "bidi-override")
|
||||
("vertical-align" "baseline" "sub" "super" "top" "text-top" "middle"
|
||||
"bottom" "text-bottom" percentage length)
|
||||
("visibility" "visible" "hidden" "collapse")
|
||||
("voice-family" specific-voice generic-voice "*" specific-voice
|
||||
generic-voice)
|
||||
("volume" number percentage "silent" "x-soft" "soft" "medium" "loud"
|
||||
"x-loud")
|
||||
("white-space" "normal" "pre" "nowrap" "pre-wrap" "pre-line")
|
||||
("widows" integer)
|
||||
("width" length percentage "auto")
|
||||
("word-spacing" "normal" length)
|
||||
("z-index" "auto" integer)
|
||||
;; CSS3
|
||||
("align-content" align-stretch "space-between" "space-around")
|
||||
("align-items" align-stretch "baseline")
|
||||
("align-self" align-items "auto")
|
||||
("animation" animation-name animation-duration animation-timing-function
|
||||
animation-delay animation-iteration-count animation-direction
|
||||
animation-fill-mode)
|
||||
("animation-delay" time)
|
||||
("animation-direction" "normal" "reverse" "alternate" "alternate-reverse")
|
||||
("animation-duration" time)
|
||||
("animation-fill-mode" "none" "forwards" "backwards" "both")
|
||||
("animation-iteration-count" integer "infinite")
|
||||
("animation-name" "none")
|
||||
("animation-play-state" "paused" "running")
|
||||
("animation-timing-function" transition-timing-function
|
||||
"step-start" "step-end" "steps(,)")
|
||||
("backface-visibility" "visible" "hidden")
|
||||
("background-clip" background-origin)
|
||||
("background-origin" "border-box" "padding-box" "content-box")
|
||||
("background-size" length percentage "auto" "cover" "contain")
|
||||
("border-image" border-image-outset border-image-repeat border-image-source
|
||||
border-image-slice border-image-width)
|
||||
("border-image-outset" length)
|
||||
("border-image-repeat" "stretch" "repeat" "round" "space")
|
||||
("border-image-source" uri "none")
|
||||
("border-image-slice" length)
|
||||
("border-image-width" length percentage)
|
||||
("border-radius" length)
|
||||
("border-top-left-radius" length)
|
||||
("border-top-right-radius" length)
|
||||
("border-bottom-left-radius" length)
|
||||
("border-bottom-right-radius" length)
|
||||
("box-decoration-break" "slice" "clone")
|
||||
("box-shadow" length color)
|
||||
("box-sizing" "content-box" "border-box")
|
||||
("break-after" "auto" "always" "avoid" "left" "right" "page" "column"
|
||||
"avoid-page" "avoid-column")
|
||||
("break-before" break-after)
|
||||
("break-inside" "avoid" "auto")
|
||||
("columns" column-width column-count)
|
||||
("column-count" integer)
|
||||
("column-fill" "auto" "balance")
|
||||
("column-gap" length "normal")
|
||||
("column-rule" column-rule-width column-rule-style column-rule-color)
|
||||
("column-rule-color" color)
|
||||
("column-rule-style" border-style)
|
||||
("column-rule-width" border-width)
|
||||
("column-span" "all" "none")
|
||||
("column-width" length "auto")
|
||||
("filter" url "blur()" "brightness()" "contrast()" "drop-shadow()"
|
||||
"grayscale()" "hue-rotate()" "invert()" "opacity()" "saturate()" "sepia()")
|
||||
("flex" flex-grow flex-shrink flex-basis)
|
||||
("flex-basis" percentage length "auto")
|
||||
("flex-direction" "row" "row-reverse" "column" "column-reverse")
|
||||
("flex-flow" flex-direction flex-wrap)
|
||||
("flex-grow" number)
|
||||
("flex-shrink" number)
|
||||
("flex-wrap" "nowrap" "wrap" "wrap-reverse")
|
||||
("font-feature-setting" normal string number)
|
||||
("font-kerning" "auto" "normal" "none")
|
||||
("font-language-override" "normal" string)
|
||||
("font-size-adjust" "none" number)
|
||||
("font-stretch" "normal" "ultra-condensed" "extra-condensed" "condensed"
|
||||
"semi-condensed" "semi-expanded" "expanded" "extra-expanded" "ultra-expanded")
|
||||
("font-synthesis" "none" "weight" "style")
|
||||
("font-variant" font-variant-alternates font-variant-caps
|
||||
font-variant-east-asian font-variant-ligatures font-variant-numeric
|
||||
font-variant-position)
|
||||
("font-variant-alternates" "normal" "historical-forms" "stylistic()"
|
||||
"styleset()" "character-variant()" "swash()" "ornaments()" "annotation()")
|
||||
("font-variant-caps" "normal" "small-caps" "all-small-caps" "petite-caps"
|
||||
"all-petite-caps" "unicase" "titling-caps")
|
||||
("font-variant-east-asian" "jis78" "jis83" "jis90" "jis04" "simplified"
|
||||
"traditional" "full-width" "proportional-width" "ruby")
|
||||
("font-variant-ligatures" "normal" "none" "common-ligatures"
|
||||
"no-common-ligatures" "discretionary-ligatures" "no-discretionary-ligatures"
|
||||
"historical-ligatures" "no-historical-ligatures" "contextual" "no-contextual")
|
||||
("font-variant-numeric" "normal" "ordinal" "slashed-zero"
|
||||
"lining-nums" "oldstyle-nums" "proportional-nums" "tabular-nums"
|
||||
"diagonal-fractions" "stacked-fractions")
|
||||
("font-variant-position" "normal" "sub" "super")
|
||||
("hyphens" "none" "manual" "auto")
|
||||
("justify-content" align-common "space-between" "space-around")
|
||||
("line-break" "auto" "loose" "normal" "strict")
|
||||
("marquee-direction" "forward" "reverse")
|
||||
("marquee-play-count" integer "infinite")
|
||||
("marquee-speed" "slow" "normal" "fast")
|
||||
("marquee-style" "scroll" "slide" "alternate")
|
||||
("opacity" number)
|
||||
("order" number)
|
||||
("outline-offset" length)
|
||||
("overflow-x" overflow)
|
||||
("overflow-y" overflow)
|
||||
("overflow-style" "auto" "marquee-line" "marquee-block")
|
||||
("overflow-wrap" "normal" "break-word")
|
||||
("perspective" "none" length)
|
||||
("perspective-origin" percentage length "left" "center" "right" "top" "bottom")
|
||||
("resize" "none" "both" "horizontal" "vertical")
|
||||
("tab-size" integer length)
|
||||
("text-align-last" "auto" "start" "end" "left" "right" "center" "justify")
|
||||
("text-decoration" text-decoration-color text-decoration-line text-decoration-style)
|
||||
("text-decoration-color" color)
|
||||
("text-decoration-line" "none" "underline" "overline" "line-through" "blink")
|
||||
("text-decoration-style" "solid" "double" "dotted" "dashed" "wavy")
|
||||
("text-overflow" "clip" "ellipsis")
|
||||
("text-shadow" color length)
|
||||
("text-underline-position" "auto" "under" "left" "right")
|
||||
("transform" "matrix(,,,,,)" "translate(,)" "translateX()" "translateY()"
|
||||
"scale()" "scaleX()" "scaleY()" "rotate()" "skewX()" "skewY()" "none")
|
||||
("transform-origin" perspective-origin)
|
||||
("transform-style" "flat" "preserve-3d")
|
||||
("transition" transition-property transition-duration
|
||||
transition-timing-function transition-delay)
|
||||
("transition-delay" time)
|
||||
("transition-duration" time)
|
||||
("transition-timing-function"
|
||||
"ease" "linear" "ease-in" "ease-out" "ease-in-out" "cubic-bezier(,,,)")
|
||||
("transition-property" "none" "all" identifier)
|
||||
("word-wrap" overflow-wrap)
|
||||
("word-break" "normal" "break-all" "keep-all"))
|
||||
"A list of CSS properties and their possible values.")
|
||||
|
||||
(defconst company-css-value-classes
|
||||
'((absolute-size "xx-small" "x-small" "small" "medium" "large" "x-large"
|
||||
"xx-large")
|
||||
(align-common "flex-start" "flex-end" "center")
|
||||
(align-stretch align-common "stretch")
|
||||
(border-style "none" "hidden" "dotted" "dashed" "solid" "double" "groove"
|
||||
"ridge" "inset" "outset")
|
||||
(border-width "thick" "medium" "thin")
|
||||
(color "aqua" "black" "blue" "fuchsia" "gray" "green" "lime" "maroon" "navy"
|
||||
"olive" "orange" "purple" "red" "silver" "teal" "white" "yellow")
|
||||
(counter "counter(,)")
|
||||
(family-name "Courier" "Helvetica" "Times")
|
||||
(generic-family "serif" "sans-serif" "cursive" "fantasy" "monospace")
|
||||
(generic-voice "male" "female" "child")
|
||||
(margin-width "auto") ;; length percentage
|
||||
(relative-size "larger" "smaller")
|
||||
(shape "rect(,,,)")
|
||||
(uri "url()"))
|
||||
"A list of CSS property value classes and their contents.")
|
||||
;; missing, because not completable
|
||||
;; <angle><frequency><identifier><integer><length><number><padding-width>
|
||||
;; <percentage><specific-voice><string><time><uri>
|
||||
|
||||
(defconst company-css-html-tags
|
||||
'("a" "abbr" "acronym" "address" "applet" "area" "b" "base" "basefont" "bdo"
|
||||
"big" "blockquote" "body" "br" "button" "caption" "center" "cite" "code"
|
||||
"col" "colgroup" "dd" "del" "dfn" "dir" "div" "dl" "dt" "em" "fieldset"
|
||||
"font" "form" "frame" "frameset" "h1" "h2" "h3" "h4" "h5" "h6" "head" "hr"
|
||||
"html" "i" "iframe" "img" "input" "ins" "isindex" "kbd" "label" "legend"
|
||||
"li" "link" "map" "menu" "meta" "noframes" "noscript" "object" "ol"
|
||||
"optgroup" "option" "p" "param" "pre" "q" "s" "samp" "script" "select"
|
||||
"small" "span" "strike" "strong" "style" "sub" "sup" "table" "tbody" "td"
|
||||
"textarea" "tfoot" "th" "thead" "title" "tr" "tt" "u" "ul" "var"
|
||||
;; HTML5
|
||||
"section" "article" "aside" "header" "footer" "nav" "figure" "figcaption"
|
||||
"time" "mark" "main")
|
||||
"A list of HTML tags for use in CSS completion.")
|
||||
|
||||
(defconst company-css-pseudo-classes
|
||||
'("active" "after" "before" "first" "first-child" "first-letter" "first-line"
|
||||
"focus" "hover" "lang" "left" "link" "right" "visited")
|
||||
"Identifiers for CSS pseudo-elements and pseudo-classes.")
|
||||
|
||||
(defconst company-css-property-cache (make-hash-table :size 115 :test 'equal))
|
||||
|
||||
(defun company-css-property-values (attribute)
|
||||
"Access the `company-css-property-alist' cached and flattened."
|
||||
(or (gethash attribute company-css-property-cache)
|
||||
(let (results)
|
||||
(dolist (value (cdr (assoc attribute company-css-property-alist)))
|
||||
(if (symbolp value)
|
||||
(dolist (child (or (cdr (assoc value company-css-value-classes))
|
||||
(company-css-property-values
|
||||
(symbol-name value))))
|
||||
(push child results))
|
||||
(push value results)))
|
||||
(setq results (sort results 'string<))
|
||||
(puthash attribute
|
||||
(if (fboundp 'delete-consecutive-dups)
|
||||
(delete-consecutive-dups results)
|
||||
(delete-dups results))
|
||||
company-css-property-cache)
|
||||
results)))
|
||||
|
||||
;;; bracket detection
|
||||
|
||||
(defconst company-css-braces-syntax-table
|
||||
(let ((table (make-syntax-table)))
|
||||
(setf (aref table ?{) '(4 . 125))
|
||||
(setf (aref table ?}) '(5 . 123))
|
||||
table)
|
||||
"A syntax table giving { and } paren syntax.")
|
||||
|
||||
(defun company-css-inside-braces-p ()
|
||||
"Return non-nil, if point is within matched { and }."
|
||||
(ignore-errors
|
||||
(with-syntax-table company-css-braces-syntax-table
|
||||
(let ((parse-sexp-ignore-comments t))
|
||||
(scan-lists (point) -1 1)))))
|
||||
|
||||
;;; tags
|
||||
(defconst company-css-tag-regexp
|
||||
(concat "\\(?:\\`\\|}\\)[[:space:]]*"
|
||||
;; multiple
|
||||
"\\(?:"
|
||||
;; previous tags:
|
||||
"\\(?:#\\|\\_<[[:alpha:]]\\)[[:alnum:]-#]*\\(?:\\[[^]]*\\]\\)?"
|
||||
;; space or selectors
|
||||
"\\(?:[[:space:]]+\\|[[:space:]]*[+,>][[:space:]]*\\)"
|
||||
"\\)*"
|
||||
"\\(\\(?:#\\|\\_<[[:alpha:]]\\)\\(?:[[:alnum:]-#]*\\_>\\)?\\_>\\|\\)"
|
||||
"\\=")
|
||||
"A regular expression matching CSS tags.")
|
||||
|
||||
;;; pseudo id
|
||||
(defconst company-css-pseudo-regexp
|
||||
(concat "\\(?:\\`\\|}\\)[[:space:]]*"
|
||||
;; multiple
|
||||
"\\(?:"
|
||||
;; previous tags:
|
||||
"\\(?:#\\|\\_<[[:alpha:]]\\)[[:alnum:]-#]*\\(?:\\[[^]]*\\]\\)?"
|
||||
;; space or delimiters
|
||||
"\\(?:[[:space:]]+\\|[[:space:]]*[+,>][[:space:]]*\\)"
|
||||
"\\)*"
|
||||
"\\(?:\\(?:\\#\\|\\_<[[:alpha:]]\\)[[:alnum:]-#]*\\):"
|
||||
"\\([[:alpha:]-]+\\_>\\|\\)\\_>\\=")
|
||||
"A regular expression matching CSS pseudo classes.")
|
||||
|
||||
;;; properties
|
||||
|
||||
(defun company-css-grab-property ()
|
||||
"Return the CSS property before point, if any.
|
||||
Returns \"\" if no property found, but feasible at this position."
|
||||
(when (company-css-inside-braces-p)
|
||||
(company-grab-symbol)))
|
||||
|
||||
;;; values
|
||||
(defconst company-css-property-value-regexp
|
||||
"\\_<\\([[:alpha:]-]+\\):\\(?:[^{};]*[[:space:]]+\\)?\\([^{};]*\\_>\\|\\)\\="
|
||||
"A regular expression matching CSS tags.")
|
||||
|
||||
;;;###autoload
|
||||
(defun company-css (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend for `css-mode'."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-css))
|
||||
(prefix (and (or (derived-mode-p 'css-mode)
|
||||
(and (derived-mode-p 'web-mode)
|
||||
(string= (web-mode-language-at-pos) "css")))
|
||||
(or (company-grab company-css-tag-regexp 1)
|
||||
(company-grab company-css-pseudo-regexp 1)
|
||||
(company-grab company-css-property-value-regexp 2)
|
||||
(company-css-grab-property))))
|
||||
(candidates
|
||||
(cond
|
||||
((company-grab company-css-tag-regexp 1)
|
||||
(all-completions arg company-css-html-tags))
|
||||
((company-grab company-css-pseudo-regexp 1)
|
||||
(all-completions arg company-css-pseudo-classes))
|
||||
((company-grab company-css-property-value-regexp 2)
|
||||
(all-completions arg
|
||||
(company-css-property-values
|
||||
(company-grab company-css-property-value-regexp 1))))
|
||||
((company-css-grab-property)
|
||||
(all-completions arg company-css-property-alist))))
|
||||
(sorted t)))
|
||||
|
||||
(provide 'company-css)
|
||||
;;; company-css.el ends here
|
Binary file not shown.
|
@ -0,0 +1,104 @@
|
|||
;;; company-dabbrev-code.el --- dabbrev-like company-mode backend for code -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2009, 2011, 2014 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'company-dabbrev)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defgroup company-dabbrev-code nil
|
||||
"dabbrev-like completion backend for code."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-dabbrev-code-modes
|
||||
'(prog-mode
|
||||
batch-file-mode csharp-mode css-mode erlang-mode haskell-mode jde-mode
|
||||
lua-mode python-mode)
|
||||
"Modes that use `company-dabbrev-code'.
|
||||
In all these modes (and their derivatives) `company-dabbrev-code' will
|
||||
complete only symbols, not text in comments or strings. In other modes
|
||||
`company-dabbrev-code' will pass control to other backends
|
||||
\(e.g. `company-dabbrev'\). Value t means complete in all modes."
|
||||
:type '(choice (repeat :tag "Some modes" (symbol :tag "Major mode"))
|
||||
(const :tag "All modes" t)))
|
||||
|
||||
(defcustom company-dabbrev-code-other-buffers t
|
||||
"Determines whether `company-dabbrev-code' should search other buffers.
|
||||
If `all', search all other buffers, except the ignored ones. If t, search
|
||||
buffers with the same major mode. If `code', search all buffers with major
|
||||
modes in `company-dabbrev-code-modes', or derived from one of them. See
|
||||
also `company-dabbrev-code-time-limit'."
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "Same major mode" t)
|
||||
(const :tag "Code major modes" code)
|
||||
(const :tag "All" all)))
|
||||
|
||||
(defcustom company-dabbrev-code-time-limit .1
|
||||
"Determines how long `company-dabbrev-code' should look for matches."
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(number :tag "Seconds")))
|
||||
|
||||
(defcustom company-dabbrev-code-everywhere nil
|
||||
"Non-nil to offer completions in comments and strings."
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom company-dabbrev-code-ignore-case nil
|
||||
"Non-nil to ignore case when collecting completion candidates."
|
||||
:type 'boolean)
|
||||
|
||||
(defun company-dabbrev-code--make-regexp (prefix)
|
||||
(concat "\\_<" (if (equal prefix "")
|
||||
"\\([a-zA-Z]\\|\\s_\\)"
|
||||
(regexp-quote prefix))
|
||||
"\\(\\sw\\|\\s_\\)*\\_>"))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-dabbrev-code (command &optional arg &rest ignored)
|
||||
"dabbrev-like `company-mode' backend for code.
|
||||
The backend looks for all symbols in the current buffer that aren't in
|
||||
comments or strings."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-dabbrev-code))
|
||||
(prefix (and (or (eq t company-dabbrev-code-modes)
|
||||
(apply #'derived-mode-p company-dabbrev-code-modes))
|
||||
(or company-dabbrev-code-everywhere
|
||||
(not (company-in-string-or-comment)))
|
||||
(or (company-grab-symbol) 'stop)))
|
||||
(candidates (let ((case-fold-search company-dabbrev-code-ignore-case))
|
||||
(company-dabbrev--search
|
||||
(company-dabbrev-code--make-regexp arg)
|
||||
company-dabbrev-code-time-limit
|
||||
(pcase company-dabbrev-code-other-buffers
|
||||
(`t (list major-mode))
|
||||
(`code company-dabbrev-code-modes)
|
||||
(`all `all))
|
||||
(not company-dabbrev-code-everywhere))))
|
||||
(ignore-case company-dabbrev-code-ignore-case)
|
||||
(duplicates t)))
|
||||
|
||||
(provide 'company-dabbrev-code)
|
||||
;;; company-dabbrev-code.el ends here
|
Binary file not shown.
|
@ -0,0 +1,198 @@
|
|||
;;; company-dabbrev.el --- dabbrev-like company-mode completion backend -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2009, 2011, 2014, 2015, 2016 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defgroup company-dabbrev nil
|
||||
"dabbrev-like completion backend."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-dabbrev-other-buffers 'all
|
||||
"Determines whether `company-dabbrev' should search other buffers.
|
||||
If `all', search all other buffers, except the ignored ones. If t, search
|
||||
buffers with the same major mode. See also `company-dabbrev-time-limit'."
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "Same major mode" t)
|
||||
(const :tag "All" all)))
|
||||
|
||||
(defcustom company-dabbrev-ignore-buffers "\\`[ *]"
|
||||
"Regexp matching the names of buffers to ignore.
|
||||
Or a function that returns non-nil for such buffers."
|
||||
:type '(choice (regexp :tag "Regexp")
|
||||
(function :tag "Predicate"))
|
||||
:package-version '(company . "0.9.0"))
|
||||
|
||||
(defcustom company-dabbrev-time-limit .1
|
||||
"Determines how many seconds `company-dabbrev' should look for matches."
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(number :tag "Seconds")))
|
||||
|
||||
(defcustom company-dabbrev-char-regexp "\\sw"
|
||||
"A regular expression matching the characters `company-dabbrev' looks for."
|
||||
:type 'regexp)
|
||||
|
||||
(defcustom company-dabbrev-ignore-case 'keep-prefix
|
||||
"Non-nil to ignore case when collecting completion candidates.
|
||||
When it's `keep-prefix', the text before point will remain unchanged after
|
||||
candidate is inserted, even some of its characters have different case.")
|
||||
|
||||
(defcustom company-dabbrev-downcase 'case-replace
|
||||
"Whether to downcase the returned candidates.
|
||||
|
||||
The value of nil means keep them as-is.
|
||||
`case-replace' means use the value of `case-replace'.
|
||||
Any other value means downcase.
|
||||
|
||||
If you set this value to nil, you may also want to set
|
||||
`company-dabbrev-ignore-case' to any value other than `keep-prefix'.")
|
||||
|
||||
(defcustom company-dabbrev-minimum-length 4
|
||||
"The minimum length for the completion candidate to be included.
|
||||
This variable affects both `company-dabbrev' and `company-dabbrev-code'."
|
||||
:type 'integer
|
||||
:package-version '(company . "0.8.3"))
|
||||
|
||||
(defcustom company-dabbrev-ignore-invisible nil
|
||||
"Non-nil to skip invisible text."
|
||||
:type 'boolean
|
||||
:package-version '(company . "0.9.0"))
|
||||
|
||||
(defmacro company-dabbrev--time-limit-while (test start limit freq &rest body)
|
||||
(declare (indent 3) (debug t))
|
||||
`(let ((company-time-limit-while-counter 0))
|
||||
(catch 'done
|
||||
(while ,test
|
||||
,@body
|
||||
(and ,limit
|
||||
(= (cl-incf company-time-limit-while-counter) ,freq)
|
||||
(setq company-time-limit-while-counter 0)
|
||||
(> (float-time (time-since ,start)) ,limit)
|
||||
(throw 'done 'company-time-out))))))
|
||||
|
||||
(defun company-dabbrev--make-regexp ()
|
||||
(concat "\\(?:" company-dabbrev-char-regexp "\\)+"))
|
||||
|
||||
(defun company-dabbrev--search-buffer (regexp pos symbols start limit
|
||||
ignore-comments)
|
||||
(save-excursion
|
||||
(cl-labels ((maybe-collect-match
|
||||
()
|
||||
(let ((match (match-string-no-properties 0)))
|
||||
(when (and (>= (length match) company-dabbrev-minimum-length)
|
||||
(not (and company-dabbrev-ignore-invisible
|
||||
(invisible-p (match-beginning 0)))))
|
||||
(push match symbols)))))
|
||||
(goto-char (if pos (1- pos) (point-min)))
|
||||
;; Search before pos.
|
||||
(let ((tmp-end (point)))
|
||||
(company-dabbrev--time-limit-while (and (not (input-pending-p))
|
||||
(> tmp-end (point-min)))
|
||||
start limit 1
|
||||
(ignore-errors
|
||||
(forward-char -10000))
|
||||
(forward-line 0)
|
||||
(save-excursion
|
||||
;; Before, we used backward search, but it matches non-greedily, and
|
||||
;; that forced us to use the "beginning/end of word" anchors in
|
||||
;; `company-dabbrev--make-regexp'. It's also about 2x slower.
|
||||
(while (and (not (input-pending-p))
|
||||
(re-search-forward regexp tmp-end t))
|
||||
(if (and ignore-comments (save-match-data (company-in-string-or-comment)))
|
||||
(re-search-forward "\\s>\\|\\s!\\|\\s\"" tmp-end t)
|
||||
(maybe-collect-match))))
|
||||
(setq tmp-end (point))))
|
||||
(goto-char (or pos (point-min)))
|
||||
;; Search after pos.
|
||||
(company-dabbrev--time-limit-while (and (not (input-pending-p))
|
||||
(re-search-forward regexp nil t))
|
||||
start limit 25
|
||||
(if (and ignore-comments (save-match-data (company-in-string-or-comment)))
|
||||
(re-search-forward "\\s>\\|\\s!\\|\\s\"" nil t)
|
||||
(maybe-collect-match)))
|
||||
symbols)))
|
||||
|
||||
(defun company-dabbrev--search (regexp &optional limit other-buffer-modes
|
||||
ignore-comments)
|
||||
(let* ((start (current-time))
|
||||
(symbols (company-dabbrev--search-buffer regexp (point) nil start limit
|
||||
ignore-comments)))
|
||||
(when other-buffer-modes
|
||||
(cl-dolist (buffer (delq (current-buffer) (buffer-list)))
|
||||
(unless (if (stringp company-dabbrev-ignore-buffers)
|
||||
(string-match-p company-dabbrev-ignore-buffers
|
||||
(buffer-name buffer))
|
||||
(funcall company-dabbrev-ignore-buffers buffer))
|
||||
(with-current-buffer buffer
|
||||
(when (or (eq other-buffer-modes 'all)
|
||||
(apply #'derived-mode-p other-buffer-modes))
|
||||
(setq symbols
|
||||
(company-dabbrev--search-buffer regexp nil symbols start
|
||||
limit ignore-comments)))))
|
||||
(and limit
|
||||
(> (float-time (time-since start)) limit)
|
||||
(cl-return))))
|
||||
symbols))
|
||||
|
||||
(defun company-dabbrev--prefix ()
|
||||
;; Not in the middle of a word.
|
||||
(unless (looking-at company-dabbrev-char-regexp)
|
||||
;; Emacs can't do greedy backward-search.
|
||||
(company-grab-line (format "\\(?:^\\| \\)[^ ]*?\\(\\(?:%s\\)*\\)"
|
||||
company-dabbrev-char-regexp)
|
||||
1)))
|
||||
|
||||
(defun company-dabbrev--filter (prefix candidates)
|
||||
(let ((completion-ignore-case company-dabbrev-ignore-case))
|
||||
(all-completions prefix candidates)))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-dabbrev (command &optional arg &rest ignored)
|
||||
"dabbrev-like `company-mode' completion backend."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-dabbrev))
|
||||
(prefix (company-dabbrev--prefix))
|
||||
(candidates
|
||||
(let* ((case-fold-search company-dabbrev-ignore-case)
|
||||
(words (company-dabbrev--search (company-dabbrev--make-regexp)
|
||||
company-dabbrev-time-limit
|
||||
(pcase company-dabbrev-other-buffers
|
||||
(`t (list major-mode))
|
||||
(`all `all))))
|
||||
(downcase-p (if (eq company-dabbrev-downcase 'case-replace)
|
||||
case-replace
|
||||
company-dabbrev-downcase)))
|
||||
(setq words (company-dabbrev--filter arg words))
|
||||
(if downcase-p
|
||||
(mapcar 'downcase words)
|
||||
words)))
|
||||
(ignore-case company-dabbrev-ignore-case)
|
||||
(duplicates t)))
|
||||
|
||||
(provide 'company-dabbrev)
|
||||
;;; company-dabbrev.el ends here
|
Binary file not shown.
|
@ -0,0 +1,186 @@
|
|||
;;; company-eclim.el --- company-mode completion backend for Eclim
|
||||
|
||||
;; Copyright (C) 2009, 2011, 2013, 2015 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Using `emacs-eclim' together with (or instead of) this backend is
|
||||
;; recommended, as it allows you to use other Eclim features.
|
||||
;;
|
||||
;; The alternative backend provided by `emacs-eclim' uses `yasnippet'
|
||||
;; instead of `company-template' to expand function calls, and it supports
|
||||
;; some languages other than Java.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'company-template)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defgroup company-eclim nil
|
||||
"Completion backend for Eclim."
|
||||
:group 'company)
|
||||
|
||||
(defun company-eclim-executable-find ()
|
||||
(let (file)
|
||||
(cl-dolist (eclipse-root '("/Applications/eclipse" "/usr/lib/eclipse"
|
||||
"/usr/local/lib/eclipse"))
|
||||
(and (file-exists-p (setq file (expand-file-name "plugins" eclipse-root)))
|
||||
(setq file (car (last (directory-files file t "^org.eclim_"))))
|
||||
(file-exists-p (setq file (expand-file-name "bin/eclim" file)))
|
||||
(cl-return file)))))
|
||||
|
||||
(defcustom company-eclim-executable
|
||||
(or (bound-and-true-p eclim-executable)
|
||||
(executable-find "eclim")
|
||||
(company-eclim-executable-find))
|
||||
"Location of eclim executable."
|
||||
:type 'file)
|
||||
|
||||
(defcustom company-eclim-auto-save t
|
||||
"Determines whether to save the buffer when retrieving completions.
|
||||
eclim can only complete correctly when the buffer has been saved."
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "On" t)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defvar-local company-eclim--project-dir 'unknown)
|
||||
|
||||
(defvar-local company-eclim--project-name nil)
|
||||
|
||||
(declare-function json-read "json")
|
||||
(defvar json-array-type)
|
||||
|
||||
(defun company-eclim--call-process (&rest args)
|
||||
(let ((coding-system-for-read 'utf-8)
|
||||
res)
|
||||
(require 'json)
|
||||
(with-temp-buffer
|
||||
(if (= 0 (setq res (apply 'call-process company-eclim-executable nil t nil
|
||||
"-command" args)))
|
||||
(let ((json-array-type 'list))
|
||||
(goto-char (point-min))
|
||||
(unless (eobp)
|
||||
(json-read)))
|
||||
(message "Company-eclim command failed with error %d:\n%s" res
|
||||
(buffer-substring (point-min) (point-max)))
|
||||
nil))))
|
||||
|
||||
(defun company-eclim--project-list ()
|
||||
(company-eclim--call-process "project_list"))
|
||||
|
||||
(defun company-eclim--project-dir ()
|
||||
(if (eq company-eclim--project-dir 'unknown)
|
||||
(let ((dir (locate-dominating-file buffer-file-name ".project")))
|
||||
(when dir
|
||||
(setq company-eclim--project-dir
|
||||
(directory-file-name
|
||||
(expand-file-name dir)))))
|
||||
company-eclim--project-dir))
|
||||
|
||||
(defun company-eclim--project-name ()
|
||||
(or company-eclim--project-name
|
||||
(let ((dir (company-eclim--project-dir)))
|
||||
(when dir
|
||||
(setq company-eclim--project-name
|
||||
(cl-loop for project in (company-eclim--project-list)
|
||||
when (equal (cdr (assoc 'path project)) dir)
|
||||
return (cdr (assoc 'name project))))))))
|
||||
|
||||
(defun company-eclim--candidates (prefix)
|
||||
(interactive "d")
|
||||
(let ((project-file (file-relative-name buffer-file-name
|
||||
(company-eclim--project-dir)))
|
||||
completions)
|
||||
(when company-eclim-auto-save
|
||||
(when (buffer-modified-p)
|
||||
(basic-save-buffer))
|
||||
;; FIXME: Sometimes this isn't finished when we complete.
|
||||
(company-eclim--call-process "java_src_update"
|
||||
"-p" (company-eclim--project-name)
|
||||
"-f" project-file))
|
||||
(dolist (item (cdr (assoc 'completions
|
||||
(company-eclim--call-process
|
||||
"java_complete" "-p" (company-eclim--project-name)
|
||||
"-f" project-file
|
||||
"-o" (number-to-string
|
||||
(company-eclim--search-point prefix))
|
||||
"-e" "utf-8"
|
||||
"-l" "standard"))))
|
||||
(let* ((meta (cdr (assoc 'info item)))
|
||||
(completion meta))
|
||||
(when (string-match " ?[(:-]" completion)
|
||||
(setq completion (substring completion 0 (match-beginning 0))))
|
||||
(put-text-property 0 1 'meta meta completion)
|
||||
(push completion completions)))
|
||||
(let ((completion-ignore-case nil))
|
||||
(all-completions prefix completions))))
|
||||
|
||||
(defun company-eclim--search-point (prefix)
|
||||
(if (or (cl-plusp (length prefix)) (eq (char-before) ?.))
|
||||
(1- (point))
|
||||
(point)))
|
||||
|
||||
(defun company-eclim--meta (candidate)
|
||||
(get-text-property 0 'meta candidate))
|
||||
|
||||
(defun company-eclim--annotation (candidate)
|
||||
(let ((meta (company-eclim--meta candidate)))
|
||||
(when (string-match "\\(([^-]*\\) -" meta)
|
||||
(substring meta (match-beginning 1) (match-end 1)))))
|
||||
|
||||
(defun company-eclim--prefix ()
|
||||
(let ((prefix (company-grab-symbol)))
|
||||
(when prefix
|
||||
;; Completion candidates for annotations don't include '@'.
|
||||
(when (eq ?@ (string-to-char prefix))
|
||||
(setq prefix (substring prefix 1)))
|
||||
prefix)))
|
||||
|
||||
(defun company-eclim (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend for Eclim.
|
||||
Eclim provides access to Eclipse Java IDE features for other editors.
|
||||
|
||||
Eclim version 1.7.13 or newer (?) is required.
|
||||
|
||||
Completions only work correctly when the buffer has been saved.
|
||||
`company-eclim-auto-save' determines whether to do this automatically."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-eclim))
|
||||
(prefix (and (derived-mode-p 'java-mode 'jde-mode)
|
||||
buffer-file-name
|
||||
company-eclim-executable
|
||||
(company-eclim--project-name)
|
||||
(not (company-in-string-or-comment))
|
||||
(or (company-eclim--prefix) 'stop)))
|
||||
(candidates (company-eclim--candidates arg))
|
||||
(meta (company-eclim--meta arg))
|
||||
;; because "" doesn't return everything
|
||||
(no-cache (equal arg ""))
|
||||
(annotation (company-eclim--annotation arg))
|
||||
(post-completion (let ((anno (company-eclim--annotation arg)))
|
||||
(when anno
|
||||
(insert anno)
|
||||
(company-template-c-like-templatify anno))))))
|
||||
|
||||
(provide 'company-eclim)
|
||||
;;; company-eclim.el ends here
|
Binary file not shown.
|
@ -0,0 +1,225 @@
|
|||
;;; company-elisp.el --- company-mode completion backend for Emacs Lisp -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2009, 2011-2013 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
(require 'help-mode)
|
||||
(require 'find-func)
|
||||
|
||||
(defgroup company-elisp nil
|
||||
"Completion backend for Emacs Lisp."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-elisp-detect-function-context t
|
||||
"If enabled, offer Lisp functions only in appropriate contexts.
|
||||
Functions are offered for completion only after ' and \(."
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "On" t)))
|
||||
|
||||
(defcustom company-elisp-show-locals-first t
|
||||
"If enabled, locally bound variables and functions are displayed
|
||||
first in the candidates list."
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "On" t)))
|
||||
|
||||
(defun company-elisp--prefix ()
|
||||
(let ((prefix (company-grab-symbol)))
|
||||
(if prefix
|
||||
(when (if (company-in-string-or-comment)
|
||||
(= (char-before (- (point) (length prefix))) ?`)
|
||||
(company-elisp--should-complete))
|
||||
prefix)
|
||||
'stop)))
|
||||
|
||||
(defun company-elisp--predicate (symbol)
|
||||
(or (boundp symbol)
|
||||
(fboundp symbol)
|
||||
(facep symbol)
|
||||
(featurep symbol)))
|
||||
|
||||
(defun company-elisp--fns-regexp (&rest names)
|
||||
(concat "\\_<\\(?:cl-\\)?" (regexp-opt names) "\\*?\\_>"))
|
||||
|
||||
(defvar company-elisp-parse-limit 30)
|
||||
(defvar company-elisp-parse-depth 100)
|
||||
|
||||
(defvar company-elisp-defun-names '("defun" "defmacro" "defsubst"))
|
||||
|
||||
(defvar company-elisp-var-binding-regexp
|
||||
(apply #'company-elisp--fns-regexp "let" "lambda" "lexical-let"
|
||||
company-elisp-defun-names)
|
||||
"Regular expression matching head of a multiple variable bindings form.")
|
||||
|
||||
(defvar company-elisp-var-binding-regexp-1
|
||||
(company-elisp--fns-regexp "dolist" "dotimes")
|
||||
"Regular expression matching head of a form with one variable binding.")
|
||||
|
||||
(defvar company-elisp-fun-binding-regexp
|
||||
(company-elisp--fns-regexp "flet" "labels")
|
||||
"Regular expression matching head of a function bindings form.")
|
||||
|
||||
(defvar company-elisp-defuns-regexp
|
||||
(concat "([ \t\n]*"
|
||||
(apply #'company-elisp--fns-regexp company-elisp-defun-names)))
|
||||
|
||||
(defun company-elisp--should-complete ()
|
||||
(let ((start (point))
|
||||
(depth (car (syntax-ppss))))
|
||||
(not
|
||||
(when (> depth 0)
|
||||
(save-excursion
|
||||
(up-list (- depth))
|
||||
(when (looking-at company-elisp-defuns-regexp)
|
||||
(forward-char)
|
||||
(forward-sexp 1)
|
||||
(unless (= (point) start)
|
||||
(condition-case nil
|
||||
(let ((args-end (scan-sexps (point) 2)))
|
||||
(or (null args-end)
|
||||
(> args-end start)))
|
||||
(scan-error
|
||||
t)))))))))
|
||||
|
||||
(defun company-elisp--locals (prefix functions-p)
|
||||
(let ((regexp (concat "[ \t\n]*\\(\\_<" (regexp-quote prefix)
|
||||
"\\(?:\\sw\\|\\s_\\)*\\_>\\)"))
|
||||
(pos (point))
|
||||
res)
|
||||
(condition-case nil
|
||||
(save-excursion
|
||||
(dotimes (_ company-elisp-parse-depth)
|
||||
(up-list -1)
|
||||
(save-excursion
|
||||
(when (eq (char-after) ?\()
|
||||
(forward-char 1)
|
||||
(when (ignore-errors
|
||||
(save-excursion (forward-list)
|
||||
(<= (point) pos)))
|
||||
(skip-chars-forward " \t\n")
|
||||
(cond
|
||||
((looking-at (if functions-p
|
||||
company-elisp-fun-binding-regexp
|
||||
company-elisp-var-binding-regexp))
|
||||
(down-list 1)
|
||||
(condition-case nil
|
||||
(dotimes (_ company-elisp-parse-limit)
|
||||
(save-excursion
|
||||
(when (looking-at "[ \t\n]*(")
|
||||
(down-list 1))
|
||||
(when (looking-at regexp)
|
||||
(cl-pushnew (match-string-no-properties 1) res)))
|
||||
(forward-sexp))
|
||||
(scan-error nil)))
|
||||
((unless functions-p
|
||||
(looking-at company-elisp-var-binding-regexp-1))
|
||||
(down-list 1)
|
||||
(when (looking-at regexp)
|
||||
(cl-pushnew (match-string-no-properties 1) res)))))))))
|
||||
(scan-error nil))
|
||||
res))
|
||||
|
||||
(defun company-elisp-candidates (prefix)
|
||||
(let* ((predicate (company-elisp--candidates-predicate prefix))
|
||||
(locals (company-elisp--locals prefix (eq predicate 'fboundp)))
|
||||
(globals (company-elisp--globals prefix predicate))
|
||||
(locals (cl-loop for local in locals
|
||||
when (not (member local globals))
|
||||
collect local)))
|
||||
(if company-elisp-show-locals-first
|
||||
(append (sort locals 'string<)
|
||||
(sort globals 'string<))
|
||||
(append locals globals))))
|
||||
|
||||
(defun company-elisp--globals (prefix predicate)
|
||||
(all-completions prefix obarray predicate))
|
||||
|
||||
(defun company-elisp--candidates-predicate (prefix)
|
||||
(let* ((completion-ignore-case nil)
|
||||
(beg (- (point) (length prefix)))
|
||||
(before (char-before beg)))
|
||||
(if (and company-elisp-detect-function-context
|
||||
(not (memq before '(?' ?`))))
|
||||
(if (and (eq before ?\()
|
||||
(not
|
||||
(save-excursion
|
||||
(ignore-errors
|
||||
(goto-char (1- beg))
|
||||
(or (company-elisp--before-binding-varlist-p)
|
||||
(progn
|
||||
(up-list -1)
|
||||
(company-elisp--before-binding-varlist-p)))))))
|
||||
'fboundp
|
||||
'boundp)
|
||||
'company-elisp--predicate)))
|
||||
|
||||
(defun company-elisp--before-binding-varlist-p ()
|
||||
(save-excursion
|
||||
(and (prog1 (search-backward "(")
|
||||
(forward-char 1))
|
||||
(looking-at company-elisp-var-binding-regexp))))
|
||||
|
||||
(defun company-elisp--doc (symbol)
|
||||
(let* ((symbol (intern symbol))
|
||||
(doc (if (fboundp symbol)
|
||||
(documentation symbol t)
|
||||
(documentation-property symbol 'variable-documentation t))))
|
||||
(and (stringp doc)
|
||||
(string-match ".*$" doc)
|
||||
(match-string 0 doc))))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-elisp (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend for Emacs Lisp."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-elisp))
|
||||
(prefix (and (derived-mode-p 'emacs-lisp-mode 'inferior-emacs-lisp-mode)
|
||||
(company-elisp--prefix)))
|
||||
(candidates (company-elisp-candidates arg))
|
||||
(sorted company-elisp-show-locals-first)
|
||||
(meta (company-elisp--doc arg))
|
||||
(doc-buffer (let ((symbol (intern arg)))
|
||||
(save-window-excursion
|
||||
(ignore-errors
|
||||
(cond
|
||||
((fboundp symbol) (describe-function symbol))
|
||||
((boundp symbol) (describe-variable symbol))
|
||||
((featurep symbol) (describe-package symbol))
|
||||
((facep symbol) (describe-face symbol))
|
||||
(t (signal 'user-error nil)))
|
||||
(help-buffer)))))
|
||||
(location (let ((sym (intern arg)))
|
||||
(cond
|
||||
((fboundp sym) (find-definition-noselect sym nil))
|
||||
((boundp sym) (find-definition-noselect sym 'defvar))
|
||||
((featurep sym) (cons (find-file-noselect (find-library-name
|
||||
(symbol-name sym)))
|
||||
0))
|
||||
((facep sym) (find-definition-noselect sym 'defface)))))))
|
||||
|
||||
(provide 'company-elisp)
|
||||
;;; company-elisp.el ends here
|
Binary file not shown.
|
@ -0,0 +1,107 @@
|
|||
;;; company-etags.el --- company-mode completion backend for etags
|
||||
|
||||
;; Copyright (C) 2009-2011, 2014 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
(require 'etags)
|
||||
|
||||
(defgroup company-etags nil
|
||||
"Completion backend for etags."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-etags-use-main-table-list t
|
||||
"Always search `tags-table-list' if set.
|
||||
If this is disabled, `company-etags' will try to find the one table for each
|
||||
buffer automatically."
|
||||
:type '(choice (const :tag "off" nil)
|
||||
(const :tag "on" t)))
|
||||
|
||||
(defcustom company-etags-ignore-case nil
|
||||
"Non-nil to ignore case in completion candidates."
|
||||
:type 'boolean
|
||||
:package-version '(company . "0.7.3"))
|
||||
|
||||
(defcustom company-etags-everywhere nil
|
||||
"Non-nil to offer completions in comments and strings.
|
||||
Set it to t or to a list of major modes."
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "Any supported mode" t)
|
||||
(repeat :tag "Some major modes"
|
||||
(symbol :tag "Major mode")))
|
||||
:package-version '(company . "0.9.0"))
|
||||
|
||||
(defvar company-etags-modes '(prog-mode c-mode objc-mode c++-mode java-mode
|
||||
jde-mode pascal-mode perl-mode python-mode))
|
||||
|
||||
(defvar-local company-etags-buffer-table 'unknown)
|
||||
|
||||
(defun company-etags-find-table ()
|
||||
(let ((file (expand-file-name
|
||||
"TAGS"
|
||||
(locate-dominating-file (or buffer-file-name
|
||||
default-directory)
|
||||
"TAGS"))))
|
||||
(when (and file (file-regular-p file))
|
||||
(list file))))
|
||||
|
||||
(defun company-etags-buffer-table ()
|
||||
(or (and company-etags-use-main-table-list tags-table-list)
|
||||
(if (eq company-etags-buffer-table 'unknown)
|
||||
(setq company-etags-buffer-table (company-etags-find-table))
|
||||
company-etags-buffer-table)))
|
||||
|
||||
(defun company-etags--candidates (prefix)
|
||||
(let ((tags-table-list (company-etags-buffer-table))
|
||||
(completion-ignore-case company-etags-ignore-case))
|
||||
(and (or tags-file-name tags-table-list)
|
||||
(fboundp 'tags-completion-table)
|
||||
(save-excursion
|
||||
(visit-tags-table-buffer)
|
||||
(all-completions prefix (tags-completion-table))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-etags (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend for etags."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-etags))
|
||||
(prefix (and (apply #'derived-mode-p company-etags-modes)
|
||||
(or (eq t company-etags-everywhere)
|
||||
(apply #'derived-mode-p company-etags-everywhere)
|
||||
(not (company-in-string-or-comment)))
|
||||
(company-etags-buffer-table)
|
||||
(or (company-grab-symbol) 'stop)))
|
||||
(candidates (company-etags--candidates arg))
|
||||
(location (let ((tags-table-list (company-etags-buffer-table)))
|
||||
(when (fboundp 'find-tag-noselect)
|
||||
(save-excursion
|
||||
(let ((buffer (find-tag-noselect arg)))
|
||||
(cons buffer (with-current-buffer buffer (point))))))))
|
||||
(ignore-case company-etags-ignore-case)))
|
||||
|
||||
(provide 'company-etags)
|
||||
;;; company-etags.el ends here
|
Binary file not shown.
|
@ -0,0 +1,148 @@
|
|||
;;; company-files.el --- company-mode completion backend for file names
|
||||
|
||||
;; Copyright (C) 2009-2011, 2014-2015 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defgroup company-files nil
|
||||
"Completion backend for file names."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-files-exclusions nil
|
||||
"File name extensions and directory names to ignore.
|
||||
The values should use the same format as `completion-ignored-extensions'."
|
||||
:type '(const string)
|
||||
:package-version '(company . "0.9.1"))
|
||||
|
||||
(defun company-files--directory-files (dir prefix)
|
||||
;; Don't use directory-files. It produces directories without trailing /.
|
||||
(condition-case err
|
||||
(let ((comp (sort (file-name-all-completions prefix dir)
|
||||
(lambda (s1 s2) (string-lessp (downcase s1) (downcase s2))))))
|
||||
(when company-files-exclusions
|
||||
(setq comp (company-files--exclusions-filtered comp)))
|
||||
(if (equal prefix "")
|
||||
(delete "../" (delete "./" comp))
|
||||
comp))
|
||||
(file-error nil)))
|
||||
|
||||
(defun company-files--exclusions-filtered (completions)
|
||||
(let* ((dir-exclusions (cl-delete-if-not #'company-files--trailing-slash-p
|
||||
company-files-exclusions))
|
||||
(file-exclusions (cl-set-difference company-files-exclusions
|
||||
dir-exclusions)))
|
||||
(cl-loop for c in completions
|
||||
unless (if (company-files--trailing-slash-p c)
|
||||
(member c dir-exclusions)
|
||||
(cl-find-if (lambda (exclusion)
|
||||
(string-suffix-p exclusion c))
|
||||
file-exclusions))
|
||||
collect c)))
|
||||
|
||||
(defvar company-files--regexps
|
||||
(let* ((root (if (eq system-type 'windows-nt)
|
||||
"[a-zA-Z]:/"
|
||||
"/"))
|
||||
(begin (concat "\\(?:\\.\\{1,2\\}/\\|~/\\|" root "\\)")))
|
||||
(list (concat "\"\\(" begin "[^\"\n]*\\)")
|
||||
(concat "\'\\(" begin "[^\'\n]*\\)")
|
||||
(concat "\\(?:[ \t=]\\|^\\)\\(" begin "[^ \t\n]*\\)"))))
|
||||
|
||||
(defun company-files--grab-existing-name ()
|
||||
;; Grab the file name.
|
||||
;; When surrounded with quotes, it can include spaces.
|
||||
(let (file dir)
|
||||
(and (cl-dolist (regexp company-files--regexps)
|
||||
(when (setq file (company-grab-line regexp 1))
|
||||
(cl-return file)))
|
||||
(company-files--connected-p file)
|
||||
(setq dir (file-name-directory file))
|
||||
(not (string-match "//" dir))
|
||||
(file-exists-p dir)
|
||||
file)))
|
||||
|
||||
(defun company-files--connected-p (file)
|
||||
(or (not (file-remote-p file))
|
||||
(file-remote-p file nil t)))
|
||||
|
||||
(defun company-files--trailing-slash-p (file)
|
||||
;; `file-directory-p' is very expensive on remotes. We are relying on
|
||||
;; `file-name-all-completions' returning directories with trailing / instead.
|
||||
(let ((len (length file)))
|
||||
(and (> len 0) (eq (aref file (1- len)) ?/))))
|
||||
|
||||
(defvar company-files--completion-cache nil)
|
||||
|
||||
(defun company-files--complete (prefix)
|
||||
(let* ((dir (file-name-directory prefix))
|
||||
(file (file-name-nondirectory prefix))
|
||||
(key (list file
|
||||
(expand-file-name dir)
|
||||
(nth 5 (file-attributes dir))))
|
||||
(completion-ignore-case read-file-name-completion-ignore-case))
|
||||
(unless (company-file--keys-match-p key (car company-files--completion-cache))
|
||||
(let* ((candidates (mapcar (lambda (f) (concat dir f))
|
||||
(company-files--directory-files dir file)))
|
||||
(directories (unless (file-remote-p dir)
|
||||
(cl-remove-if-not (lambda (f)
|
||||
(and (company-files--trailing-slash-p f)
|
||||
(not (file-remote-p f))
|
||||
(company-files--connected-p f)))
|
||||
candidates)))
|
||||
(children (and directories
|
||||
(cl-mapcan (lambda (d)
|
||||
(mapcar (lambda (c) (concat d c))
|
||||
(company-files--directory-files d "")))
|
||||
directories))))
|
||||
(setq company-files--completion-cache
|
||||
(cons key (append candidates children)))))
|
||||
(all-completions prefix
|
||||
(cdr company-files--completion-cache))))
|
||||
|
||||
(defun company-file--keys-match-p (new old)
|
||||
(and (equal (cdr old) (cdr new))
|
||||
(string-prefix-p (car old) (car new))))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-files (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend existing file names.
|
||||
Completions works for proper absolute and relative files paths.
|
||||
File paths with spaces are only supported inside strings."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-files))
|
||||
(prefix (company-files--grab-existing-name))
|
||||
(candidates (company-files--complete arg))
|
||||
(location (cons (dired-noselect
|
||||
(file-name-directory (directory-file-name arg))) 1))
|
||||
(post-completion (when (company-files--trailing-slash-p arg)
|
||||
(delete-char -1)))
|
||||
(sorted t)
|
||||
(no-cache t)))
|
||||
|
||||
(provide 'company-files)
|
||||
;;; company-files.el ends here
|
Binary file not shown.
|
@ -0,0 +1,117 @@
|
|||
;;; company-gtags.el --- company-mode completion backend for GNU Global
|
||||
|
||||
;; Copyright (C) 2009-2011, 2014 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'company-template)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defgroup company-gtags nil
|
||||
"Completion backend for GNU Global."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-gtags-executable
|
||||
(executable-find "global")
|
||||
"Location of GNU global executable."
|
||||
:type 'string)
|
||||
|
||||
(define-obsolete-variable-alias
|
||||
'company-gtags-gnu-global-program-name
|
||||
'company-gtags-executable "earlier")
|
||||
|
||||
(defcustom company-gtags-insert-arguments t
|
||||
"When non-nil, insert function arguments as a template after completion."
|
||||
:type 'boolean
|
||||
:package-version '(company . "0.8.1"))
|
||||
|
||||
(defvar-local company-gtags--tags-available-p 'unknown)
|
||||
|
||||
(defcustom company-gtags-modes '(prog-mode jde-mode)
|
||||
"Modes that use `company-gtags'.
|
||||
In all these modes (and their derivatives) `company-gtags' will perform
|
||||
completion."
|
||||
:type '(repeat (symbol :tag "Major mode"))
|
||||
:package-version '(company . "0.8.4"))
|
||||
|
||||
(defun company-gtags--tags-available-p ()
|
||||
(if (eq company-gtags--tags-available-p 'unknown)
|
||||
(setq company-gtags--tags-available-p
|
||||
(locate-dominating-file buffer-file-name "GTAGS"))
|
||||
company-gtags--tags-available-p))
|
||||
|
||||
(defun company-gtags--fetch-tags (prefix)
|
||||
(with-temp-buffer
|
||||
(let (tags)
|
||||
(when (= 0 (process-file company-gtags-executable nil
|
||||
;; "-T" goes through all the tag files listed in GTAGSLIBPATH
|
||||
(list (current-buffer) nil) nil "-xGqT" (concat "^" prefix)))
|
||||
(goto-char (point-min))
|
||||
(cl-loop while
|
||||
(re-search-forward (concat
|
||||
"^"
|
||||
"\\([^ ]*\\)" ;; completion
|
||||
"[ \t]+\\([[:digit:]]+\\)" ;; linum
|
||||
"[ \t]+\\([^ \t]+\\)" ;; file
|
||||
"[ \t]+\\(.*\\)" ;; definition
|
||||
"$"
|
||||
) nil t)
|
||||
collect
|
||||
(propertize (match-string 1)
|
||||
'meta (match-string 4)
|
||||
'location (cons (expand-file-name (match-string 3))
|
||||
(string-to-number (match-string 2)))
|
||||
))))))
|
||||
|
||||
(defun company-gtags--annotation (arg)
|
||||
(let ((meta (get-text-property 0 'meta arg)))
|
||||
(when (string-match (concat arg "\\((.*)\\).*") meta)
|
||||
(match-string 1 meta))))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-gtags (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend for GNU Global."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-gtags))
|
||||
(prefix (and company-gtags-executable
|
||||
buffer-file-name
|
||||
(apply #'derived-mode-p company-gtags-modes)
|
||||
(not (company-in-string-or-comment))
|
||||
(company-gtags--tags-available-p)
|
||||
(or (company-grab-symbol) 'stop)))
|
||||
(candidates (company-gtags--fetch-tags arg))
|
||||
(sorted t)
|
||||
(duplicates t)
|
||||
(annotation (company-gtags--annotation arg))
|
||||
(meta (get-text-property 0 'meta arg))
|
||||
(location (get-text-property 0 'location arg))
|
||||
(post-completion (let ((anno (company-gtags--annotation arg)))
|
||||
(when (and company-gtags-insert-arguments anno)
|
||||
(insert anno)
|
||||
(company-template-c-like-templatify anno))))))
|
||||
|
||||
(provide 'company-gtags)
|
||||
;;; company-gtags.el ends here
|
Binary file not shown.
|
@ -0,0 +1,82 @@
|
|||
;;; company-ispell.el --- company-mode completion backend using Ispell
|
||||
|
||||
;; Copyright (C) 2009-2011, 2013-2016 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
(require 'ispell)
|
||||
|
||||
(defgroup company-ispell nil
|
||||
"Completion backend using Ispell."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-ispell-dictionary nil
|
||||
"Dictionary to use for `company-ispell'.
|
||||
If nil, use `ispell-complete-word-dict'."
|
||||
:type '(choice (const :tag "default (nil)" nil)
|
||||
(file :tag "dictionary" t)))
|
||||
|
||||
(defvar company-ispell-available 'unknown)
|
||||
|
||||
(defalias 'company-ispell--lookup-words
|
||||
(if (fboundp 'ispell-lookup-words)
|
||||
'ispell-lookup-words
|
||||
'lookup-words))
|
||||
|
||||
(defun company-ispell-available ()
|
||||
(when (eq company-ispell-available 'unknown)
|
||||
(condition-case err
|
||||
(progn
|
||||
(company-ispell--lookup-words "WHATEVER")
|
||||
(setq company-ispell-available t))
|
||||
(error
|
||||
(message "Company: ispell-look-command not found")
|
||||
(setq company-ispell-available nil))))
|
||||
company-ispell-available)
|
||||
|
||||
;;;###autoload
|
||||
(defun company-ispell (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend using Ispell."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-ispell))
|
||||
(prefix (when (company-ispell-available)
|
||||
(company-grab-word)))
|
||||
(candidates
|
||||
(let ((words (company-ispell--lookup-words
|
||||
arg
|
||||
(or company-ispell-dictionary ispell-complete-word-dict)))
|
||||
(completion-ignore-case t))
|
||||
(if (string= arg "")
|
||||
;; Small optimization.
|
||||
words
|
||||
;; Work around issue #284.
|
||||
(all-completions arg words))))
|
||||
(sorted t)
|
||||
(ignore-case 'keep-prefix)))
|
||||
|
||||
(provide 'company-ispell)
|
||||
;;; company-ispell.el ends here
|
Binary file not shown.
|
@ -0,0 +1,268 @@
|
|||
;;; company-keywords.el --- A company backend for programming language keywords
|
||||
|
||||
;; Copyright (C) 2009-2011, 2016 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defun company-keywords-upper-lower (&rest lst)
|
||||
;; Upcase order is different for _.
|
||||
(nconc (sort (mapcar 'upcase lst) 'string<) lst))
|
||||
|
||||
(defvar company-keywords-alist
|
||||
;; Please contribute corrections or additions.
|
||||
`((c++-mode
|
||||
"alignas" "alignof" "asm" "auto" "bool" "break" "case" "catch" "char"
|
||||
"char16_t" "char32_t" "class" "const" "const_cast" "constexpr" "continue"
|
||||
"decltype" "default" "delete" "do" "double" "dynamic_cast" "else" "enum"
|
||||
"explicit" "export" "extern" "false" "final" "float" "for" "friend"
|
||||
"goto" "if" "inline" "int" "long" "mutable" "namespace" "new" "noexcept"
|
||||
"nullptr" "operator" "override"
|
||||
"private" "protected" "public" "register" "reinterpret_cast"
|
||||
"return" "short" "signed" "sizeof" "static" "static_assert"
|
||||
"static_cast" "struct" "switch" "template" "this" "thread_local"
|
||||
"throw" "true" "try" "typedef" "typeid" "typename"
|
||||
"union" "unsigned" "using" "virtual" "void" "volatile" "wchar_t" "while")
|
||||
(c-mode
|
||||
"auto" "break" "case" "char" "const" "continue" "default" "do"
|
||||
"double" "else" "enum" "extern" "float" "for" "goto" "if" "int" "long"
|
||||
"register" "return" "short" "signed" "sizeof" "static" "struct"
|
||||
"switch" "typedef" "union" "unsigned" "void" "volatile" "while")
|
||||
(csharp-mode
|
||||
"abstract" "add" "alias" "as" "base" "bool" "break" "byte" "case"
|
||||
"catch" "char" "checked" "class" "const" "continue" "decimal" "default"
|
||||
"delegate" "do" "double" "else" "enum" "event" "explicit" "extern"
|
||||
"false" "finally" "fixed" "float" "for" "foreach" "get" "global" "goto"
|
||||
"if" "implicit" "in" "int" "interface" "internal" "is" "lock" "long"
|
||||
"namespace" "new" "null" "object" "operator" "out" "override" "params"
|
||||
"partial" "private" "protected" "public" "readonly" "ref" "remove"
|
||||
"return" "sbyte" "sealed" "set" "short" "sizeof" "stackalloc" "static"
|
||||
"string" "struct" "switch" "this" "throw" "true" "try" "typeof" "uint"
|
||||
"ulong" "unchecked" "unsafe" "ushort" "using" "value" "var" "virtual"
|
||||
"void" "volatile" "where" "while" "yield")
|
||||
(d-mode
|
||||
;; from http://www.digitalmars.com/d/2.0/lex.html
|
||||
"abstract" "alias" "align" "asm"
|
||||
"assert" "auto" "body" "bool" "break" "byte" "case" "cast" "catch"
|
||||
"cdouble" "cent" "cfloat" "char" "class" "const" "continue" "creal"
|
||||
"dchar" "debug" "default" "delegate" "delete" "deprecated" "do"
|
||||
"double" "else" "enum" "export" "extern" "false" "final" "finally"
|
||||
"float" "for" "foreach" "foreach_reverse" "function" "goto" "idouble"
|
||||
"if" "ifloat" "import" "in" "inout" "int" "interface" "invariant"
|
||||
"ireal" "is" "lazy" "long" "macro" "mixin" "module" "new" "nothrow"
|
||||
"null" "out" "override" "package" "pragma" "private" "protected"
|
||||
"public" "pure" "real" "ref" "return" "scope" "short" "static" "struct"
|
||||
"super" "switch" "synchronized" "template" "this" "throw" "true" "try"
|
||||
"typedef" "typeid" "typeof" "ubyte" "ucent" "uint" "ulong" "union"
|
||||
"unittest" "ushort" "version" "void" "volatile" "wchar" "while" "with")
|
||||
(f90-mode .
|
||||
;; from f90.el
|
||||
;; ".AND." ".GE." ".GT." ".LT." ".LE." ".NE." ".OR." ".TRUE." ".FALSE."
|
||||
,(company-keywords-upper-lower
|
||||
"abs" "abstract" "achar" "acos" "adjustl" "adjustr" "aimag" "aint"
|
||||
"align" "all" "all_prefix" "all_scatter" "all_suffix" "allocatable"
|
||||
"allocate" "allocated" "and" "anint" "any" "any_prefix" "any_scatter"
|
||||
"any_suffix" "asin" "assign" "assignment" "associate" "associated"
|
||||
"asynchronous" "atan" "atan2" "backspace" "bind" "bit_size" "block"
|
||||
"btest" "c_alert" "c_associated" "c_backspace" "c_bool"
|
||||
"c_carriage_return" "c_char" "c_double" "c_double_complex" "c_f_pointer"
|
||||
"c_f_procpointer" "c_float" "c_float_complex" "c_form_feed" "c_funloc"
|
||||
"c_funptr" "c_horizontal_tab" "c_int" "c_int16_t" "c_int32_t" "c_int64_t"
|
||||
"c_int8_t" "c_int_fast16_t" "c_int_fast32_t" "c_int_fast64_t"
|
||||
"c_int_fast8_t" "c_int_least16_t" "c_int_least32_t" "c_int_least64_t"
|
||||
"c_int_least8_t" "c_intmax_t" "c_intptr_t" "c_loc" "c_long"
|
||||
"c_long_double" "c_long_double_complex" "c_long_long" "c_new_line"
|
||||
"c_null_char" "c_null_funptr" "c_null_ptr" "c_ptr" "c_short"
|
||||
"c_signed_char" "c_size_t" "c_vertical_tab" "call" "case" "ceiling"
|
||||
"char" "character" "character_storage_size" "class" "close" "cmplx"
|
||||
"command_argument_count" "common" "complex" "conjg" "contains" "continue"
|
||||
"copy_prefix" "copy_scatter" "copy_suffix" "cos" "cosh" "count"
|
||||
"count_prefix" "count_scatter" "count_suffix" "cpu_time" "cshift"
|
||||
"cycle" "cyclic" "data" "date_and_time" "dble" "deallocate" "deferred"
|
||||
"digits" "dim" "dimension" "distribute" "do" "dot_product" "double"
|
||||
"dprod" "dynamic" "elemental" "else" "elseif" "elsewhere" "end" "enddo"
|
||||
"endfile" "endif" "entry" "enum" "enumerator" "eoshift" "epsilon" "eq"
|
||||
"equivalence" "eqv" "error_unit" "exit" "exp" "exponent" "extends"
|
||||
"extends_type_of" "external" "extrinsic" "false" "file_storage_size"
|
||||
"final" "floor" "flush" "forall" "format" "fraction" "function" "ge"
|
||||
"generic" "get_command" "get_command_argument" "get_environment_variable"
|
||||
"goto" "grade_down" "grade_up" "gt" "hpf_alignment" "hpf_distribution"
|
||||
"hpf_template" "huge" "iachar" "iall" "iall_prefix" "iall_scatter"
|
||||
"iall_suffix" "iand" "iany" "iany_prefix" "iany_scatter" "iany_suffix"
|
||||
"ibclr" "ibits" "ibset" "ichar" "ieee_arithmetic" "ieee_exceptions"
|
||||
"ieee_features" "ieee_get_underflow_mode" "ieee_set_underflow_mode"
|
||||
"ieee_support_underflow_control" "ieor" "if" "ilen" "implicit"
|
||||
"import" "include" "independent" "index" "inherit" "input_unit"
|
||||
"inquire" "int" "integer" "intent" "interface" "intrinsic" "ior"
|
||||
"iostat_end" "iostat_eor" "iparity" "iparity_prefix" "iparity_scatter"
|
||||
"iparity_suffix" "ishft" "ishftc" "iso_c_binding" "iso_fortran_env"
|
||||
"kind" "lbound" "le" "leadz" "len" "len_trim" "lge" "lgt" "lle" "llt"
|
||||
"log" "log10" "logical" "lt" "matmul" "max" "maxexponent" "maxloc"
|
||||
"maxval" "maxval_prefix" "maxval_scatter" "maxval_suffix" "merge"
|
||||
"min" "minexponent" "minloc" "minval" "minval_prefix" "minval_scatter"
|
||||
"minval_suffix" "mod" "module" "modulo" "move_alloc" "mvbits" "namelist"
|
||||
"ne" "nearest" "neqv" "new" "new_line" "nint" "non_intrinsic"
|
||||
"non_overridable" "none" "nopass" "not" "null" "nullify"
|
||||
"number_of_processors" "numeric_storage_size" "only" "onto" "open"
|
||||
"operator" "optional" "or" "output_unit" "pack" "parameter" "parity"
|
||||
"parity_prefix" "parity_scatter" "parity_suffix" "pass" "pause"
|
||||
"pointer" "popcnt" "poppar" "precision" "present" "print" "private"
|
||||
"procedure" "processors" "processors_shape" "product" "product_prefix"
|
||||
"product_scatter" "product_suffix" "program" "protected" "public"
|
||||
"pure" "radix" "random_number" "random_seed" "range" "read" "real"
|
||||
"realign" "recursive" "redistribute" "repeat" "reshape" "result"
|
||||
"return" "rewind" "rrspacing" "same_type_as" "save" "scale" "scan"
|
||||
"select" "selected_char_kind" "selected_int_kind" "selected_real_kind"
|
||||
"sequence" "set_exponent" "shape" "sign" "sin" "sinh" "size" "spacing"
|
||||
"spread" "sqrt" "stop" "subroutine" "sum" "sum_prefix" "sum_scatter"
|
||||
"sum_suffix" "system_clock" "tan" "tanh" "target" "template" "then"
|
||||
"tiny" "transfer" "transpose" "trim" "true" "type" "ubound" "unpack"
|
||||
"use" "value" "verify" "volatile" "wait" "where" "while" "with" "write"))
|
||||
(java-mode
|
||||
"abstract" "assert" "boolean" "break" "byte" "case" "catch" "char" "class"
|
||||
"continue" "default" "do" "double" "else" "enum" "extends" "final"
|
||||
"finally" "float" "for" "if" "implements" "import" "instanceof" "int"
|
||||
"interface" "long" "native" "new" "package" "private" "protected" "public"
|
||||
"return" "short" "static" "strictfp" "super" "switch" "synchronized"
|
||||
"this" "throw" "throws" "transient" "try" "void" "volatile" "while")
|
||||
(javascript-mode
|
||||
"break" "catch" "const" "continue" "delete" "do" "else" "export" "for"
|
||||
"function" "if" "import" "in" "instanceOf" "label" "let" "new" "return"
|
||||
"switch" "this" "throw" "try" "typeof" "var" "void" "while" "with" "yield")
|
||||
(objc-mode
|
||||
"@catch" "@class" "@encode" "@end" "@finally" "@implementation"
|
||||
"@interface" "@private" "@protected" "@protocol" "@public"
|
||||
"@selector" "@synchronized" "@throw" "@try" "alloc" "autorelease"
|
||||
"bycopy" "byref" "in" "inout" "oneway" "out" "release" "retain")
|
||||
(perl-mode
|
||||
;; from cperl.el
|
||||
"AUTOLOAD" "BEGIN" "CHECK" "CORE" "DESTROY" "END" "INIT" "__END__"
|
||||
"__FILE__" "__LINE__" "abs" "accept" "alarm" "and" "atan2" "bind"
|
||||
"binmode" "bless" "caller" "chdir" "chmod" "chomp" "chop" "chown" "chr"
|
||||
"chroot" "close" "closedir" "cmp" "connect" "continue" "cos"
|
||||
"crypt" "dbmclose" "dbmopen" "defined" "delete" "die" "do" "dump" "each"
|
||||
"else" "elsif" "endgrent" "endhostent" "endnetent" "endprotoent"
|
||||
"endpwent" "endservent" "eof" "eq" "eval" "exec" "exists" "exit" "exp"
|
||||
"fcntl" "fileno" "flock" "for" "foreach" "fork" "format" "formline"
|
||||
"ge" "getc" "getgrent" "getgrgid" "getgrnam" "gethostbyaddr"
|
||||
"gethostbyname" "gethostent" "getlogin" "getnetbyaddr" "getnetbyname"
|
||||
"getnetent" "getpeername" "getpgrp" "getppid" "getpriority"
|
||||
"getprotobyname" "getprotobynumber" "getprotoent" "getpwent" "getpwnam"
|
||||
"getpwuid" "getservbyname" "getservbyport" "getservent" "getsockname"
|
||||
"getsockopt" "glob" "gmtime" "goto" "grep" "gt" "hex" "if" "index" "int"
|
||||
"ioctl" "join" "keys" "kill" "last" "lc" "lcfirst" "le" "length"
|
||||
"link" "listen" "local" "localtime" "lock" "log" "lstat" "lt" "map"
|
||||
"mkdir" "msgctl" "msgget" "msgrcv" "msgsnd" "my" "ne" "next" "no"
|
||||
"not" "oct" "open" "opendir" "or" "ord" "our" "pack" "package" "pipe"
|
||||
"pop" "pos" "print" "printf" "push" "q" "qq" "quotemeta" "qw" "qx"
|
||||
"rand" "read" "readdir" "readline" "readlink" "readpipe" "recv" "redo"
|
||||
"ref" "rename" "require" "reset" "return" "reverse" "rewinddir" "rindex"
|
||||
"rmdir" "scalar" "seek" "seekdir" "select" "semctl" "semget" "semop"
|
||||
"send" "setgrent" "sethostent" "setnetent" "setpgrp" "setpriority"
|
||||
"setprotoent" "setpwent" "setservent" "setsockopt" "shift" "shmctl"
|
||||
"shmget" "shmread" "shmwrite" "shutdown" "sin" "sleep" "socket"
|
||||
"socketpair" "sort" "splice" "split" "sprintf" "sqrt" "srand" "stat"
|
||||
"study" "sub" "substr" "symlink" "syscall" "sysopen" "sysread" "system"
|
||||
"syswrite" "tell" "telldir" "tie" "time" "times" "tr" "truncate" "uc"
|
||||
"ucfirst" "umask" "undef" "unless" "unlink" "unpack" "unshift" "untie"
|
||||
"until" "use" "utime" "values" "vec" "wait" "waitpid"
|
||||
"wantarray" "warn" "while" "write" "x" "xor" "y")
|
||||
(php-mode
|
||||
"__CLASS__" "__DIR__" "__FILE__" "__FUNCTION__" "__LINE__" "__METHOD__"
|
||||
"__NAMESPACE__" "_once" "abstract" "and" "array" "as" "break" "case"
|
||||
"catch" "cfunction" "class" "clone" "const" "continue" "declare"
|
||||
"default" "die" "do" "echo" "else" "elseif" "empty" "enddeclare"
|
||||
"endfor" "endforeach" "endif" "endswitch" "endwhile" "eval" "exception"
|
||||
"exit" "extends" "final" "for" "foreach" "function" "global"
|
||||
"goto" "if" "implements" "include" "instanceof" "interface"
|
||||
"isset" "list" "namespace" "new" "old_function" "or" "php_user_filter"
|
||||
"print" "private" "protected" "public" "require" "require_once" "return"
|
||||
"static" "switch" "this" "throw" "try" "unset" "use" "var" "while" "xor")
|
||||
(python-mode
|
||||
"and" "assert" "break" "class" "continue" "def" "del" "elif" "else"
|
||||
"except" "exec" "finally" "for" "from" "global" "if" "import" "in" "is"
|
||||
"lambda" "not" "or" "pass" "print" "raise" "return" "try" "while" "yield")
|
||||
(ruby-mode
|
||||
"BEGIN" "END" "alias" "and" "begin" "break" "case" "class" "def" "defined?"
|
||||
"do" "else" "elsif" "end" "ensure" "false" "for" "if" "in" "module"
|
||||
"next" "nil" "not" "or" "redo" "rescue" "retry" "return" "self" "super"
|
||||
"then" "true" "undef" "unless" "until" "when" "while" "yield")
|
||||
;; From https://doc.rust-lang.org/grammar.html#keywords
|
||||
;; but excluding unused reserved words: https://www.reddit.com/r/rust/comments/34fq0k/is_there_a_good_list_of_rusts_keywords/cqucvnj
|
||||
(go-mode
|
||||
"break" "case" "chan" "const" "continue" "default" "defer" "else" "fallthrough"
|
||||
"for" "func" "go" "goto" "if" "import" "interface" "map" "package" "range"
|
||||
"return" "select" "struct" "switch" "type" "var")
|
||||
(rust-mode
|
||||
"Self"
|
||||
"as" "box" "break" "const" "continue" "crate" "else" "enum" "extern"
|
||||
"false" "fn" "for" "if" "impl" "in" "let" "loop" "macro" "match" "mod"
|
||||
"move" "mut" "pub" "ref" "return" "self" "static" "struct" "super"
|
||||
"trait" "true" "type" "unsafe" "use" "where" "while")
|
||||
(scala-mode
|
||||
"abstract" "case" "catch" "class" "def" "do" "else" "extends" "false"
|
||||
"final" "finally" "for" "forSome" "if" "implicit" "import" "lazy" "match"
|
||||
"new" "null" "object" "override" "package" "private" "protected"
|
||||
"return" "sealed" "super" "this" "throw" "trait" "true" "try" "type" "val"
|
||||
"var" "while" "with" "yield")
|
||||
(julia-mode
|
||||
"abstract" "break" "case" "catch" "const" "continue" "do" "else" "elseif"
|
||||
"end" "eval" "export" "false" "finally" "for" "function" "global" "if"
|
||||
"ifelse" "immutable" "import" "importall" "in" "let" "macro" "module"
|
||||
"otherwise" "quote" "return" "switch" "throw" "true" "try" "type"
|
||||
"typealias" "using" "while"
|
||||
)
|
||||
;; aliases
|
||||
(js2-mode . javascript-mode)
|
||||
(js2-jsx-mode . javascript-mode)
|
||||
(espresso-mode . javascript-mode)
|
||||
(js-mode . javascript-mode)
|
||||
(js-jsx-mode . javascript-mode)
|
||||
(cperl-mode . perl-mode)
|
||||
(jde-mode . java-mode)
|
||||
(ess-julia-mode . julia-mode)
|
||||
(enh-ruby-mode . ruby-mode))
|
||||
"Alist mapping major-modes to sorted keywords for `company-keywords'.")
|
||||
|
||||
;;;###autoload
|
||||
(defun company-keywords (command &optional arg &rest ignored)
|
||||
"`company-mode' backend for programming language keywords."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-keywords))
|
||||
(prefix (and (assq major-mode company-keywords-alist)
|
||||
(not (company-in-string-or-comment))
|
||||
(or (company-grab-symbol) 'stop)))
|
||||
(candidates
|
||||
(let ((completion-ignore-case nil)
|
||||
(symbols (cdr (assq major-mode company-keywords-alist))))
|
||||
(all-completions arg (if (consp symbols)
|
||||
symbols
|
||||
(cdr (assq symbols company-keywords-alist))))))
|
||||
(sorted t)))
|
||||
|
||||
(provide 'company-keywords)
|
||||
;;; company-keywords.el ends here
|
Binary file not shown.
|
@ -0,0 +1,142 @@
|
|||
;;; company-nxml.el --- company-mode completion backend for nxml-mode
|
||||
|
||||
;; Copyright (C) 2009-2011, 2013 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defvar rng-open-elements)
|
||||
(defvar rng-validate-mode)
|
||||
(defvar rng-in-attribute-regex)
|
||||
(defvar rng-in-attribute-value-regex)
|
||||
(declare-function rng-set-state-after "rng-nxml")
|
||||
(declare-function rng-match-possible-start-tag-names "rng-match")
|
||||
(declare-function rng-adjust-state-for-attribute "rng-nxml")
|
||||
(declare-function rng-match-possible-attribute-names "rng-match")
|
||||
(declare-function rng-adjust-state-for-attribute-value "rng-nxml")
|
||||
(declare-function rng-match-possible-value-strings "rng-match")
|
||||
|
||||
(defconst company-nxml-token-regexp
|
||||
"\\(?:[_[:alpha:]][-._[:alnum:]]*\\_>\\)")
|
||||
|
||||
(defvar company-nxml-in-attribute-value-regexp
|
||||
(replace-regexp-in-string "w" company-nxml-token-regexp
|
||||
"<w\\(?::w\\)?\
|
||||
\\(?:[ \t\r\n]+w\\(?::w\\)?[ \t\r\n]*=\
|
||||
\[ \t\r\n]*\\(?:\"[^\"]*\"\\|'[^']*'\\)\\)*\
|
||||
\[ \t\r\n]+\\(w\\(:w\\)?\\)[ \t\r\n]*=[ \t\r\n]*\
|
||||
\\(\"\\([^\"]*\\>\\)\\|'\\([^']*\\>\\)\\)\\="
|
||||
t t))
|
||||
|
||||
(defvar company-nxml-in-tag-name-regexp
|
||||
(replace-regexp-in-string "w" company-nxml-token-regexp
|
||||
"<\\(/?w\\(?::w?\\)?\\)?\\=" t t))
|
||||
|
||||
(defun company-nxml-all-completions (prefix alist)
|
||||
(let ((candidates (mapcar 'cdr alist))
|
||||
(case-fold-search nil)
|
||||
filtered)
|
||||
(when (cdar rng-open-elements)
|
||||
(push (concat "/" (cdar rng-open-elements)) candidates))
|
||||
(setq candidates (sort (all-completions prefix candidates) 'string<))
|
||||
(while candidates
|
||||
(unless (equal (car candidates) (car filtered))
|
||||
(push (car candidates) filtered))
|
||||
(pop candidates))
|
||||
(nreverse filtered)))
|
||||
|
||||
(defmacro company-nxml-prepared (&rest body)
|
||||
(declare (indent 0) (debug t))
|
||||
`(let ((lt-pos (save-excursion (search-backward "<" nil t)))
|
||||
xmltok-dtd)
|
||||
(when (and lt-pos (= (rng-set-state-after lt-pos) lt-pos))
|
||||
,@body)))
|
||||
|
||||
(defun company-nxml-tag (command &optional arg &rest ignored)
|
||||
(cl-case command
|
||||
(prefix (and (derived-mode-p 'nxml-mode)
|
||||
rng-validate-mode
|
||||
(company-grab company-nxml-in-tag-name-regexp 1)))
|
||||
(candidates (company-nxml-prepared
|
||||
(company-nxml-all-completions
|
||||
arg (rng-match-possible-start-tag-names))))
|
||||
(sorted t)))
|
||||
|
||||
(defun company-nxml-attribute (command &optional arg &rest ignored)
|
||||
(cl-case command
|
||||
(prefix (and (derived-mode-p 'nxml-mode)
|
||||
rng-validate-mode
|
||||
(memq (char-after) '(?\ ?\t ?\n)) ;; outside word
|
||||
(company-grab rng-in-attribute-regex 1)))
|
||||
(candidates (company-nxml-prepared
|
||||
(and (rng-adjust-state-for-attribute
|
||||
lt-pos (- (point) (length arg)))
|
||||
(company-nxml-all-completions
|
||||
arg (rng-match-possible-attribute-names)))))
|
||||
(sorted t)))
|
||||
|
||||
(defun company-nxml-attribute-value (command &optional arg &rest ignored)
|
||||
(cl-case command
|
||||
(prefix (and (derived-mode-p 'nxml-mode)
|
||||
rng-validate-mode
|
||||
(and (memq (char-after) '(?' ?\" ?\ ?\t ?\n)) ;; outside word
|
||||
(looking-back company-nxml-in-attribute-value-regexp nil)
|
||||
(or (match-string-no-properties 4)
|
||||
(match-string-no-properties 5)
|
||||
""))))
|
||||
(candidates (company-nxml-prepared
|
||||
(let (attr-start attr-end colon)
|
||||
(and (looking-back rng-in-attribute-value-regex lt-pos)
|
||||
(setq colon (match-beginning 2)
|
||||
attr-start (match-beginning 1)
|
||||
attr-end (match-end 1))
|
||||
(rng-adjust-state-for-attribute lt-pos attr-start)
|
||||
(rng-adjust-state-for-attribute-value
|
||||
attr-start colon attr-end)
|
||||
(all-completions
|
||||
arg (rng-match-possible-value-strings))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-nxml (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend for `nxml-mode'."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-nxml))
|
||||
(prefix (or (company-nxml-tag 'prefix)
|
||||
(company-nxml-attribute 'prefix)
|
||||
(company-nxml-attribute-value 'prefix)))
|
||||
(candidates (cond
|
||||
((company-nxml-tag 'prefix)
|
||||
(company-nxml-tag 'candidates arg))
|
||||
((company-nxml-attribute 'prefix)
|
||||
(company-nxml-attribute 'candidates arg))
|
||||
((company-nxml-attribute-value 'prefix)
|
||||
(sort (company-nxml-attribute-value 'candidates arg)
|
||||
'string<))))
|
||||
(sorted t)))
|
||||
|
||||
(provide 'company-nxml)
|
||||
;;; company-nxml.el ends here
|
Binary file not shown.
|
@ -0,0 +1,57 @@
|
|||
;;; company-oddmuse.el --- company-mode completion backend for oddmuse-mode
|
||||
|
||||
;; Copyright (C) 2009-2011, 2014 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
(eval-when-compile (require 'yaoddmuse nil t))
|
||||
(eval-when-compile (require 'oddmuse nil t))
|
||||
|
||||
(defvar company-oddmuse-link-regexp
|
||||
"\\(\\<[A-Z][[:alnum:]]*\\>\\)\\|\\[\\[\\([[:alnum:]]+\\>\\|\\)")
|
||||
|
||||
(defun company-oddmuse-get-page-table ()
|
||||
(cl-case major-mode
|
||||
(yaoddmuse-mode (with-no-warnings
|
||||
(yaoddmuse-get-pagename-table yaoddmuse-wikiname)))
|
||||
(oddmuse-mode (with-no-warnings
|
||||
(oddmuse-make-completion-table oddmuse-wiki)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-oddmuse (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend for `oddmuse-mode'."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-oddmuse))
|
||||
(prefix (let ((case-fold-search nil))
|
||||
(and (memq major-mode '(oddmuse-mode yaoddmuse-mode))
|
||||
(looking-back company-oddmuse-link-regexp (point-at-bol))
|
||||
(or (match-string 1)
|
||||
(match-string 2)))))
|
||||
(candidates (all-completions arg (company-oddmuse-get-page-table)))))
|
||||
|
||||
(provide 'company-oddmuse)
|
||||
;;; company-oddmuse.el ends here
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
|||
(define-package "company" "20171122.716" "Modular text completion framework"
|
||||
'((emacs "24.3"))
|
||||
:url "http://company-mode.github.io/" :keywords
|
||||
'("abbrev" "convenience" "matching"))
|
||||
;; Local Variables:
|
||||
;; no-byte-compile: t
|
||||
;; End:
|
|
@ -0,0 +1,167 @@
|
|||
;;; company-semantic.el --- company-mode completion backend using Semantic
|
||||
|
||||
;; Copyright (C) 2009-2011, 2013-2016 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'company-template)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defvar semantic-idle-summary-function)
|
||||
(declare-function semantic-documentation-for-tag "semantic/doc" )
|
||||
(declare-function semantic-analyze-current-context "semantic/analyze")
|
||||
(declare-function semantic-analyze-possible-completions "semantic/complete")
|
||||
(declare-function semantic-analyze-find-tags-by-prefix "semantic/analyze/fcn")
|
||||
(declare-function semantic-tag-class "semantic/tag")
|
||||
(declare-function semantic-tag-name "semantic/tag")
|
||||
(declare-function semantic-tag-start "semantic/tag")
|
||||
(declare-function semantic-tag-buffer "semantic/tag")
|
||||
(declare-function semantic-active-p "semantic")
|
||||
(declare-function semantic-format-tag-prototype "semantic/format")
|
||||
|
||||
(defgroup company-semantic nil
|
||||
"Completion backend using Semantic."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-semantic-metadata-function 'company-semantic-summary-and-doc
|
||||
"The function turning a semantic tag into doc information."
|
||||
:type 'function)
|
||||
|
||||
(defcustom company-semantic-begin-after-member-access t
|
||||
"When non-nil, automatic completion will start whenever the current
|
||||
symbol is preceded by \".\", \"->\" or \"::\", ignoring
|
||||
`company-minimum-prefix-length'.
|
||||
|
||||
If `company-begin-commands' is a list, it should include `c-electric-lt-gt'
|
||||
and `c-electric-colon', for automatic completion right after \">\" and
|
||||
\":\".")
|
||||
|
||||
(defcustom company-semantic-insert-arguments t
|
||||
"When non-nil, insert function arguments as a template after completion."
|
||||
:type 'boolean
|
||||
:package-version '(company . "0.9.0"))
|
||||
|
||||
(defvar company-semantic-modes '(c-mode c++-mode jde-mode java-mode))
|
||||
|
||||
(defvar-local company-semantic--current-tags nil
|
||||
"Tags for the current context.")
|
||||
|
||||
(defun company-semantic-documentation-for-tag (tag)
|
||||
(when (semantic-tag-buffer tag)
|
||||
;; When TAG's buffer is unknown, the function below raises an error.
|
||||
(semantic-documentation-for-tag tag)))
|
||||
|
||||
(defun company-semantic-doc-or-summary (tag)
|
||||
(or (company-semantic-documentation-for-tag tag)
|
||||
(and (require 'semantic-idle nil t)
|
||||
(require 'semantic/idle nil t)
|
||||
(funcall semantic-idle-summary-function tag nil t))))
|
||||
|
||||
(defun company-semantic-summary-and-doc (tag)
|
||||
(let ((doc (company-semantic-documentation-for-tag tag))
|
||||
(summary (funcall semantic-idle-summary-function tag nil t)))
|
||||
(and (stringp doc)
|
||||
(string-match "\n*\\(.*\\)$" doc)
|
||||
(setq doc (match-string 1 doc)))
|
||||
(concat summary
|
||||
(when doc
|
||||
(if (< (+ (length doc) (length summary) 4) (window-width))
|
||||
" -- "
|
||||
"\n"))
|
||||
doc)))
|
||||
|
||||
(defun company-semantic-doc-buffer (tag)
|
||||
(let ((doc (company-semantic-documentation-for-tag tag)))
|
||||
(when doc
|
||||
(company-doc-buffer
|
||||
(concat (funcall semantic-idle-summary-function tag nil t)
|
||||
"\n"
|
||||
doc)))))
|
||||
|
||||
(defsubst company-semantic-completions (prefix)
|
||||
(ignore-errors
|
||||
(let ((completion-ignore-case nil)
|
||||
(context (semantic-analyze-current-context)))
|
||||
(setq company-semantic--current-tags
|
||||
(semantic-analyze-possible-completions context 'no-unique))
|
||||
(all-completions prefix company-semantic--current-tags))))
|
||||
|
||||
(defun company-semantic-completions-raw (prefix)
|
||||
(setq company-semantic--current-tags nil)
|
||||
(dolist (tag (semantic-analyze-find-tags-by-prefix prefix))
|
||||
(unless (eq (semantic-tag-class tag) 'include)
|
||||
(push tag company-semantic--current-tags)))
|
||||
(delete "" (mapcar 'semantic-tag-name company-semantic--current-tags)))
|
||||
|
||||
(defun company-semantic-annotation (argument tags)
|
||||
(let* ((tag (assq argument tags))
|
||||
(kind (when tag (elt tag 1))))
|
||||
(cl-case kind
|
||||
(function (let* ((prototype (semantic-format-tag-prototype tag nil nil))
|
||||
(par-pos (string-match "(" prototype)))
|
||||
(when par-pos (substring prototype par-pos)))))))
|
||||
|
||||
(defun company-semantic--prefix ()
|
||||
(if company-semantic-begin-after-member-access
|
||||
(company-grab-symbol-cons "\\.\\|->\\|::" 2)
|
||||
(company-grab-symbol)))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-semantic (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend using CEDET Semantic."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-semantic))
|
||||
(prefix (and (featurep 'semantic)
|
||||
(semantic-active-p)
|
||||
(memq major-mode company-semantic-modes)
|
||||
(not (company-in-string-or-comment))
|
||||
(or (company-semantic--prefix) 'stop)))
|
||||
(candidates (if (and (equal arg "")
|
||||
(not (looking-back "->\\|\\.\\|::" (- (point) 2))))
|
||||
(company-semantic-completions-raw arg)
|
||||
(company-semantic-completions arg)))
|
||||
(meta (funcall company-semantic-metadata-function
|
||||
(assoc arg company-semantic--current-tags)))
|
||||
(annotation (company-semantic-annotation arg
|
||||
company-semantic--current-tags))
|
||||
(doc-buffer (company-semantic-doc-buffer
|
||||
(assoc arg company-semantic--current-tags)))
|
||||
;; Because "" is an empty context and doesn't return local variables.
|
||||
(no-cache (equal arg ""))
|
||||
(duplicates t)
|
||||
(location (let ((tag (assoc arg company-semantic--current-tags)))
|
||||
(when (buffer-live-p (semantic-tag-buffer tag))
|
||||
(cons (semantic-tag-buffer tag)
|
||||
(semantic-tag-start tag)))))
|
||||
(post-completion (let ((anno (company-semantic-annotation
|
||||
arg company-semantic--current-tags)))
|
||||
(when (and company-semantic-insert-arguments anno)
|
||||
(insert anno)
|
||||
(company-template-c-like-templatify (concat arg anno)))
|
||||
))))
|
||||
|
||||
(provide 'company-semantic)
|
||||
;;; company-semantic.el ends here
|
Binary file not shown.
|
@ -0,0 +1,260 @@
|
|||
;;; company-template.el --- utility library for template expansion
|
||||
|
||||
;; Copyright (C) 2009, 2010, 2014-2017 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
|
||||
(defface company-template-field
|
||||
'((((background dark)) (:background "yellow" :foreground "black"))
|
||||
(((background light)) (:background "orange" :foreground "black")))
|
||||
"Face used for editable text in template fields."
|
||||
:group 'company)
|
||||
|
||||
(defvar company-template-nav-map
|
||||
(let ((keymap (make-sparse-keymap)))
|
||||
(define-key keymap [tab] 'company-template-forward-field)
|
||||
(define-key keymap (kbd "TAB") 'company-template-forward-field)
|
||||
keymap))
|
||||
|
||||
(defvar company-template-field-map
|
||||
(let ((keymap (make-sparse-keymap)))
|
||||
(set-keymap-parent keymap company-template-nav-map)
|
||||
(define-key keymap (kbd "C-d") 'company-template-clear-field)
|
||||
keymap))
|
||||
|
||||
(defvar-local company-template--buffer-templates nil)
|
||||
|
||||
;; interactive ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun company-template-templates-at (pos)
|
||||
(let (os)
|
||||
(dolist (o (overlays-at pos))
|
||||
;; FIXME: Always return the whole list of templates?
|
||||
;; We remove templates not at point after every command.
|
||||
(when (memq o company-template--buffer-templates)
|
||||
(push o os)))
|
||||
os))
|
||||
|
||||
(defun company-template-move-to-first (templ)
|
||||
(interactive)
|
||||
(goto-char (overlay-start templ))
|
||||
(company-template-forward-field))
|
||||
|
||||
(defun company-template-forward-field ()
|
||||
(interactive)
|
||||
(let ((start (point))
|
||||
(next-field-start (company-template-find-next-field)))
|
||||
(push-mark)
|
||||
(goto-char next-field-start)
|
||||
(company-template-remove-field (company-template-field-at start))))
|
||||
|
||||
(defun company-template-clear-field ()
|
||||
"Clear the field at point."
|
||||
(interactive)
|
||||
(let ((ovl (company-template-field-at (point))))
|
||||
(when ovl
|
||||
(company-template-remove-field ovl t)
|
||||
(let ((after-clear-fn
|
||||
(overlay-get ovl 'company-template-after-clear)))
|
||||
(when (functionp after-clear-fn)
|
||||
(funcall after-clear-fn))))))
|
||||
|
||||
(defun company-template--after-clear-c-like-field ()
|
||||
"Function that can be called after deleting a field of a c-like template.
|
||||
For c-like templates it is set as `after-post-fn' property on fields in
|
||||
`company-template-add-field'. If there is a next field, delete everything
|
||||
from point to it. If there is no field after point, remove preceding comma
|
||||
if present."
|
||||
(let* ((pos (point))
|
||||
(next-field-start (company-template-find-next-field))
|
||||
(last-field-p (not (company-template-field-at next-field-start))))
|
||||
(cond ((and (not last-field-p)
|
||||
(< pos next-field-start)
|
||||
(string-match "^[ ]*,+[ ]*$" (buffer-substring-no-properties
|
||||
pos next-field-start)))
|
||||
(delete-region pos next-field-start))
|
||||
((and last-field-p
|
||||
(looking-back ",+[ ]*" (line-beginning-position)))
|
||||
(delete-region (match-beginning 0) pos)))))
|
||||
|
||||
(defun company-template-find-next-field ()
|
||||
(let* ((start (point))
|
||||
(templates (company-template-templates-at start))
|
||||
(minimum (apply 'max (mapcar 'overlay-end templates)))
|
||||
(fields (cl-loop for templ in templates
|
||||
append (overlay-get templ 'company-template-fields))))
|
||||
(dolist (pos (mapcar 'overlay-start fields) minimum)
|
||||
(and pos
|
||||
(> pos start)
|
||||
(< pos minimum)
|
||||
(setq minimum pos)))))
|
||||
|
||||
(defun company-template-field-at (&optional point)
|
||||
(cl-loop for ovl in (overlays-at (or point (point)))
|
||||
when (overlay-get ovl 'company-template-parent)
|
||||
return ovl))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun company-template-declare-template (beg end)
|
||||
(let ((ov (make-overlay beg end)))
|
||||
;; (overlay-put ov 'face 'highlight)
|
||||
(overlay-put ov 'keymap company-template-nav-map)
|
||||
(overlay-put ov 'priority 101)
|
||||
(overlay-put ov 'evaporate t)
|
||||
(push ov company-template--buffer-templates)
|
||||
(add-hook 'post-command-hook 'company-template-post-command nil t)
|
||||
ov))
|
||||
|
||||
(defun company-template-remove-template (templ)
|
||||
(mapc 'company-template-remove-field
|
||||
(overlay-get templ 'company-template-fields))
|
||||
(setq company-template--buffer-templates
|
||||
(delq templ company-template--buffer-templates))
|
||||
(delete-overlay templ))
|
||||
|
||||
(defun company-template-add-field (templ beg end &optional display after-clear-fn)
|
||||
"Add new field to template TEMPL spanning from BEG to END.
|
||||
When DISPLAY is non-nil, set the respective property on the overlay.
|
||||
Leave point at the end of the field.
|
||||
AFTER-CLEAR-FN is a function that can be used to apply custom behavior
|
||||
after deleting a field in `company-template-remove-field'."
|
||||
(cl-assert templ)
|
||||
(when (> end (overlay-end templ))
|
||||
(move-overlay templ (overlay-start templ) end))
|
||||
(let ((ov (make-overlay beg end))
|
||||
(siblings (overlay-get templ 'company-template-fields)))
|
||||
;; (overlay-put ov 'evaporate t)
|
||||
(overlay-put ov 'intangible t)
|
||||
(overlay-put ov 'face 'company-template-field)
|
||||
(when display
|
||||
(overlay-put ov 'display display))
|
||||
(overlay-put ov 'company-template-parent templ)
|
||||
(overlay-put ov 'insert-in-front-hooks '(company-template-insert-hook))
|
||||
(when after-clear-fn
|
||||
(overlay-put ov 'company-template-after-clear after-clear-fn))
|
||||
(overlay-put ov 'keymap company-template-field-map)
|
||||
(overlay-put ov 'priority 101)
|
||||
(push ov siblings)
|
||||
(overlay-put templ 'company-template-fields siblings)))
|
||||
|
||||
(defun company-template-remove-field (ovl &optional clear)
|
||||
(when (overlayp ovl)
|
||||
(when (overlay-buffer ovl)
|
||||
(when clear
|
||||
(delete-region (overlay-start ovl) (overlay-end ovl)))
|
||||
(delete-overlay ovl))
|
||||
(let* ((templ (overlay-get ovl 'company-template-parent))
|
||||
(siblings (overlay-get templ 'company-template-fields)))
|
||||
(setq siblings (delq ovl siblings))
|
||||
(overlay-put templ 'company-template-fields siblings))))
|
||||
|
||||
(defun company-template-clean-up (&optional pos)
|
||||
"Clean up all templates that don't contain POS."
|
||||
(let ((local-ovs (overlays-at (or pos (point)))))
|
||||
(dolist (templ company-template--buffer-templates)
|
||||
(unless (memq templ local-ovs)
|
||||
(company-template-remove-template templ)))))
|
||||
|
||||
;; hooks ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun company-template-insert-hook (ovl after-p &rest _ignore)
|
||||
"Called when a snippet input prompt is modified."
|
||||
(unless after-p
|
||||
(company-template-remove-field ovl t)))
|
||||
|
||||
(defun company-template-post-command ()
|
||||
(company-template-clean-up)
|
||||
(unless company-template--buffer-templates
|
||||
(remove-hook 'post-command-hook 'company-template-post-command t)))
|
||||
|
||||
;; common ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun company-template-c-like-templatify (call)
|
||||
(let* ((end (point-marker))
|
||||
(beg (- (point) (length call)))
|
||||
(templ (company-template-declare-template beg end))
|
||||
paren-open paren-close)
|
||||
(with-syntax-table (make-syntax-table (syntax-table))
|
||||
(modify-syntax-entry ?< "(")
|
||||
(modify-syntax-entry ?> ")")
|
||||
(when (search-backward ")" beg t)
|
||||
(setq paren-close (point-marker))
|
||||
(forward-char 1)
|
||||
(delete-region (point) end)
|
||||
(backward-sexp)
|
||||
(forward-char 1)
|
||||
(setq paren-open (point-marker)))
|
||||
(when (search-backward ">" beg t)
|
||||
(let ((angle-close (point-marker)))
|
||||
(forward-char 1)
|
||||
(backward-sexp)
|
||||
(forward-char)
|
||||
(company-template--c-like-args templ angle-close)))
|
||||
(when (looking-back "\\((\\*)\\)(" (line-beginning-position))
|
||||
(delete-region (match-beginning 1) (match-end 1)))
|
||||
(when paren-open
|
||||
(goto-char paren-open)
|
||||
(company-template--c-like-args templ paren-close)))
|
||||
(if (overlay-get templ 'company-template-fields)
|
||||
(company-template-move-to-first templ)
|
||||
(company-template-remove-template templ)
|
||||
(goto-char end))))
|
||||
|
||||
(defun company-template--c-like-args (templ end)
|
||||
(let ((last-pos (point)))
|
||||
(while (re-search-forward "\\([^,]+\\),?" end 'move)
|
||||
(when (zerop (car (parse-partial-sexp last-pos (point))))
|
||||
(company-template-add-field templ last-pos (match-end 1) nil
|
||||
#'company-template--after-clear-c-like-field)
|
||||
(skip-chars-forward " ")
|
||||
(setq last-pos (point))))))
|
||||
|
||||
;; objc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun company-template-objc-templatify (selector)
|
||||
(let* ((end (point-marker))
|
||||
(beg (- (point) (length selector) 1))
|
||||
(templ (company-template-declare-template beg end))
|
||||
(cnt 0))
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(catch 'stop
|
||||
(while (search-forward ":" end t)
|
||||
(if (looking-at "\\(([^)]*)\\) ?")
|
||||
(company-template-add-field templ (point) (match-end 1))
|
||||
;; Not sure which conditions this case manifests under, but
|
||||
;; apparently it did before, when I wrote the first test for this
|
||||
;; function. FIXME: Revisit it.
|
||||
(company-template-add-field templ (point)
|
||||
(progn
|
||||
(insert (format "arg%d" cnt))
|
||||
(point)))
|
||||
(when (< (point) end)
|
||||
(insert " "))
|
||||
(cl-incf cnt))
|
||||
(when (>= (point) end)
|
||||
(throw 'stop t)))))
|
||||
(company-template-move-to-first templ)))
|
||||
|
||||
(provide 'company-template)
|
||||
;;; company-template.el ends here
|
Binary file not shown.
|
@ -0,0 +1,71 @@
|
|||
;;; company-tempo.el --- company-mode completion backend for tempo
|
||||
|
||||
;; Copyright (C) 2009-2011, 2015 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
(require 'tempo)
|
||||
|
||||
(defgroup company-tempo nil
|
||||
"Tempo completion backend."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-tempo-expand nil
|
||||
"Whether to expand a tempo tag after completion."
|
||||
:type '(choice (const :tag "Off" nil)
|
||||
(const :tag "On" t)))
|
||||
|
||||
(defsubst company-tempo-lookup (match)
|
||||
(cdr (assoc match (tempo-build-collection))))
|
||||
|
||||
(defun company-tempo-insert (match)
|
||||
"Replace MATCH with the expanded tempo template."
|
||||
(search-backward match)
|
||||
(goto-char (match-beginning 0))
|
||||
(replace-match "")
|
||||
(call-interactively (company-tempo-lookup match)))
|
||||
|
||||
(defsubst company-tempo-meta (match)
|
||||
(let ((templ (company-tempo-lookup match))
|
||||
doc)
|
||||
(and templ
|
||||
(setq doc (documentation templ t))
|
||||
(car (split-string doc "\n" t)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-tempo (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend for tempo."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-tempo))
|
||||
(prefix (or (car (tempo-find-match-string tempo-match-finder)) ""))
|
||||
(candidates (all-completions arg (tempo-build-collection)))
|
||||
(meta (company-tempo-meta arg))
|
||||
(post-completion (when company-tempo-expand (company-tempo-insert arg)))
|
||||
(sorted t)))
|
||||
|
||||
(provide 'company-tempo)
|
||||
;;; company-tempo.el ends here
|
Binary file not shown.
|
@ -0,0 +1,159 @@
|
|||
;;; company-tng.el --- company-mode configuration for single-button interaction
|
||||
|
||||
;; Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikita Leshenko
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; company-tng (Tab and Go) allows you to perform completion using just TAB.
|
||||
;; Pressing it will both select the next completion candidate in the list and
|
||||
;; insert it into the buffer (or make it look like it's inserted, in fact).
|
||||
;;
|
||||
;; It cycles the candidates like `yank-pop' or `dabbrev-expand' or Vim:
|
||||
;; Pressing TAB selects the first item in the completion menu and inserts it in
|
||||
;; the buffer. Pressing TAB again selects the second item and replaces the
|
||||
;; "inserted" item with the second one. This can continue as long as the user
|
||||
;; wishes to cycle through the menu. You can also press S-TAB to select the
|
||||
;; previous candidate, of course.
|
||||
;;
|
||||
;; The benefits are that you only have to use one shortcut key and there is no
|
||||
;; need to confirm the entry.
|
||||
;;
|
||||
;; Usage:
|
||||
;;
|
||||
;; To apply the default configuration for company-tng call
|
||||
;; `company-tng-configure-default' from your init script.
|
||||
;;
|
||||
;; You can also configure company-tng manually:
|
||||
;;
|
||||
;; Add `company-tng-frontend' to `company-frontends':
|
||||
;;
|
||||
;; (add-to-list 'company-frontends 'company-tng-frontend)
|
||||
;;
|
||||
;; We recommend to bind TAB to `company-select-next', S-TAB to
|
||||
;; `company-select-previous', and unbind RET and other now-unnecessary
|
||||
;; keys from `company-active-map':
|
||||
;;
|
||||
;; (define-key company-active-map (kbd "TAB") 'company-select-next)
|
||||
;; (define-key company-active-map (kbd "<backtab>") 'company-select-previous)
|
||||
;; (define-key company-active-map (kbd "RET") nil)
|
||||
;;
|
||||
;; Note that it's not necessary to rebind keys to use this frontend,
|
||||
;; you can use the arrow keys or M-n/M-p to select and insert
|
||||
;; candidates. You also need to decide which keys to unbind, depending
|
||||
;; on whether you want them to do the Company action or the default
|
||||
;; Emacs action (for example C-s or C-w).
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defvar-local company-tng--overlay nil)
|
||||
|
||||
;;;###autoload
|
||||
(defun company-tng-frontend (command)
|
||||
"When the user changes the selection at least once, this
|
||||
frontend will display the candidate in the buffer as if it's
|
||||
already there and any key outside of `company-active-map' will
|
||||
confirm the selection and finish the completion."
|
||||
(cl-case command
|
||||
(show
|
||||
(let ((ov (make-overlay (point) (point))))
|
||||
(setq company-tng--overlay ov)
|
||||
(overlay-put ov 'priority 2))
|
||||
(advice-add 'company-select-next :before-until 'company-tng--allow-unselected)
|
||||
(advice-add 'company-fill-propertize :filter-args 'company-tng--adjust-tooltip-highlight))
|
||||
(update
|
||||
(let ((ov company-tng--overlay)
|
||||
(selected (nth company-selection company-candidates))
|
||||
(prefix (length company-prefix)))
|
||||
(move-overlay ov (- (point) prefix) (point))
|
||||
(overlay-put ov
|
||||
(if (= prefix 0) 'after-string 'display)
|
||||
(and company-selection-changed selected))))
|
||||
(hide
|
||||
(when company-tng--overlay
|
||||
(delete-overlay company-tng--overlay)
|
||||
(kill-local-variable 'company-tng--overlay))
|
||||
(advice-remove 'company-select-next 'company-tng--allow-unselected)
|
||||
(advice-remove 'company-fill-propertize 'company-tng--adjust-tooltip-highlight))
|
||||
(pre-command
|
||||
(when (and company-selection-changed
|
||||
(not (company--company-command-p (this-command-keys))))
|
||||
(company--unread-this-command-keys)
|
||||
(setq this-command 'company-complete-selection)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-tng-configure-default ()
|
||||
"Applies the default configuration to enable company-tng."
|
||||
(setq company-frontends '(company-tng-frontend
|
||||
company-pseudo-tooltip-frontend
|
||||
company-echo-metadata-frontend))
|
||||
(let ((keymap company-active-map))
|
||||
(define-key keymap [return] nil)
|
||||
(define-key keymap (kbd "RET") nil)
|
||||
(define-key keymap [tab] 'company-select-next)
|
||||
(define-key keymap (kbd "TAB") 'company-select-next)
|
||||
(define-key keymap [backtab] 'company-select-previous)
|
||||
(define-key keymap (kbd "S-TAB") 'company-select-previous)))
|
||||
|
||||
(defun company-tng--allow-unselected (&optional arg)
|
||||
"Advice `company-select-next' to allow for an 'unselected'
|
||||
state. Unselected means that no user interaction took place on the
|
||||
completion candidates and it's marked by setting
|
||||
`company-selection-changed' to nil. This advice will call the underlying
|
||||
`company-select-next' unless we need to transition to or from an unselected
|
||||
state.
|
||||
|
||||
Possible state transitions:
|
||||
- (arg > 0) unselected -> first candidate selected
|
||||
- (arg < 0) first candidate selected -> unselected
|
||||
- (arg < 0 wrap-round) unselected -> last candidate selected
|
||||
- (arg < 0 no wrap-round) unselected -> unselected
|
||||
|
||||
There is no need to advice `company-select-previous' because it calls
|
||||
`company-select-next' internally."
|
||||
(cond
|
||||
;; Selecting next
|
||||
((or (not arg) (> arg 0))
|
||||
(unless company-selection-changed
|
||||
(company-set-selection (1- (or arg 1)) 'force-update)
|
||||
t))
|
||||
;; Selecting previous
|
||||
((< arg 0)
|
||||
(when (and company-selection-changed
|
||||
(< (+ company-selection arg) 0))
|
||||
(company-set-selection 0)
|
||||
(setq company-selection-changed nil)
|
||||
(company-call-frontends 'update)
|
||||
t)
|
||||
)))
|
||||
|
||||
(defun company-tng--adjust-tooltip-highlight (args)
|
||||
"Prevent the tooltip from highlighting the current selection if it wasn't
|
||||
made explicitly (i.e. `company-selection-changed' is true)"
|
||||
(unless company-selection-changed
|
||||
;; The 4th arg of `company-fill-propertize' is selected
|
||||
(setf (nth 3 args) nil))
|
||||
args)
|
||||
|
||||
(provide 'company-tng)
|
||||
;;; company-tng.el ends here
|
Binary file not shown.
|
@ -0,0 +1,123 @@
|
|||
;;; company-xcode.el --- company-mode completion backend for Xcode projects
|
||||
|
||||
;; Copyright (C) 2009-2011, 2014 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Nikolaj Schumacher
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
|
||||
(defgroup company-xcode nil
|
||||
"Completion backend for Xcode projects."
|
||||
:group 'company)
|
||||
|
||||
(defcustom company-xcode-xcodeindex-executable (executable-find "xcodeindex")
|
||||
"Location of xcodeindex executable."
|
||||
:type 'file)
|
||||
|
||||
(defvar company-xcode-tags nil)
|
||||
|
||||
(defun company-xcode-reset ()
|
||||
"Reset the cached tags."
|
||||
(interactive)
|
||||
(setq company-xcode-tags nil))
|
||||
|
||||
(defcustom company-xcode-types
|
||||
'("Class" "Constant" "Enum" "Macro" "Modeled Class" "Structure"
|
||||
"Type" "Union" "Function")
|
||||
"The types of symbols offered by `company-xcode'.
|
||||
No context-enabled completion is available. Types like methods will be
|
||||
offered regardless of whether the class supports them. The defaults should be
|
||||
valid in most contexts."
|
||||
:set (lambda (variable value)
|
||||
(set variable value)
|
||||
(company-xcode-reset))
|
||||
:type '(set (const "Category") (const "Class") (const "Class Method")
|
||||
(const "Class Variable") (const "Constant") (const "Enum")
|
||||
(const "Field") (const "Instance Method")
|
||||
(const "Instance Variable") (const "Macro")
|
||||
(const "Modeled Class") (const "Modeled Method")
|
||||
(const "Modeled Property") (const "Property") (const "Protocol")
|
||||
(const "Structure") (const "Type") (const "Union")
|
||||
(const "Variable") (const "Function")))
|
||||
|
||||
(defvar-local company-xcode-project 'unknown)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun company-xcode-fetch (project-bundle)
|
||||
(setq project-bundle (directory-file-name project-bundle))
|
||||
(message "Retrieving dump from %s..." project-bundle)
|
||||
(with-temp-buffer
|
||||
(let ((default-directory (file-name-directory project-bundle)))
|
||||
(call-process company-xcode-xcodeindex-executable nil (current-buffer)
|
||||
nil "dump" "-project"
|
||||
(file-name-nondirectory project-bundle) "-quiet")
|
||||
(goto-char (point-min))
|
||||
(let ((regexp (concat "^\\([^\t\n]*\\)\t[^\t\n]*\t"
|
||||
(regexp-opt company-xcode-types)
|
||||
"\t[^\t\n]*\t[^\t\n]*"))
|
||||
candidates)
|
||||
(while (re-search-forward regexp nil t)
|
||||
(cl-pushnew (match-string 1) candidates :test #'equal))
|
||||
(message "Retrieving dump from %s...done" project-bundle)
|
||||
candidates))))
|
||||
|
||||
(defun company-xcode-find-project ()
|
||||
(let ((dir (if buffer-file-name
|
||||
(file-name-directory buffer-file-name)
|
||||
(expand-file-name default-directory)))
|
||||
(prev-dir nil)
|
||||
file)
|
||||
(while (not (or file (equal dir prev-dir)))
|
||||
(setq file (car (directory-files dir t ".xcodeproj\\'" t))
|
||||
prev-dir dir
|
||||
dir (file-name-directory (directory-file-name dir))))
|
||||
file))
|
||||
|
||||
(defun company-xcode-tags ()
|
||||
(when (eq company-xcode-project 'unknown)
|
||||
(setq company-xcode-project (company-xcode-find-project)))
|
||||
(when company-xcode-project
|
||||
(cdr (or (assoc company-xcode-project company-xcode-tags)
|
||||
(car (push (cons company-xcode-project
|
||||
(company-xcode-fetch company-xcode-project))
|
||||
company-xcode-tags))))))
|
||||
;;;###autoload
|
||||
(defun company-xcode (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend for Xcode projects."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-xcode))
|
||||
(prefix (and company-xcode-xcodeindex-executable
|
||||
(company-xcode-tags)
|
||||
(not (company-in-string-or-comment))
|
||||
(or (company-grab-symbol) 'stop)))
|
||||
(candidates (let ((completion-ignore-case nil))
|
||||
(company-xcode-tags)
|
||||
(all-completions arg (company-xcode-tags))))))
|
||||
|
||||
|
||||
(provide 'company-xcode)
|
||||
;;; company-xcode.el ends here
|
Binary file not shown.
|
@ -0,0 +1,147 @@
|
|||
;;; company-yasnippet.el --- company-mode completion backend for Yasnippet
|
||||
|
||||
;; Copyright (C) 2014, 2015 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Dmitry Gutov
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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.
|
||||
|
||||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'company)
|
||||
(require 'cl-lib)
|
||||
|
||||
(declare-function yas--table-hash "yasnippet")
|
||||
(declare-function yas--get-snippet-tables "yasnippet")
|
||||
(declare-function yas-expand-snippet "yasnippet")
|
||||
(declare-function yas--template-content "yasnippet")
|
||||
(declare-function yas--template-expand-env "yasnippet")
|
||||
(declare-function yas--warning "yasnippet")
|
||||
|
||||
(defun company-yasnippet--key-prefixes ()
|
||||
;; Mostly copied from `yas--templates-for-key-at-point'.
|
||||
(defvar yas-key-syntaxes)
|
||||
(save-excursion
|
||||
(let ((original (point))
|
||||
(methods yas-key-syntaxes)
|
||||
prefixes
|
||||
method)
|
||||
(while methods
|
||||
(unless (eq method (car methods))
|
||||
(goto-char original))
|
||||
(setq method (car methods))
|
||||
(cond ((stringp method)
|
||||
(skip-syntax-backward method)
|
||||
(setq methods (cdr methods)))
|
||||
((functionp method)
|
||||
(unless (eq (funcall method original)
|
||||
'again)
|
||||
(setq methods (cdr methods))))
|
||||
(t
|
||||
(setq methods (cdr methods))
|
||||
(yas--warning "Invalid element `%s' in `yas-key-syntaxes'" method)))
|
||||
(let ((prefix (buffer-substring-no-properties (point) original)))
|
||||
(unless (equal prefix (car prefixes))
|
||||
(push prefix prefixes))))
|
||||
prefixes)))
|
||||
|
||||
(defun company-yasnippet--candidates (prefix)
|
||||
;; Process the prefixes in reverse: unlike Yasnippet, we look for prefix
|
||||
;; matches, so the longest prefix with any matches should be the most useful.
|
||||
(cl-loop with tables = (yas--get-snippet-tables)
|
||||
for key-prefix in (company-yasnippet--key-prefixes)
|
||||
;; Only consider keys at least as long as the symbol at point.
|
||||
when (>= (length key-prefix) (length prefix))
|
||||
thereis (company-yasnippet--completions-for-prefix prefix
|
||||
key-prefix
|
||||
tables)))
|
||||
|
||||
(defun company-yasnippet--completions-for-prefix (prefix key-prefix tables)
|
||||
(cl-mapcan
|
||||
(lambda (table)
|
||||
(let ((keyhash (yas--table-hash table))
|
||||
res)
|
||||
(when keyhash
|
||||
(maphash
|
||||
(lambda (key value)
|
||||
(when (and (stringp key)
|
||||
(string-prefix-p key-prefix key))
|
||||
(maphash
|
||||
(lambda (name template)
|
||||
(push
|
||||
(propertize key
|
||||
'yas-annotation name
|
||||
'yas-template template
|
||||
'yas-prefix-offset (- (length key-prefix)
|
||||
(length prefix)))
|
||||
res))
|
||||
value)))
|
||||
keyhash))
|
||||
res))
|
||||
tables))
|
||||
|
||||
;;;###autoload
|
||||
(defun company-yasnippet (command &optional arg &rest ignore)
|
||||
"`company-mode' backend for `yasnippet'.
|
||||
|
||||
This backend should be used with care, because as long as there are
|
||||
snippets defined for the current major mode, this backend will always
|
||||
shadow backends that come after it. Recommended usages:
|
||||
|
||||
* In a buffer-local value of `company-backends', grouped with a backend or
|
||||
several that provide actual text completions.
|
||||
|
||||
(add-hook 'js-mode-hook
|
||||
(lambda ()
|
||||
(set (make-local-variable 'company-backends)
|
||||
'((company-dabbrev-code company-yasnippet)))))
|
||||
|
||||
* After keyword `:with', grouped with other backends.
|
||||
|
||||
(push '(company-semantic :with company-yasnippet) company-backends)
|
||||
|
||||
* Not in `company-backends', just bound to a key.
|
||||
|
||||
(global-set-key (kbd \"C-c y\") 'company-yasnippet)
|
||||
"
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-yasnippet))
|
||||
(prefix
|
||||
;; Should probably use `yas--current-key', but that's bound to be slower.
|
||||
;; How many trigger keys start with non-symbol characters anyway?
|
||||
(and (bound-and-true-p yas-minor-mode)
|
||||
(company-grab-symbol)))
|
||||
(annotation
|
||||
(concat
|
||||
(unless company-tooltip-align-annotations " -> ")
|
||||
(get-text-property 0 'yas-annotation arg)))
|
||||
(candidates (company-yasnippet--candidates arg))
|
||||
(no-cache t)
|
||||
(post-completion
|
||||
(let ((template (get-text-property 0 'yas-template arg))
|
||||
(prefix-offset (get-text-property 0 'yas-prefix-offset arg)))
|
||||
(yas-expand-snippet (yas--template-content template)
|
||||
(- (point) (length arg) prefix-offset)
|
||||
(point)
|
||||
(yas--template-expand-env template))))))
|
||||
|
||||
(provide 'company-yasnippet)
|
||||
;;; company-yasnippet.el ends here
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -0,0 +1,521 @@
|
|||
New in Elpy 1.17.0
|
||||
==================
|
||||
|
||||
- The xref functionality in newer Emacsen is now supported for
|
||||
following symbols at point.
|
||||
- Elpy now supports PEP 397 for Windows executable names.
|
||||
- In addition to pylint, Elpy now also supports epylint correctly.
|
||||
- A number of features for working with interactive Python have been added to
|
||||
Elpy; e.g., commands for sending code fragments to the Python shell and the
|
||||
ability to echo their output in the message area. See the documentation for a
|
||||
full list.
|
||||
- Bunch of little bugfixes.
|
||||
|
||||
|
||||
New in Elpy 1.16.0
|
||||
==================
|
||||
|
||||
- You can now change which function is used to run test commands,
|
||||
instead of the default ``compile``, using
|
||||
``elpy-test-compilation-function``. This allows using ``pdb`` to run
|
||||
tests in a debugger, for example.
|
||||
- Elpy now sets ``IPY_TEST_SIMPLE_PROMPT``, which should prevent a
|
||||
number of problems with IPython 5.
|
||||
- If you like Elpy, you can now sponsor its development using Patreon
|
||||
at https://www.patreon.com/jorgenschaefer
|
||||
|
||||
|
||||
New in Elpy 1.15.0
|
||||
==================
|
||||
|
||||
- Elpy now supports profiling, try ``elpy-profile-buffer-or-region``!
|
||||
- Do not randomly downcase completion candidates anymore.
|
||||
- Work around a bug in Emacs 25.1 and before related to current
|
||||
IPython shells.
|
||||
- And lots of other bugfixes.
|
||||
|
||||
|
||||
New in Elpy 1.14.0
|
||||
==================
|
||||
|
||||
- Basic Django support. Try ``C-c C-x c`` and ``C-c C-x r``! Thanks to
|
||||
Daniel Gopar for this.
|
||||
- You can now use manage.py to run Django tests, instead of always
|
||||
using django-admin.py.
|
||||
- When called with a prefix argument ``elpy-importmagic-add-import``
|
||||
will now ask for an alias name to import as.
|
||||
|
||||
|
||||
New in Elpy 1.13.0
|
||||
==================
|
||||
|
||||
- Fewer surprises with syntax checks, ``C-c C-z``, reformatting
|
||||
- Improved behavior for reformatting.
|
||||
- Improved documentation for IPython. IPython 5 broke a lot of things
|
||||
with Emacs. Use it at your own risk.
|
||||
|
||||
|
||||
New in Elpy 1.12.0
|
||||
==================
|
||||
|
||||
- Some symbols can now be completed with parentheses after them,
|
||||
useful for functions. The heuristic for when to add parentheses and
|
||||
not is not optimal, so this is not enabled by default—customize
|
||||
``elpy-company-post-completion-function`` to enable this feature.
|
||||
- Support dedicated Python shells (one per Python file). You can
|
||||
customized ``elpy-dedicated-shells`` to make this the default.
|
||||
- Elpy now uses ``python -m pip`` instead of pip directly to improve
|
||||
interaction with virtualenvs.
|
||||
- Support for Python 2.6 has been officially dropped.
|
||||
- Display for backend errors has been disabled by default. Jedi has
|
||||
not had a release in a long while, and many bugs have been unfixed.
|
||||
These errors have only caused added workload for Elpy maintainers.
|
||||
The option will be reverted once Jedi has had a new release.
|
||||
|
||||
|
||||
New in Elpy 1.11.0
|
||||
==================
|
||||
|
||||
- Elpy now supports yapf to format your code.
|
||||
- You can now adjust whether Elpy should hide modes from the mode line
|
||||
or not using ``elpy-remove-modeline-lighter``
|
||||
- When the new option ``elpy-disable-backend-error-display`` is set,
|
||||
Elpy will not show its error pop-up anymore. This can be useful if
|
||||
you run into an annoying bug in Jedi, for example.
|
||||
- New command ``elpy-goto-definition-other-window`` on ``C-x 4 M-.``.
|
||||
- Expanding ``super`` now gives the short form supported in Python 3.
|
||||
- All Rope errors are now caught, as the upstream maintainers did not
|
||||
show interest in distinguishing between malformed input and bugs in
|
||||
their library.
|
||||
|
||||
|
||||
New in Elpy 1.10.0
|
||||
==================
|
||||
|
||||
- Marking the current indentation level at the top level will now mark
|
||||
the whole buffer.
|
||||
- The region will be normalized before re-indenting it, making the
|
||||
behavior more predictable for partially marked lines.
|
||||
- Using autopep8 on the whole buffer will now keep point (roughly) at
|
||||
the same location as it was.
|
||||
- The autopep8 code now also uses the same configuration options as
|
||||
the command line tool.
|
||||
- Malformed JSON data from the backend is now handled better.
|
||||
- RPC processes are restarted when the current virtualenv changes.
|
||||
- Python 3.5 is now officially supported.
|
||||
- Flymake will now not be enabled in buffers without file name, where
|
||||
it can't run anyhow, or when the checker program does not exist in
|
||||
the first place.
|
||||
- It is now possible to ask Elpy not to remove the mode line lighter
|
||||
of company mode, which can be useful if you use company in other
|
||||
modes.
|
||||
- Test discovery now also allows file names without "test" in them to
|
||||
be tested. Classes and methods still require the substring, though.
|
||||
- Spurious equals signs at the end of completions from Jedi will now
|
||||
be ignored.
|
||||
- Various other bug fixes.
|
||||
|
||||
|
||||
New in Elpy 1.9.0
|
||||
=================
|
||||
|
||||
- Elpy now supports the ``autopep8`` library for automatically
|
||||
formatting Python code. All refactoring-related code is now grouped
|
||||
under ``C-c C-r``. Use ``C-c C-r i`` to fix up imports using
|
||||
importmagic, ``C-c C-r p`` to fix up Python code with autopep8, and
|
||||
``C-c C-r r`` to bring up the old Rope refactoring menu.
|
||||
- ``C-c C-b`` will now select a region containing surrounding lines of
|
||||
the current indentation or more.
|
||||
- ``C-c C-z`` in a Python shell will now switch back to the last
|
||||
Python buffer, allowing to use the key to cycle back and forth
|
||||
between the Python buffer and shell.
|
||||
- The pattern used for ``C-c C-s`` is now customizeable in
|
||||
``elpy-rgrep-file-pattern``.
|
||||
- ``<C-return>`` now can be used to send the current statement to the
|
||||
Python shell. Be careful, this can break with nested statements.
|
||||
- The Elpy minor mode now also works in modes derived from
|
||||
``python-mode``, not just in the mode itself.
|
||||
|
||||
|
||||
New in Elpy 1.8.1
|
||||
=================
|
||||
|
||||
- Ignore a ``KeyError`` from Jedi 0.9.0 which occurs a lot in normal
|
||||
code.
|
||||
|
||||
|
||||
New in Elpy 1.8.0
|
||||
=================
|
||||
|
||||
- Emacs 24.5 is now officially supported
|
||||
- The new configuration option ``elpy-rpc-ignored-buffer-size`` defines a maximum buffer size to be handle completion in, to avoid laggy interaction in unusually large files
|
||||
- Indentation block movement was replaced with code that just moves the marked block or the current line; this should be a lot less magical and more predictable
|
||||
- Running the test at point now correctly ignores any inner methods
|
||||
- Jedi docstrings now show the full name of the object
|
||||
- The RPC interpreter is now chosen correctly on cygwin
|
||||
- ``elpy-shell-send-region-or-buffer`` now warns of tabs in the data being sent
|
||||
- Elpy now binds stdout and stderr to ``/dev/null`` to avoid being confused by spurious output from other libraries
|
||||
- RPC buffers (and processes) are removed after some time to avoid them piling up endlessly
|
||||
- It is not possibly anymore to use customize alone to use ipython, because of some bad interaction between custom options in Elpy and python.el
|
||||
- And lots of bugfixes (50 issues closed!)
|
||||
|
||||
|
||||
New in Elpy 1.7.1
|
||||
=================
|
||||
|
||||
- Do not fail on errors from importmagic.
|
||||
- Handle new minor mode behavior of new versions of yasnippet.
|
||||
- Do use the argument to ``elpy-use-ipython`` correctly.
|
||||
- Handle unexpected data from the backend more gracefully.
|
||||
|
||||
|
||||
New in Elpy 1.7.0
|
||||
=================
|
||||
|
||||
- Elpy now can add missing import directives automatically, by using
|
||||
Alec Thomas' excellent importmagic_ library. Use ``C-c C-m`` to add
|
||||
a single import statement, or ``C-c C-S-m`` to include all missing
|
||||
import statements. Many thanks to Georg Brandl for doing a lot of
|
||||
work to bring this feature to Elpy!
|
||||
- The Jedi backend now also supports ``C-c C-d`` to display a
|
||||
docstring. Thanks again to Georg Brandl for the patch.
|
||||
- It is now possible to disable the display of the current function in
|
||||
the echo area by setting ``elpy-eldoc-show-current-function`` to
|
||||
``nil``.
|
||||
- idomenu was removed.
|
||||
- Twisted's Trial test runner is now supported. Thanks to Elric Milon
|
||||
for the patch!
|
||||
- All test runners now use a variable to decide which command to run,
|
||||
which for example allows using ``manage.py`` for the Django test
|
||||
runner, or your own test script which sets up the environment
|
||||
correctly.
|
||||
- Emacs 24.4 is now officially supported.
|
||||
- Various bugfixes.
|
||||
|
||||
.. _importmagic: https://github.com/alecthomas/importmagic
|
||||
|
||||
New in Elpy 1.6.0
|
||||
=================
|
||||
|
||||
- When point is on a line with a flymake error, Elpy will now show the
|
||||
error in the echo area.
|
||||
- The movement commands (``C-<cursor>``) have been reworked again.
|
||||
Going left and right will now move by indentation levels left of the
|
||||
current indentation, i.e. jump four spaces, and by words right of
|
||||
the current indentation. Going up and down will go to the previous
|
||||
or next line with the indentation level point is at, not the
|
||||
indentation the line has. Try it, it's more difficult to explain
|
||||
than to use.
|
||||
- Completion results are now sorted more sensibly, with
|
||||
single-underscore symbols at the end, and double-underscore symbols
|
||||
after normal symbols, but before single-underscore ones.
|
||||
- ``M-x elpy-config`` will now point out if there are newer versions
|
||||
available for packages used by Elpy.
|
||||
- ``M-x elpy-config`` will now warn if ``~/.local/bin`` is not in
|
||||
``PATH`` while there is no virtualenv active.
|
||||
- The ``M-x elpy-version`` command is back by popular demand.
|
||||
- RPC buffers used by Elpy are now hidden by default, having a space
|
||||
at the beginning of the name.
|
||||
- When the Rope library throws an error, Elpy will now also attempt to
|
||||
provide reproduction steps. This used to only happen for Jedi.
|
||||
- Various bug fixes.
|
||||
|
||||
|
||||
New in Elpy 1.5.1
|
||||
=================
|
||||
|
||||
- Fix a bug where company-mode might get confused about the current
|
||||
backend, leading to an error about ``Symbol's function definition is
|
||||
void: nil``
|
||||
- Fix Rope so it won’t search the whole project directory. This was an
|
||||
intended feature in v1.5 which did not work originally.
|
||||
- Use ``yas-text`` instead of ``text`` in snippets for compatibility
|
||||
with the unreleased yasnippet from MELPA (thanks to Daniel Wu!)
|
||||
|
||||
New in Elpy 1.5.0
|
||||
=================
|
||||
|
||||
- Elpy now has a `manual`_. Additionally, there's a menu bar now which
|
||||
should make it easier to discover Elpy features.
|
||||
- The Elpy Python package now ships with the Emacs Lisp package,
|
||||
removing the need to install Elpy via pip.
|
||||
- Python 3.4 is now officially supported.
|
||||
- The new command ``elpy-config`` can be used to configure Elpy using
|
||||
Emacs' built-in customize system. Elpy has been changed to make the
|
||||
most of this.
|
||||
- Elpy now uses company-mode instead of auto-complete for on-the-fly
|
||||
auto completion. This changes a few things. There is no automatic
|
||||
documentation popup anymore. Instead, you can type ``C-d`` and get
|
||||
the documentation buffer. In addition, you can type ``C-w`` to see
|
||||
the source of the current candidate in context.
|
||||
- Elpy now uses pyvenv as the virtualenv module, enabling
|
||||
virtualenvwrapper hooks.
|
||||
- We now ship with a large number of YASnippet snippets. Try ``M-x
|
||||
yas-insert-snippet``.
|
||||
- The new unified test running interface on ``C-c C-t`` will try to
|
||||
determine the current test and run it, or, failing that, run all
|
||||
tests. Provide a prefix argument to just run all tests no matter
|
||||
what. You can change the test runner to be used using
|
||||
``elpy-set-test-runner``. Elpy supports the default unittest
|
||||
discover runner, the Django discover runner, nosetests and py.test
|
||||
by default. New test runners can easily be defined.
|
||||
- There's a new multi-edit functionality. ``C-c C-e`` will edit all
|
||||
occurrences of the symbol under point. When using Jedi, this is
|
||||
using semantic information as opposed to just syntactic one. When a
|
||||
region is active, edit all occurrences of the text in region in the
|
||||
current buffer.
|
||||
- When sending Python code to the interactive interpreter using ``C-c
|
||||
C-c``, Elpy will now not automatically pop to the interpreter
|
||||
anymore. Use ``C-c C-z`` to switch to the interpreter.
|
||||
- Elpy will now display the current class and function if there is no
|
||||
call tip to be displayed. Removes the ``C-c C-q`` binding.
|
||||
- If there is a call tip, highlight the current argument (requires Jedi).
|
||||
- The documentation interface using ``C-c C-d`` is much smarter now,
|
||||
falling back to pydoc when necessary and providing sensible
|
||||
completion for that, too. Provide a prefix argument if you want no
|
||||
smarts, just pydoc.
|
||||
- ``<S-return>`` and ``<C-S-return>`` now open a line below or above
|
||||
the current one.
|
||||
- ``<C-cursor>`` will now navigate between Python blocks of the same
|
||||
indentation level. ``<M-cursor>`` will move the current block. Try
|
||||
it, it's easier to understand when you see it than to explain it.
|
||||
- There's a new concept of modules. The variable
|
||||
``elpy-default-minor-modes`` is gone (use ``elpy-mode-hook`` for
|
||||
minor modes). Instead, there's now ``elpy-modules`` which can be
|
||||
used to enable or disable certain features of Elpy.
|
||||
- ``elpy-clean-modeline`` is gone, modules now clean themselves up.
|
||||
- Elpy now distinguishes between the project root, where project files
|
||||
are located, and the library root, which should be part of
|
||||
``sys.path`` to import the module under development.
|
||||
- ``elpy-project-ignored-directories`` replaces the old
|
||||
``elpy-rgrep-ignored-directories`` and is used by more features.
|
||||
- ``elpy-doc-websearch`` has been removed as it was barely useable
|
||||
as is.
|
||||
- Elpy now tries to be more helpful when errors in the backend happen.
|
||||
This removes ``elpy-rpc-traceback``, as that will be displayed by
|
||||
default.
|
||||
- Optimizations were added to handle large files, making general
|
||||
interaction a lot faster.
|
||||
- When Rope is being used, do not search through unusually large
|
||||
directories. This should speed up interaction in those cases,
|
||||
especially when editing a file in the home directory.
|
||||
- And a whole lot of minor bug fixes and little improvements.
|
||||
|
||||
.. _manual: https://elpy.readthedocs.io/
|
||||
|
||||
|
||||
New in Elpy 1.4.2
|
||||
==================
|
||||
|
||||
- Minor bugfix to prevent an error from projectile-project-root to
|
||||
interfere with Elpy’s project finding strategy.
|
||||
|
||||
New in Elpy 1.4.1
|
||||
=================
|
||||
|
||||
- Elpy now sets project-wide preferences for Rope, enabling completion
|
||||
in the sys package, among others.
|
||||
- An error is avoided in the Jedi backend when trying to go to symbols
|
||||
in compiled packages.
|
||||
- A compatibility alias was added for nose.el, which insists on
|
||||
breaking backwards compatibility with Emacs 24.x.
|
||||
|
||||
New in Elpy 1.4.0
|
||||
=================
|
||||
|
||||
- Elpy has moved to its own ELPA. Make sure to update your
|
||||
package-archives (as described above).
|
||||
- For a file in a Projectile-managed project is opened, Elpy will now
|
||||
use Projectile’s project root.
|
||||
- When the user has set a valid python-check-command, elpy will now
|
||||
refrain from overriding it.
|
||||
- On Windows, elpy is now using the pythonw.exe interpreter for the
|
||||
RPC process, as that seems to be causing fewer issues.
|
||||
- And various smaller bugfixes.
|
||||
|
||||
New in Elpy 1.3.0
|
||||
=================
|
||||
|
||||
- virtualenv.el has been replaced by pyvenv.el, as that library offers
|
||||
more features.
|
||||
- elpy-rpc-restart now works globally, not just in Elpy buffers.
|
||||
- Elpy does not try to complete in comments anymore.
|
||||
- The new command elpy-rpc-traceback gives access to the last stack
|
||||
trace in the Elpy backend, helping with debugging problems.
|
||||
- The flymake check function is now run with the current directory as
|
||||
/ to avoid accidental imports.
|
||||
- Ensure correct handling of yas-snippet-dirs by Elpy. This variable
|
||||
can be a string, so ensure it’s a list before adding to it.
|
||||
- The new variable elpy-show-installation-instructions can be used to
|
||||
disable the installation screen.
|
||||
- Fix a very nasty bug causing spurious empty lines in a buffer or
|
||||
consume 100% CPU in certain situations when using the Jedi backend.
|
||||
Thanks to Matthias Dahl for finding this bug.
|
||||
- Various other bugfixes.
|
||||
|
||||
New in Elpy 1.2.1
|
||||
=================
|
||||
|
||||
Bugfix release.
|
||||
|
||||
- The refactoring was not ported to the new asynchronous API,
|
||||
resulting in an error when refactoring was attempted.
|
||||
- The project root now always returns a directory. Too many parts of
|
||||
elpy relies on this. If the project root turns out to be your home
|
||||
directory, elpy will warn you about it.
|
||||
- Elpy now works correctly with Emacs 24.2. There were some
|
||||
compatibility functions missing.
|
||||
- Blocking RPC calls now do not block for one second even if there is
|
||||
process output.
|
||||
|
||||
New in Elpy 1.2
|
||||
===============
|
||||
|
||||
- Elpy now uses asynchronous RPC. This means that Emacs should not
|
||||
freeze anymore while eldoc or auto-complete functions run.
|
||||
- ``elpy-shell-send-region-or-buffer`` will now remove common
|
||||
indentation of the region, making it possible to easily send parts
|
||||
of an if statement or function body without manually adjusting the
|
||||
indentation.
|
||||
- The Python package depends on ``flake8``, and will also try to be
|
||||
smarter when detecting ``flake8`` for on-the-fly checking.
|
||||
- ``elpy-check`` can be run with a prefix argument to check the whole
|
||||
project, instead of only the current file.
|
||||
- ``elpy-rgrep-symbol`` now ignores a few common directories
|
||||
(``.tox``, ``build``, ``dist``).
|
||||
- When using the rope backend, Elpy will not create the
|
||||
``.ropeproject`` folders anymore. This should keep projects a lot
|
||||
cleaner.
|
||||
|
||||
New in Elpy 1.1
|
||||
===============
|
||||
|
||||
- Elpy now always uses the root directory of the package as the
|
||||
project root; this should avoid some confusion and improve
|
||||
auto-completion suggestions
|
||||
- ``elpy-shell-send-region-or-buffer`` now accepts a prefix argument
|
||||
to run code wrapped behind ``if __name__ == '__main__'``, which is
|
||||
ignored by default
|
||||
- ``elpy-project-root`` is now a safe local variable and can be set
|
||||
from file variables
|
||||
- Elpy now supports project-specific RPC processes, see
|
||||
``elpy-rpc-project-specific`` for how to use this
|
||||
- ``M-*`` now works to go back where you came from after a ``M-.``
|
||||
- Elpy now ships with a few dedicated snippets for YASnippet
|
||||
- Support and require Jedi 0.6.0
|
||||
|
||||
New in Elpy 1.0
|
||||
===============
|
||||
|
||||
- Version 0.9 was a release candidate, so this release focused on bug
|
||||
fixes instead of new features.
|
||||
- ``elpy-enable`` now takes an optional argument that skips variable
|
||||
initialization for those users who prefer their own defaults for
|
||||
other modes.
|
||||
- ``python-check.sh`` has been removed from Elpy, as the flake8 tool
|
||||
from pypi does everything it does, only better.
|
||||
- Elpy will now start the helper subprocess in the root directory,
|
||||
avoiding accidental Python path clobbering.
|
||||
|
||||
New in Elpy 0.9
|
||||
===============
|
||||
|
||||
- Elpy now officially support Python 2.6, 2.7 and 3.3 on Emacs 24.2
|
||||
and 24.3, with continuous integration tests thanks to
|
||||
`Travis CI`_.
|
||||
- Extended support for Pydoc. ``C-u C-c C-d`` will now prompt for an
|
||||
auto-completed symbol to run Pydoc on. The pydoc output will be
|
||||
formatted and placed in a help buffer for easy review.
|
||||
- Refactoring support is back. ``C-c C-r`` will pop up a refactoring
|
||||
wizard offering various refactoring options. Most of them depend on
|
||||
the presence of Rope, though, even if Jedi is used as a completion
|
||||
backend.
|
||||
- The Rope backend has been extended to provide completions for
|
||||
modules in an import clause.
|
||||
- New refactoring option: Add missing imports. This will search for
|
||||
undefined symbols in the current file and automatically add
|
||||
appropriate imports.
|
||||
- ``C-c C-c (elpy-rgrep-symbol)`` now prompts for a regexp when a prefix
|
||||
argument is given instead of using the symbol at point.
|
||||
|
||||
.. _Travis CI: https://travis-ci.org/
|
||||
|
||||
New in Elpy 0.8
|
||||
===============
|
||||
|
||||
Python Backend Rewrite
|
||||
----------------------
|
||||
|
||||
- Elpy does not use Pymacs, Ropemacs and Ropemode anymore, but instead
|
||||
provides its own Python interface with the elpy package on PyPI.
|
||||
- This not only should improve performance, but also enables using
|
||||
Jedi as an alternative backend for completion. Use ``M-x
|
||||
elpy-set-backend`` to change between rope and jedi. For now, this
|
||||
does disable all refactoring support, though.
|
||||
|
||||
Project Support
|
||||
---------------
|
||||
|
||||
- Elpy now has built-in project support. The interface is rather
|
||||
simple: You can set ``elpy-project-root`` to the correct value in
|
||||
``.dir-locals.el``, or just rely on the automatic detection. If you
|
||||
change your mind, you can always just ``elpy-set-project-root``.
|
||||
- New dependency: Find File in Project (ffip), bound to ``C-c C-f`` by
|
||||
default. This will allow you to find files anywhere in your project
|
||||
using a search-as-you-type interface like ido.
|
||||
- New dependency: nose, bound to ``C-c C-t`` by default. This will run
|
||||
the nosetests binary in the root of your current library directory.
|
||||
You can restrict the tests being run to the current test or the
|
||||
current module by adding prefix arguments.
|
||||
- New function: Recursive grep for symbol, bound to ``C-c C-s`` by
|
||||
default. This will search for the symbol at point in the whole
|
||||
project.
|
||||
|
||||
New dependencies
|
||||
----------------
|
||||
|
||||
- idomenu, bound to ``C-c C-j`` by default. This replaces the standard
|
||||
imenu interface with an ido-based search-as-you-type interface for
|
||||
definitions in the current buffer.
|
||||
- virtualenv.el, replacing pyvirtualenv.el). Use ``M-x
|
||||
virtualenv-workon`` to enable a virtualenv.
|
||||
- iedit.el, bound to ``M-,`` by default. This highlights all occurrences
|
||||
of the symbol at point or the active region in the current buffer or
|
||||
narrowing. When you edit any of them, all others will be edited the
|
||||
same. This allows some basic and very quick refactoring.
|
||||
- New variable ``elpy-default-minor-modes`` which is run by ``elpy-mode``
|
||||
on startup. If you don’t want to use some modes, remove them from
|
||||
here.
|
||||
|
||||
Key Bindings and Functions
|
||||
--------------------------
|
||||
|
||||
- The key bindings have been reworked and cleaned up. Sorry, this
|
||||
might cause confusion.
|
||||
- Yasnippet is now on its own keybinding, ``C-c C-i``, instead of
|
||||
sharing the auto-complete interface. This was done because some
|
||||
snippets conflicted with legitimate, unsnippy completions.
|
||||
- New function: Occur Definitions, bound to ``C-c C-o`` by default. This
|
||||
will run the standard occur command to show definitions (classes and
|
||||
functions) in your current buffer, giving you a very quick outline
|
||||
and the ability to jump to different definitions quickly.
|
||||
- New function: Show Defun, bound to ``C-c C-q`` by default. This will
|
||||
show the current method and possibly class in the mode line, which
|
||||
is helpful in long functions.
|
||||
- New functions: Forward/backward definition, bound to ``M-n`` and ``M-p``
|
||||
as well as ``<M-down>`` and ``<M-up>`` by default. These will jump to
|
||||
the next or previous definition (class or function), helping with
|
||||
quick navigation through a file.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
|
||||
- The documentation function (``C-c C-d``) now uses pydoc when a prefix
|
||||
arg is given.
|
||||
- The web search function (``C-c C-w``) now searches for the current
|
||||
symbol by default. The tab-completing web documentation interface
|
||||
was removed and is scheduled to be replaced with a new pydoc
|
||||
interface in future versions.
|
||||
- The ``python-check.sh`` is now shipped with elpy. If you load elpy.el
|
||||
before you load python.el, it should be the default
|
||||
``python-check-command``.
|
|
@ -0,0 +1,51 @@
|
|||
;;; elpy-autoloads.el --- automatically extracted autoloads
|
||||
;;
|
||||
;;; Code:
|
||||
(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path))))
|
||||
|
||||
;;;### (autoloads nil "elpy" "elpy.el" (23080 28380 123219 501000))
|
||||
;;; Generated autoloads from elpy.el
|
||||
|
||||
(autoload 'elpy-enable "elpy" "\
|
||||
Enable Elpy in all future Python buffers.
|
||||
|
||||
\(fn &optional IGNORED)" t nil)
|
||||
|
||||
(autoload 'elpy-mode "elpy" "\
|
||||
Minor mode in Python buffers for the Emacs Lisp Python Environment.
|
||||
|
||||
This mode fully supports virtualenvs. Once you switch a
|
||||
virtualenv using \\[pyvenv-workon], you can use
|
||||
\\[elpy-rpc-restart] to make the elpy Python process use your
|
||||
virtualenv.
|
||||
|
||||
\\{elpy-mode-map}
|
||||
|
||||
\(fn &optional ARG)" t nil)
|
||||
|
||||
(autoload 'elpy-config "elpy" "\
|
||||
Configure Elpy.
|
||||
|
||||
This function will pop up a configuration buffer, which is mostly
|
||||
a customize buffer, but has some more options.
|
||||
|
||||
\(fn)" t nil)
|
||||
|
||||
(autoload 'elpy-version "elpy" "\
|
||||
Display the version of Elpy.
|
||||
|
||||
\(fn)" t nil)
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil nil ("elpy-django.el" "elpy-pkg.el" "elpy-profile.el"
|
||||
;;;;;; "elpy-refactor.el" "elpy-shell.el") (23080 28381 456561 159000))
|
||||
|
||||
;;;***
|
||||
|
||||
;; Local Variables:
|
||||
;; version-control: never
|
||||
;; no-byte-compile: t
|
||||
;; no-update-autoloads: t
|
||||
;; End:
|
||||
;;; elpy-autoloads.el ends here
|
|
@ -0,0 +1,170 @@
|
|||
;;; elpy-django.el --- Django extension for elpy
|
||||
|
||||
;; Copyright (C) 2013-2016 Jorgen Schaefer
|
||||
|
||||
;; Author: Daniel Gopar <gopardaniel@gmail.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 serves as an extension to elpy by adding django support
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 's)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; User customization
|
||||
|
||||
(defcustom elpy-django-command "django-admin.py"
|
||||
"Command to use when running Django specific commands.
|
||||
Best to set it to full path to 'manage.py' if it's available."
|
||||
:type 'string
|
||||
:safe 'stringp
|
||||
:group 'elpy)
|
||||
(make-variable-buffer-local 'elpy-django-command)
|
||||
|
||||
(defcustom elpy-django-server-ipaddr "127.0.0.1"
|
||||
"What address Django will use when running the dev server."
|
||||
:type 'string
|
||||
:safe 'stringp
|
||||
:group 'elpy)
|
||||
(make-variable-buffer-local 'elpy-django-server-ipaddr)
|
||||
|
||||
(defcustom elpy-django-server-port "8000"
|
||||
"What port Django will use when running the dev server."
|
||||
:type 'string
|
||||
:safe 'stringp
|
||||
:group 'elpy)
|
||||
(make-variable-buffer-local 'elpy-django-server-port)
|
||||
|
||||
(defcustom elpy-django-server-command "runserver"
|
||||
"When executing `elpy-django-runserver' what should be the server
|
||||
command to use."
|
||||
:type 'string
|
||||
:safe 'stringp
|
||||
:group 'elpy)
|
||||
(make-variable-buffer-local 'elpy-django-server-command)
|
||||
|
||||
(defcustom elpy-django-always-prompt nil
|
||||
"When non-nil, it will always prompt for extra arguments
|
||||
to pass with the chosen command."
|
||||
:type 'boolean
|
||||
:safe 'booleanp
|
||||
:group 'elpy)
|
||||
(make-variable-buffer-local 'elpy-django-always-prompt)
|
||||
|
||||
(defcustom elpy-django-commands-with-req-arg '("startapp" "startproject"
|
||||
"loaddata" "sqlmigrate"
|
||||
"sqlsequencereset"
|
||||
"squashmigrations")
|
||||
"Used to determine if we should prompt for arguments. Some commands
|
||||
require arguments in order for it to work."
|
||||
:type 'list
|
||||
:safe 'listp
|
||||
:group 'elpy)
|
||||
(make-variable-buffer-local 'elpy-django-commands-with-req-arg)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Key map
|
||||
|
||||
(defvar elpy-django-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map (kbd "c") 'elpy-django-command)
|
||||
(define-key map (kbd "r") 'elpy-django-runserver)
|
||||
map)
|
||||
"Key map for django extension")
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Helper Functions
|
||||
|
||||
(defun elpy-django-setup ()
|
||||
"Decides whether to start the minor mode or not."
|
||||
;; Make sure we're in an actual file and we can find
|
||||
;; manage.py. Otherwise user will have to manually
|
||||
;; start this mode if they're using 'django-admin.py'
|
||||
(when (locate-dominating-file default-directory "manage.py")
|
||||
;; Let's be nice and point to full path of 'manage.py'
|
||||
;; This only affects the buffer if there's no directory
|
||||
;; variable overwriting it.
|
||||
(setq elpy-django-command
|
||||
(concat (locate-dominating-file default-directory "manage.py") "manage.py"))
|
||||
(elpy-django 1)))
|
||||
|
||||
(defun elpy-django--get-commands ()
|
||||
"Return list of django commands."
|
||||
(let ((dj-commands-str nil)
|
||||
(help-output
|
||||
(shell-command-to-string (concat elpy-django-command " -h"))))
|
||||
(setq dj-commands-str
|
||||
(with-temp-buffer
|
||||
(progn
|
||||
(insert help-output)
|
||||
(goto-char (point-min))
|
||||
(delete-region (point) (search-forward "Available subcommands:" nil nil nil))
|
||||
;; cleanup [auth] and stuff
|
||||
(goto-char (point-min))
|
||||
(save-excursion
|
||||
(replace-regexp "\\[.*\\]" ""))
|
||||
(buffer-string))))
|
||||
;; get a list of commands from the output of manage.py -h
|
||||
;; What would be the pattern to optimize this ?
|
||||
(setq dj-commands-str (split-string dj-commands-str "\n"))
|
||||
(setq dj-commands-str (-remove (lambda (x) (string= x "")) dj-commands-str))
|
||||
(setq dj-commands-str (mapcar (lambda (x) (s-trim x)) dj-commands-str))
|
||||
(sort dj-commands-str 'string-lessp)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; User Functions
|
||||
|
||||
(defun elpy-django-command (cmd)
|
||||
"Prompt user for Django command. If called with `C-u',
|
||||
it will prompt for other flags/arguments to run."
|
||||
(interactive (list (completing-read "Command: " (elpy-django--get-commands) nil nil)))
|
||||
;; Called with C-u, variable is set or is a cmd that requires an argument
|
||||
(when (or current-prefix-arg
|
||||
elpy-django-always-prompt
|
||||
(member cmd elpy-django-commands-with-req-arg))
|
||||
(setq cmd (concat cmd " " (read-shell-command (concat cmd ": ") "--noinput"))))
|
||||
(compile (concat elpy-django-command " " cmd)))
|
||||
|
||||
(defun elpy-django-runserver (arg)
|
||||
"Start the server and automatically add the ipaddr and port.
|
||||
Also create it's own special buffer so that we can have multiple
|
||||
servers running per project.
|
||||
|
||||
When called with a prefix (C-u), it will prompt for additional args."
|
||||
(interactive "P")
|
||||
(let* ((cmd (concat elpy-django-command " " elpy-django-server-command))
|
||||
(proj-root (file-name-base (directory-file-name (elpy-project-root))))
|
||||
(buff-name (format "*runserver[%s]*" proj-root)))
|
||||
;; Kill any previous instance of runserver since we might be doing something new
|
||||
(when (get-buffer buff-name)
|
||||
(kill-buffer buff-name))
|
||||
(setq cmd (concat cmd " " elpy-django-server-ipaddr ":" elpy-django-server-port))
|
||||
(when (or arg elpy-django-always-prompt)
|
||||
(setq cmd (concat cmd " "(read-shell-command (concat cmd ": ")))))
|
||||
(compile cmd)
|
||||
(with-current-buffer "*compilation*"
|
||||
(rename-buffer buff-name))))
|
||||
|
||||
(define-minor-mode elpy-django
|
||||
"Minor mode to for Django commands."
|
||||
:group 'elpy)
|
||||
|
||||
(provide 'elpy-django)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; elpy-django.el ends here
|
Binary file not shown.
|
@ -0,0 +1,11 @@
|
|||
(define-package "elpy" "20171206.847" "Emacs Python Development Environment"
|
||||
'((company "0.9.2")
|
||||
(emacs "24.4")
|
||||
(find-file-in-project "3.3")
|
||||
(highlight-indentation "0.5.0")
|
||||
(pyvenv "1.3")
|
||||
(yasnippet "0.8.0")
|
||||
(s "1.11.0")))
|
||||
;; Local Variables:
|
||||
;; no-byte-compile: t
|
||||
;; End:
|
|
@ -0,0 +1,111 @@
|
|||
;;; elpy-profile.el --- Profiling capabilitiss for elpy
|
||||
|
||||
;; Copyright (C) 2013-2016 Jorgen Schaefer
|
||||
|
||||
;; Author: Gaby Launay <gaby.launay@tutanota.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 serves as an extension to elpy by adding profiling capabilities
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; User customization
|
||||
|
||||
(defcustom elpy-profile-visualizer "snakeviz"
|
||||
"Visualizer for elpy profile results."
|
||||
:type 'str
|
||||
:group 'elpy)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Helper Functions
|
||||
|
||||
(defun elpy-profile--display-profiling (file)
|
||||
"Display the profile result FILE using `elpy-profile-visualizer'."
|
||||
(let ((exec (car (split-string elpy-profile-visualizer " " t)))
|
||||
(args (append (cdr (split-string elpy-profile-visualizer " " t)) (list file))))
|
||||
(if (executable-find exec)
|
||||
(apply 'call-process exec nil 0 nil args)
|
||||
(message "Elpy profile visualizer '%s' not found" exec))))
|
||||
|
||||
(defun elpy-profile--sentinel (process string)
|
||||
"Elpy profile sentinel."
|
||||
(let ((filename (file-name-nondirectory (process-get process 'file)))
|
||||
(prof-file (process-get process 'prof-file))
|
||||
(dont-display (process-get process 'dont-display)))
|
||||
(with-current-buffer "*elpy-profile-log*"
|
||||
(view-mode))
|
||||
(if (not (string-equal string "finished\n"))
|
||||
(progn
|
||||
(message "[%s] Profiling failed" filename)
|
||||
(display-buffer "*elpy-profile-log*"))
|
||||
(message "[%s] Profiling succeeded" filename)
|
||||
(when (not dont-display)
|
||||
(elpy-profile--display-profiling prof-file)))))
|
||||
|
||||
(defun elpy-profile--file (file &optional in-dir dont-display)
|
||||
"Profile asynchronously FILE and display the result using
|
||||
`elpy-profile-visualizer'.
|
||||
|
||||
If IN-DIR is non nil, profile result is saved in the same
|
||||
directory as the script.
|
||||
If DONT-DISPLAY is non nil, don't display the profile results."
|
||||
(ignore-errors (kill-buffer "*elpy-profile-log*"))
|
||||
(let* ((prof-file (if in-dir
|
||||
(concat (file-name-sans-extension file) ".profile")
|
||||
(concat (make-temp-file "elpy-profile-" nil ".profile"))))
|
||||
(proc-name (format "elpy-profile-%s" file))
|
||||
(proc-cmd (list python-shell-interpreter "-m" "cProfile" "-o" prof-file file))
|
||||
(proc (make-process :name proc-name
|
||||
:buffer "*elpy-profile-log*"
|
||||
:sentinel 'elpy-profile--sentinel
|
||||
:command proc-cmd)))
|
||||
(message "[%s] Profiling ..." (file-name-nondirectory file))
|
||||
(process-put proc 'prof-file prof-file)
|
||||
(process-put proc 'file file)
|
||||
(process-put proc 'dont-display dont-display)
|
||||
prof-file))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; User Functions
|
||||
|
||||
(defun elpy-profile-buffer-or-region (&optional in-dir dont-display)
|
||||
"Profile asynchronously the active region or the current buffer
|
||||
and display the result using `elpy-profile-visualizer'.
|
||||
|
||||
If IN-DIR is non nil, profile result is saved in the same
|
||||
directory as the script.
|
||||
If DONT-DISPLAY is non nil, don't display the profile results."
|
||||
(interactive "P")
|
||||
(let* ((file-name (buffer-name))
|
||||
(file-dir (file-name-directory (buffer-file-name)))
|
||||
(beg (if (region-active-p) (region-beginning) (point-min)))
|
||||
(end (if (region-active-p) (region-end) (point-max)))
|
||||
(tmp-file-prefix (if (region-active-p) "_region_" ""))
|
||||
(tmp-file (if in-dir
|
||||
(concat file-dir "/" tmp-file-prefix file-name)
|
||||
(concat (make-temp-file "elpy-profile-" t) "/" tmp-file-prefix file-name)))
|
||||
(region (elpy-shell--region-without-indentation beg end)))
|
||||
(with-temp-buffer
|
||||
(insert region)
|
||||
(write-region (point-min) (point-max) tmp-file nil t))
|
||||
(elpy-profile--file tmp-file t dont-display)))
|
||||
|
||||
(provide 'elpy-profile)
|
||||
;;; elpy-profile.el ends here
|
Binary file not shown.
|
@ -0,0 +1,297 @@
|
|||
;;; elpy-refactor.el --- Refactoring mode for Elpy
|
||||
|
||||
;; Copyright (C) 2013-2016 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))))
|
||||
(when (not (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 (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]."
|
||||
(when (not 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)
|
||||
(when (not 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
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -0,0 +1,41 @@
|
|||
# Elpy, the Emacs Lisp Python Environment
|
||||
|
||||
# Copyright (C) 2013-2016 Jorgen Schaefer
|
||||
|
||||
# Author: Jorgen Schaefer <contact@jorgenschaefer.de>
|
||||
# URL: http://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/>.
|
||||
|
||||
"""The Emacs Lisp Python Environment.
|
||||
|
||||
Elpy is a mode for Emacs to support writing Python code. This package
|
||||
provides the backend within Python to support auto-completion,
|
||||
documentation extraction, and navigation.
|
||||
|
||||
Emacs will start the protocol by running the module itself, like so:
|
||||
|
||||
python -m elpy
|
||||
|
||||
This will emit a greeting string on a single line, and then wait for
|
||||
the protocol to start. Details of the protocol can be found in
|
||||
elpy.rpc.
|
||||
|
||||
This package is unlikely to be useful on its own.
|
||||
|
||||
"""
|
||||
|
||||
__author__ = "Jorgen Schaefer"
|
||||
__version__ = "1.17.0"
|
||||
__license__ = "GPL"
|
|
@ -0,0 +1,25 @@
|
|||
"""Main interface to the RPC server.
|
||||
|
||||
You should be able to just run the following to use this module:
|
||||
|
||||
python -m elpy
|
||||
|
||||
The first line should be "elpy-rpc ready". If it isn't, something
|
||||
broke.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import elpy
|
||||
from elpy.server import ElpyRPCServer
|
||||
|
||||
if __name__ == '__main__':
|
||||
stdin = sys.stdin
|
||||
stdout = sys.stdout
|
||||
sys.stdout = sys.stderr = open(os.devnull, "w")
|
||||
stdout.write('elpy-rpc ready ({0})\n'
|
||||
.format(elpy.__version__))
|
||||
stdout.flush()
|
||||
ElpyRPCServer(stdin, stdout).serve_forever()
|
Binary file not shown.
|
@ -0,0 +1,21 @@
|
|||
"""Glue for the "autopep8" library.
|
||||
|
||||
"""
|
||||
|
||||
from elpy.rpc import Fault
|
||||
|
||||
|
||||
try:
|
||||
import autopep8
|
||||
except ImportError: # pragma: no cover
|
||||
autopep8 = None
|
||||
|
||||
|
||||
def fix_code(code):
|
||||
"""Formats Python code to conform to the PEP 8 style guide.
|
||||
|
||||
"""
|
||||
if not autopep8:
|
||||
raise Fault('autopep8 not installed, cannot fix code.',
|
||||
code=400)
|
||||
return autopep8.fix_code(code, apply_config=True)
|
|
@ -0,0 +1,33 @@
|
|||
"""Python 2/3 compatibility definitions.
|
||||
|
||||
These are used by the rest of Elpy to keep compatibility definitions
|
||||
in one place.
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
if sys.version_info >= (3, 0):
|
||||
PYTHON3 = True
|
||||
|
||||
from io import StringIO
|
||||
|
||||
def ensure_not_unicode(obj):
|
||||
return obj
|
||||
else:
|
||||
PYTHON3 = False
|
||||
|
||||
from StringIO import StringIO # noqa
|
||||
|
||||
def ensure_not_unicode(obj):
|
||||
"""Return obj. If it's a unicode string, convert it to str first.
|
||||
|
||||
Pydoc functions simply don't find anything for unicode
|
||||
strings. No idea why.
|
||||
|
||||
"""
|
||||
if isinstance(obj, unicode):
|
||||
return obj.encode("utf-8")
|
||||
else:
|
||||
return obj
|
|
@ -0,0 +1,118 @@
|
|||
"""Glue for the "importmagic" library.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
|
||||
|
||||
try:
|
||||
import importmagic.index
|
||||
import importmagic.symbols
|
||||
import importmagic.importer
|
||||
except ImportError: # pragma: no cover
|
||||
importmagic = None
|
||||
|
||||
|
||||
class ImportMagicError(Exception):
|
||||
"""Used to pass defined errors from importmagic to the RPC layer."""
|
||||
|
||||
|
||||
class ImportMagic(object):
|
||||
|
||||
def __init__(self):
|
||||
self.is_enabled = bool(importmagic)
|
||||
# fail_message is reported to the user when symbol_index
|
||||
# is (still) None
|
||||
self.fail_message = "symbol index is not yet ready"
|
||||
self.project_root = None
|
||||
self.symbol_index = None
|
||||
self.favorites = set()
|
||||
self._thread = None
|
||||
|
||||
def _build_symbol_index(self, project_root, custom_path, blacklist_re):
|
||||
try:
|
||||
index = importmagic.index.SymbolIndex(blacklist_re=blacklist_re)
|
||||
if os.environ.get('ELPY_TEST'):
|
||||
# test suite support: do not index the whole PYTHONPATH, it
|
||||
# takes much too long
|
||||
index.build_index([])
|
||||
elif custom_path:
|
||||
index.build_index(custom_path)
|
||||
else:
|
||||
index.build_index([project_root] + sys.path)
|
||||
except Exception as e:
|
||||
self.fail_message = "symbol index failed to build: %s" % e
|
||||
else:
|
||||
self.symbol_index = index
|
||||
|
||||
def build_index(self, project_root, custom_path=None, blacklist_re=None):
|
||||
self.project_root = None
|
||||
self._thread = threading.Thread(target=self._build_symbol_index,
|
||||
args=(project_root, custom_path,
|
||||
blacklist_re))
|
||||
self._thread.setDaemon(True)
|
||||
self._thread.start()
|
||||
|
||||
def get_import_symbols(self, symbol):
|
||||
scores = self.symbol_index.symbol_scores(symbol)
|
||||
|
||||
def sort_key(item):
|
||||
score, mod, var = item
|
||||
if mod in self.favorites:
|
||||
return 2 + score, mod, var
|
||||
return score, mod, var
|
||||
|
||||
scores.sort(key=sort_key, reverse=True)
|
||||
return ["from %s import %s" % (mod, var) if var else "import %s" % mod
|
||||
for (_, mod, var) in scores]
|
||||
|
||||
def add_import(self, source, statement):
|
||||
imports = importmagic.importer.Imports(self.symbol_index, source)
|
||||
if statement.startswith('import '):
|
||||
sepalias = None
|
||||
alias = None
|
||||
if ' as ' in statement:
|
||||
sepalias = statement.find(' as ')
|
||||
alias = statement[sepalias+4:]
|
||||
modname = statement[7:sepalias]
|
||||
imports.add_import(modname, alias)
|
||||
self.favorites.add(modname)
|
||||
else:
|
||||
sep = statement.find(' import ')
|
||||
sepalias = None
|
||||
alias = None
|
||||
if ' as ' in statement:
|
||||
sepalias = statement.find(' as ')
|
||||
alias = statement[sepalias+4:]
|
||||
modname = statement[5:sep]
|
||||
name = statement[sep+8:sepalias]
|
||||
if sep > -1:
|
||||
self.favorites.add(modname)
|
||||
imports.add_import_from(modname, name, alias)
|
||||
start_line, end_line, import_block = imports.get_update()
|
||||
return start_line, end_line, import_block
|
||||
|
||||
def get_unresolved_symbols(self, source):
|
||||
try:
|
||||
scope = importmagic.symbols.Scope.from_source(source)
|
||||
except SyntaxError:
|
||||
raise ImportMagicError('cannot find unresolved names in '
|
||||
'incomplete file')
|
||||
unres, unref = scope.find_unresolved_and_unreferenced_symbols()
|
||||
return list(unres)
|
||||
|
||||
def remove_unreferenced_imports(self, source):
|
||||
try:
|
||||
scope = importmagic.symbols.Scope.from_source(source)
|
||||
except SyntaxError:
|
||||
raise ImportMagicError('cannot find unreferenced imports in '
|
||||
'incomplete file')
|
||||
unres, unref = scope.find_unresolved_and_unreferenced_symbols()
|
||||
# Note: we do not supply "unres" to the call below, since we do
|
||||
# not want to add imports without querying the user from which
|
||||
# module symbols should be imported.
|
||||
start_line, end_line, import_block = importmagic.importer.get_update(
|
||||
source, self.symbol_index, set(), unref)
|
||||
return start_line, end_line, import_block
|
|
@ -0,0 +1,397 @@
|
|||
"""Elpy backend using the Jedi library.
|
||||
|
||||
This backend uses the Jedi library:
|
||||
|
||||
https://github.com/davidhalter/jedi
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import jedi
|
||||
|
||||
from elpy import rpc
|
||||
|
||||
|
||||
class JediBackend(object):
|
||||
"""The Jedi backend class.
|
||||
|
||||
Implements the RPC calls we can pass on to Jedi.
|
||||
|
||||
Documentation: http://jedi.jedidjah.ch/en/latest/docs/plugin-api.html
|
||||
|
||||
"""
|
||||
name = "jedi"
|
||||
|
||||
def __init__(self, project_root):
|
||||
self.project_root = project_root
|
||||
self.completions = {}
|
||||
sys.path.append(project_root)
|
||||
|
||||
def rpc_get_completions(self, filename, source, offset):
|
||||
line, column = pos_to_linecol(source, offset)
|
||||
proposals = run_with_debug(jedi, 'completions',
|
||||
source=source, line=line, column=column,
|
||||
path=filename, encoding='utf-8')
|
||||
if proposals is None:
|
||||
return []
|
||||
self.completions = dict((proposal.name, proposal)
|
||||
for proposal in proposals)
|
||||
return [{'name': proposal.name.rstrip("="),
|
||||
'suffix': proposal.complete.rstrip("="),
|
||||
'annotation': proposal.type,
|
||||
'meta': proposal.description}
|
||||
for proposal in proposals]
|
||||
|
||||
def rpc_get_completion_docstring(self, completion):
|
||||
proposal = self.completions.get(completion)
|
||||
if proposal is None:
|
||||
return None
|
||||
else:
|
||||
return proposal.docstring(fast=False)
|
||||
|
||||
def rpc_get_completion_location(self, completion):
|
||||
proposal = self.completions.get(completion)
|
||||
if proposal is None:
|
||||
return None
|
||||
else:
|
||||
return (proposal.module_path, proposal.line)
|
||||
|
||||
def rpc_get_docstring(self, filename, source, offset):
|
||||
line, column = pos_to_linecol(source, offset)
|
||||
locations = run_with_debug(jedi, 'goto_definitions',
|
||||
source=source, line=line, column=column,
|
||||
path=filename, encoding='utf-8')
|
||||
if locations and locations[-1].docstring():
|
||||
return ('Documentation for {0}:\n\n'.format(
|
||||
locations[-1].full_name) + locations[-1].docstring())
|
||||
else:
|
||||
return None
|
||||
|
||||
def rpc_get_definition(self, filename, source, offset):
|
||||
line, column = pos_to_linecol(source, offset)
|
||||
locations = run_with_debug(jedi, 'goto_definitions',
|
||||
source=source, line=line, column=column,
|
||||
path=filename, encoding='utf-8')
|
||||
# goto_definitions() can return silly stuff like __builtin__
|
||||
# for int variables, so we fall back on goto() in those
|
||||
# cases. See issue #76.
|
||||
if (
|
||||
locations and
|
||||
locations[0].module_path is None
|
||||
):
|
||||
locations = run_with_debug(jedi, 'goto_assignments',
|
||||
source=source, line=line,
|
||||
column=column,
|
||||
path=filename, encoding='utf-8')
|
||||
if not locations:
|
||||
return None
|
||||
else:
|
||||
loc = locations[-1]
|
||||
try:
|
||||
if loc.module_path:
|
||||
if loc.module_path == filename:
|
||||
offset = linecol_to_pos(source,
|
||||
loc.line,
|
||||
loc.column)
|
||||
else:
|
||||
with open(loc.module_path) as f:
|
||||
offset = linecol_to_pos(f.read(),
|
||||
loc.line,
|
||||
loc.column)
|
||||
else:
|
||||
return None
|
||||
except IOError:
|
||||
return None
|
||||
return (loc.module_path, offset)
|
||||
|
||||
def rpc_get_assignment(self, filename, source, offset):
|
||||
line, column = pos_to_linecol(source, offset)
|
||||
locations = run_with_debug(jedi, 'goto_assignments',
|
||||
source=source, line=line, column=column,
|
||||
path=filename, encoding='utf-8')
|
||||
if not locations:
|
||||
return None
|
||||
else:
|
||||
loc = locations[-1]
|
||||
try:
|
||||
if loc.module_path:
|
||||
if loc.module_path == filename:
|
||||
offset = linecol_to_pos(source,
|
||||
loc.line,
|
||||
loc.column)
|
||||
else:
|
||||
with open(loc.module_path) as f:
|
||||
offset = linecol_to_pos(f.read(),
|
||||
loc.line,
|
||||
loc.column)
|
||||
else:
|
||||
return None
|
||||
except IOError:
|
||||
return None
|
||||
return (loc.module_path, offset)
|
||||
|
||||
def rpc_get_calltip(self, filename, source, offset):
|
||||
line, column = pos_to_linecol(source, offset)
|
||||
calls = run_with_debug(jedi, 'call_signatures',
|
||||
source=source, line=line, column=column,
|
||||
path=filename, encoding='utf-8')
|
||||
if calls:
|
||||
call = calls[0]
|
||||
else:
|
||||
call = None
|
||||
if not call:
|
||||
return None
|
||||
try:
|
||||
call.index
|
||||
except AttributeError as e:
|
||||
if "get_definition" in str(e):
|
||||
# Bug #627 / jedi#573
|
||||
return None
|
||||
elif "get_subscope_by_name" in str(e):
|
||||
# Bug #677 / jedi#628
|
||||
return None
|
||||
else:
|
||||
raise
|
||||
return {"name": call.name,
|
||||
"index": call.index,
|
||||
"params": [param.description for param in call.params]}
|
||||
|
||||
def rpc_get_usages(self, filename, source, offset):
|
||||
"""Return the uses of the symbol at offset.
|
||||
|
||||
Returns a list of occurrences of the symbol, as dicts with the
|
||||
fields name, filename, and offset.
|
||||
|
||||
"""
|
||||
line, column = pos_to_linecol(source, offset)
|
||||
uses = run_with_debug(jedi, 'usages',
|
||||
source=source, line=line, column=column,
|
||||
path=filename, encoding='utf-8')
|
||||
if uses is None:
|
||||
return None
|
||||
result = []
|
||||
for use in uses:
|
||||
if use.module_path == filename:
|
||||
offset = linecol_to_pos(source, use.line, use.column)
|
||||
elif use.module_path is not None:
|
||||
with open(use.module_path) as f:
|
||||
text = f.read()
|
||||
offset = linecol_to_pos(text, use.line, use.column)
|
||||
|
||||
result.append({"name": use.name,
|
||||
"filename": use.module_path,
|
||||
"offset": offset})
|
||||
|
||||
return result
|
||||
|
||||
def rpc_get_names(self, filename, source, offset):
|
||||
"""Return the list of possible names"""
|
||||
# Remove form feed characters, they confuse Jedi (jedi#424)
|
||||
source = source.replace("\f", " ")
|
||||
names = jedi.api.names(source=source,
|
||||
path=filename, encoding='utf-8',
|
||||
all_scopes=True,
|
||||
definitions=True,
|
||||
references=True)
|
||||
|
||||
result = []
|
||||
for name in names:
|
||||
if name.module_path == filename:
|
||||
offset = linecol_to_pos(source, name.line, name.column)
|
||||
elif name.module_path is not None:
|
||||
with open(name.module_path) as f:
|
||||
text = f.read()
|
||||
offset = linecol_to_pos(text, name.line, name.column)
|
||||
result.append({"name": name.name,
|
||||
"filename": name.module_path,
|
||||
"offset": offset})
|
||||
return result
|
||||
|
||||
|
||||
# From the Jedi documentation:
|
||||
#
|
||||
# line is the current line you want to perform actions on (starting
|
||||
# with line #1 as the first line). column represents the current
|
||||
# column/indent of the cursor (starting with zero). source_path
|
||||
# should be the path of your file in the file system.
|
||||
|
||||
def pos_to_linecol(text, pos):
|
||||
"""Return a tuple of line and column for offset pos in text.
|
||||
|
||||
Lines are one-based, columns zero-based.
|
||||
|
||||
This is how Jedi wants it. Don't ask me why.
|
||||
|
||||
"""
|
||||
line_start = text.rfind("\n", 0, pos) + 1
|
||||
line = text.count("\n", 0, line_start) + 1
|
||||
col = pos - line_start
|
||||
return line, col
|
||||
|
||||
|
||||
def linecol_to_pos(text, line, col):
|
||||
"""Return the offset of this line and column in text.
|
||||
|
||||
Lines are one-based, columns zero-based.
|
||||
|
||||
This is how Jedi wants it. Don't ask me why.
|
||||
|
||||
"""
|
||||
nth_newline_offset = 0
|
||||
for i in range(line - 1):
|
||||
new_offset = text.find("\n", nth_newline_offset)
|
||||
if new_offset < 0:
|
||||
raise ValueError("Text does not have {0} lines."
|
||||
.format(line))
|
||||
nth_newline_offset = new_offset + 1
|
||||
offset = nth_newline_offset + col
|
||||
if offset > len(text):
|
||||
raise ValueError("Line {0} column {1} is not within the text"
|
||||
.format(line, col))
|
||||
return offset
|
||||
|
||||
|
||||
def run_with_debug(jedi, name, *args, **kwargs):
|
||||
re_raise = kwargs.pop('re_raise', ())
|
||||
# Remove form feed characters, they confuse Jedi (jedi#424)
|
||||
if 'source' in kwargs:
|
||||
kwargs['source'] = kwargs['source'].replace("\f", " ")
|
||||
try:
|
||||
script = jedi.Script(*args, **kwargs)
|
||||
return getattr(script, name)()
|
||||
except Exception as e:
|
||||
if isinstance(e, re_raise):
|
||||
raise
|
||||
# Bug jedi#417
|
||||
if isinstance(e, TypeError) and str(e) == 'no dicts allowed':
|
||||
return None
|
||||
# Bug jedi#427
|
||||
if isinstance(e, UnicodeDecodeError):
|
||||
return None
|
||||
# Bug jedi#429
|
||||
if isinstance(e, IndexError):
|
||||
return None
|
||||
# Bug jedi#431
|
||||
if isinstance(e, AttributeError) and str(e).endswith("'end_pos'"):
|
||||
return None
|
||||
# Bug in Python 2.6, see #275
|
||||
if isinstance(e, OSError) and e.errno == 13:
|
||||
return None
|
||||
# Bug jedi#466
|
||||
if (
|
||||
isinstance(e, SyntaxError) and
|
||||
"EOL while scanning string literal" in str(e)
|
||||
):
|
||||
return None
|
||||
# Bug jedi#482
|
||||
if isinstance(e, UnicodeEncodeError):
|
||||
return None
|
||||
# Bug jedi#485
|
||||
if (
|
||||
isinstance(e, ValueError) and
|
||||
"invalid \\x escape" in str(e)
|
||||
):
|
||||
return None
|
||||
# Bug jedi#485 in Python 3
|
||||
if (
|
||||
isinstance(e, SyntaxError) and
|
||||
"truncated \\xXX escape" in str(e)
|
||||
):
|
||||
return None
|
||||
# Bug jedi#465
|
||||
if (
|
||||
isinstance(e, SyntaxError) and
|
||||
"encoding declaration in Unicode string" in str(e)
|
||||
):
|
||||
return None
|
||||
# Bug #337 / jedi#471
|
||||
if (
|
||||
isinstance(e, ImportError) and
|
||||
"No module named" in str(e)
|
||||
):
|
||||
return None
|
||||
# Bug #365 / jedi#486 - fixed in Jedi 0.8.2
|
||||
if (
|
||||
isinstance(e, UnboundLocalError) and
|
||||
"local variable 'path' referenced before assignment" in str(e)
|
||||
):
|
||||
return None
|
||||
# Bug #366 / jedi#491
|
||||
if (
|
||||
isinstance(e, ValueError) and
|
||||
"__loader__ is None" in str(e)
|
||||
):
|
||||
return None
|
||||
# Bug #353
|
||||
if (
|
||||
isinstance(e, OSError) and
|
||||
"No such file or directory" in str(e)
|
||||
):
|
||||
return None
|
||||
# Bug #561, #564, #570, #588, #593, #599 / jedi#572, jedi#579, jedi#590
|
||||
if isinstance(e, KeyError):
|
||||
return None
|
||||
# Bug #519 / jedi#610
|
||||
if (
|
||||
isinstance(e, RuntimeError) and
|
||||
"maximum recursion depth exceeded" in str(e)
|
||||
):
|
||||
return None
|
||||
# Bug #563 / jedi#589
|
||||
if (
|
||||
isinstance(e, AttributeError) and
|
||||
"MergedNamesDict" in str(e)
|
||||
):
|
||||
return None
|
||||
# Bug #615 / jedi#592
|
||||
if (
|
||||
isinstance(e, AttributeError) and
|
||||
"ListComprehension" in str(e)
|
||||
):
|
||||
return None
|
||||
# Bug #569 / jedi#593
|
||||
if (
|
||||
isinstance(e, AttributeError) and
|
||||
"names_dict" in str(e)
|
||||
):
|
||||
return None
|
||||
|
||||
from jedi import debug
|
||||
|
||||
debug_info = []
|
||||
|
||||
def _debug(level, str_out):
|
||||
if level == debug.NOTICE:
|
||||
prefix = "[N]"
|
||||
elif level == debug.WARNING:
|
||||
prefix = "[W]"
|
||||
else:
|
||||
prefix = "[?]"
|
||||
debug_info.append(u"{0} {1}".format(prefix, str_out))
|
||||
|
||||
jedi.set_debug_function(_debug, speed=False)
|
||||
try:
|
||||
script = jedi.Script(*args, **kwargs)
|
||||
return getattr(script, name)()
|
||||
except Exception as e:
|
||||
source = kwargs.get('source')
|
||||
sc_args = []
|
||||
sc_args.extend(repr(arg) for arg in args)
|
||||
sc_args.extend("{0}={1}".format(k, "source" if k == "source"
|
||||
else repr(v))
|
||||
for (k, v) in kwargs.items())
|
||||
|
||||
data = {
|
||||
"traceback": traceback.format_exc(),
|
||||
"jedi_debug_info": {'script_args': ", ".join(sc_args),
|
||||
'source': source,
|
||||
'method': name,
|
||||
'debug_info': debug_info}
|
||||
}
|
||||
raise rpc.Fault(message=str(e),
|
||||
code=500,
|
||||
data=data)
|
||||
finally:
|
||||
jedi.set_debug_function(None)
|
|
@ -0,0 +1,91 @@
|
|||
import sys
|
||||
import types
|
||||
|
||||
from pydoc import safeimport, resolve, ErrorDuringImport
|
||||
from pkgutil import iter_modules
|
||||
|
||||
from elpy import compat
|
||||
|
||||
# Types we want to recurse into (nodes).
|
||||
CONTAINER_TYPES = (type, types.ModuleType)
|
||||
# Types of attributes we can get documentation for (leaves).
|
||||
PYDOC_TYPES = (type,
|
||||
types.FunctionType,
|
||||
types.BuiltinFunctionType,
|
||||
types.BuiltinMethodType,
|
||||
types.MethodType,
|
||||
types.ModuleType)
|
||||
if not compat.PYTHON3: # pragma: nocover
|
||||
# Python 2 old style classes
|
||||
CONTAINER_TYPES = tuple(list(CONTAINER_TYPES) + [types.ClassType])
|
||||
PYDOC_TYPES = tuple(list(PYDOC_TYPES) + [types.ClassType])
|
||||
|
||||
|
||||
def get_pydoc_completions(modulename):
|
||||
"""Get possible completions for modulename for pydoc.
|
||||
|
||||
Returns a list of possible values to be passed to pydoc.
|
||||
|
||||
"""
|
||||
modulename = compat.ensure_not_unicode(modulename)
|
||||
modulename = modulename.rstrip(".")
|
||||
if modulename == "":
|
||||
return sorted(get_modules())
|
||||
candidates = get_completions(modulename)
|
||||
if candidates:
|
||||
return sorted(candidates)
|
||||
needle = modulename
|
||||
if "." in needle:
|
||||
modulename, part = needle.rsplit(".", 1)
|
||||
candidates = get_completions(modulename)
|
||||
else:
|
||||
candidates = get_modules()
|
||||
return sorted(candidate for candidate in candidates
|
||||
if candidate.startswith(needle))
|
||||
|
||||
|
||||
def get_completions(modulename):
|
||||
modules = set("{0}.{1}".format(modulename, module)
|
||||
for module in get_modules(modulename))
|
||||
|
||||
try:
|
||||
module, name = resolve(modulename)
|
||||
except ImportError:
|
||||
return modules
|
||||
if isinstance(module, CONTAINER_TYPES):
|
||||
modules.update("{0}.{1}".format(modulename, name)
|
||||
for name in dir(module)
|
||||
if not name.startswith("_") and
|
||||
isinstance(getattr(module, name),
|
||||
PYDOC_TYPES))
|
||||
return modules
|
||||
|
||||
|
||||
def get_modules(modulename=None):
|
||||
"""Return a list of modules and packages under modulename.
|
||||
|
||||
If modulename is not given, return a list of all top level modules
|
||||
and packages.
|
||||
|
||||
"""
|
||||
modulename = compat.ensure_not_unicode(modulename)
|
||||
if not modulename:
|
||||
try:
|
||||
return ([modname for (importer, modname, ispkg)
|
||||
in iter_modules()
|
||||
if not modname.startswith("_")] +
|
||||
list(sys.builtin_module_names))
|
||||
except OSError:
|
||||
# Bug in Python 2.6, see #275
|
||||
return list(sys.builtin_module_names)
|
||||
try:
|
||||
module = safeimport(modulename)
|
||||
except ErrorDuringImport:
|
||||
return []
|
||||
if module is None:
|
||||
return []
|
||||
if hasattr(module, "__path__"):
|
||||
return [modname for (importer, modname, ispkg)
|
||||
in iter_modules(module.__path__)
|
||||
if not modname.startswith("_")]
|
||||
return []
|
|
@ -0,0 +1,381 @@
|
|||
"""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()
|
|
@ -0,0 +1,290 @@
|
|||
"""Elpy backend using the Rope library.
|
||||
|
||||
This backend uses the Rope library:
|
||||
|
||||
http://rope.sourceforge.net/
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
import rope.contrib.codeassist
|
||||
import rope.base.project
|
||||
import rope.base.libutils
|
||||
import rope.base.exceptions
|
||||
import rope.contrib.findit
|
||||
|
||||
from elpy import rpc
|
||||
import elpy.pydocutils
|
||||
|
||||
VALIDATE_EVERY_SECONDS = 5
|
||||
MAXFIXES = 5
|
||||
|
||||
|
||||
class RopeBackend(object):
|
||||
"""The Rope backend class.
|
||||
|
||||
Implements the RPC calls we can pass on to Rope. Also subclasses
|
||||
the native backend to provide methods Rope does not provide, if
|
||||
any.
|
||||
|
||||
"""
|
||||
name = "rope"
|
||||
|
||||
def __init__(self, project_root):
|
||||
super(RopeBackend, self).__init__()
|
||||
self.last_validation = 0
|
||||
if not os.path.exists(project_root):
|
||||
raise rpc.Fault(
|
||||
"rope does not support files without a local project root",
|
||||
code=400
|
||||
)
|
||||
self.project_root = project_root
|
||||
self.completions = {}
|
||||
prefs = dict(ignored_resources=['*.pyc', '*~', '.ropeproject',
|
||||
'.hg', '.svn', '_svn', '.git'],
|
||||
python_files=['*.py'],
|
||||
save_objectdb=False,
|
||||
compress_objectdb=False,
|
||||
automatic_soa=True,
|
||||
soa_followed_calls=0,
|
||||
perform_doa=True,
|
||||
validate_objectdb=True,
|
||||
max_history_items=32,
|
||||
save_history=False,
|
||||
compress_history=False,
|
||||
indent_size=4,
|
||||
extension_modules=[],
|
||||
import_dynload_stdmods=True,
|
||||
ignore_syntax_errors=False,
|
||||
ignore_bad_imports=False)
|
||||
self.project = rope.base.project.Project(self.project_root,
|
||||
ropefolder=None,
|
||||
**prefs)
|
||||
|
||||
def get_resource(self, filename):
|
||||
if filename is not None and os.path.exists(filename):
|
||||
return rope.base.libutils.path_to_resource(self.project,
|
||||
filename,
|
||||
'file')
|
||||
else:
|
||||
return None
|
||||
|
||||
def validate(self):
|
||||
"""Validate the stored project.
|
||||
|
||||
This should be called before every use of Rope. It will
|
||||
revalidate the project, but do some call throttling.
|
||||
|
||||
"""
|
||||
now = time.time()
|
||||
if now > self.last_validation + VALIDATE_EVERY_SECONDS:
|
||||
try:
|
||||
self.project.validate()
|
||||
except rope.base.exceptions.ResourceNotFoundError:
|
||||
pass
|
||||
self.last_validation = now
|
||||
|
||||
def call_rope(self, rope_function, filename, source, offset,
|
||||
**kwargs):
|
||||
self.validate()
|
||||
resource = self.get_resource(filename)
|
||||
try:
|
||||
return rope_function(self.project,
|
||||
source, offset,
|
||||
resource,
|
||||
maxfixes=MAXFIXES,
|
||||
**kwargs)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def rpc_get_completions(self, filename, source, offset):
|
||||
proposals = self.call_rope(
|
||||
rope.contrib.codeassist.code_assist,
|
||||
filename, source, offset
|
||||
)
|
||||
if proposals is None:
|
||||
return []
|
||||
try:
|
||||
starting_offset = rope.contrib.codeassist.starting_offset(source,
|
||||
offset)
|
||||
except Exception:
|
||||
return []
|
||||
prefixlen = offset - starting_offset
|
||||
try:
|
||||
self.completions = dict((proposal.name, proposal)
|
||||
for proposal in proposals)
|
||||
return [{'name': proposal.name,
|
||||
'suffix': proposal.name[prefixlen:],
|
||||
'annotation': proposal.type,
|
||||
'meta': str(proposal)}
|
||||
for proposal in proposals]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
def rpc_get_completion_docstring(self, completion):
|
||||
proposal = self.completions.get(completion)
|
||||
if proposal is None:
|
||||
return None
|
||||
else:
|
||||
return proposal.get_doc()
|
||||
|
||||
def rpc_get_completion_location(self, completion):
|
||||
proposal = self.completions.get(completion)
|
||||
if proposal is None:
|
||||
return None
|
||||
else:
|
||||
if not proposal.pyname:
|
||||
return None
|
||||
module, lineno = proposal.pyname.get_definition_location()
|
||||
if module is None:
|
||||
return None
|
||||
resource = module.get_module().get_resource()
|
||||
return (resource.real_path, lineno)
|
||||
|
||||
def rpc_get_definition(self, filename, source, offset):
|
||||
location = self.call_rope(
|
||||
rope.contrib.findit.find_definition,
|
||||
filename, source, offset
|
||||
)
|
||||
if location is None:
|
||||
return None
|
||||
else:
|
||||
return (location.resource.real_path, location.offset)
|
||||
|
||||
def rpc_get_calltip(self, filename, source, offset):
|
||||
offset = find_called_name_offset(source, offset)
|
||||
if 0 < offset < len(source) and source[offset] == ')':
|
||||
offset -= 1
|
||||
|
||||
calltip = self.call_rope(
|
||||
rope.contrib.codeassist.get_calltip,
|
||||
filename, source, offset,
|
||||
remove_self=True
|
||||
)
|
||||
if calltip is None:
|
||||
return None
|
||||
|
||||
calltip = calltip.replace(".__init__(", "(")
|
||||
calltip = calltip.replace("(self)", "()")
|
||||
calltip = calltip.replace("(self, ", "(")
|
||||
# "elpy.tests.support.source_and_offset(source)"
|
||||
# =>
|
||||
# "support.source_and_offset(source)"
|
||||
try:
|
||||
openpos = calltip.index("(")
|
||||
period2 = calltip.rindex(".", 0, openpos)
|
||||
period1 = calltip.rindex(".", 0, period2)
|
||||
calltip = calltip[period1 + 1:]
|
||||
except ValueError:
|
||||
pass
|
||||
return calltip
|
||||
|
||||
def rpc_get_docstring(self, filename, source, offset):
|
||||
return self.call_rope(
|
||||
rope.contrib.codeassist.get_doc,
|
||||
filename, source, offset
|
||||
)
|
||||
|
||||
|
||||
def find_called_name_offset(source, orig_offset):
|
||||
"""Return the offset of a calling function.
|
||||
|
||||
This only approximates movement.
|
||||
|
||||
"""
|
||||
offset = min(orig_offset, len(source) - 1)
|
||||
paren_count = 0
|
||||
while True:
|
||||
if offset <= 1:
|
||||
return orig_offset
|
||||
elif source[offset] == '(':
|
||||
if paren_count == 0:
|
||||
return offset - 1
|
||||
else:
|
||||
paren_count -= 1
|
||||
elif source[offset] == ')':
|
||||
paren_count += 1
|
||||
offset -= 1
|
||||
|
||||
|
||||
##################################################################
|
||||
# A recurring problem in Rope for Elpy is that it searches the whole
|
||||
# project root for Python files. If the user edits a file in their
|
||||
# home directory, this can easily read a whole lot of files, making
|
||||
# Rope practically useless. We change the file finding algorithm here
|
||||
# to only recurse into directories with an __init__.py file in them.
|
||||
def find_source_folders(self, folder):
|
||||
for resource in folder.get_folders():
|
||||
if self._is_package(resource):
|
||||
return [folder]
|
||||
result = []
|
||||
for resource in folder.get_files():
|
||||
if resource.name.endswith('.py'):
|
||||
result.append(folder)
|
||||
break
|
||||
for resource in folder.get_folders():
|
||||
if self._is_package(resource):
|
||||
result.append(resource)
|
||||
return result
|
||||
|
||||
import rope.base.pycore
|
||||
rope.base.pycore.PyCore._find_source_folders = find_source_folders
|
||||
|
||||
|
||||
def get_files(self):
|
||||
if self.files is None:
|
||||
self.files = get_python_project_files(self.project)
|
||||
return self.files
|
||||
|
||||
rope.base.project._FileListCacher.get_files = get_files
|
||||
|
||||
|
||||
def get_python_project_files(project):
|
||||
for dirname, subdirs, files in os.walk(project.root.real_path):
|
||||
for filename in files:
|
||||
yield rope.base.libutils.path_to_resource(
|
||||
project, os.path.join(dirname, filename), 'file')
|
||||
subdirs[:] = [subdir for subdir in subdirs
|
||||
if os.path.exists(os.path.join(dirname, subdir,
|
||||
"__init__.py"))]
|
||||
|
||||
|
||||
##################################################################
|
||||
# Monkey patching a method in rope because it doesn't complete import
|
||||
# statements.
|
||||
|
||||
orig_code_completions = (rope.contrib.codeassist.
|
||||
_PythonCodeAssist._code_completions)
|
||||
|
||||
|
||||
def code_completions(self):
|
||||
proposals = get_import_completions(self)
|
||||
if proposals:
|
||||
return proposals
|
||||
else:
|
||||
return orig_code_completions(self)
|
||||
|
||||
|
||||
def get_import_completions(self):
|
||||
if not self.word_finder.is_import_statement(self.offset):
|
||||
return []
|
||||
modulename = self.word_finder.get_primary_at(self.offset)
|
||||
# Rope can handle modules in packages
|
||||
if "." in modulename:
|
||||
return []
|
||||
return dict((name, FakeProposal(name))
|
||||
for name in elpy.pydocutils.get_modules()
|
||||
if name.startswith(modulename))
|
||||
|
||||
|
||||
class FakeProposal(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.type = "mock"
|
||||
|
||||
def get_doc(self):
|
||||
return None
|
||||
|
||||
rope.contrib.codeassist._PythonCodeAssist._code_completions = code_completions
|
|
@ -0,0 +1,151 @@
|
|||
"""A simple JSON-RPC-like server.
|
||||
|
||||
The server will read and write lines of JSON-encoded method calls and
|
||||
responses.
|
||||
|
||||
See the documentation of the JSONRPCServer class for further details.
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
|
||||
class JSONRPCServer(object):
|
||||
"""Simple JSON-RPC-like server.
|
||||
|
||||
This class will read single-line JSON expressions from stdin,
|
||||
decode them, and pass them to a handler. Return values from the
|
||||
handler will be JSON-encoded and written to stdout.
|
||||
|
||||
To implement a handler, you need to subclass this class and add
|
||||
methods starting with "rpc_". Methods then will be found.
|
||||
|
||||
Method calls should be encoded like this:
|
||||
|
||||
{"id": 23, "method": "method_name", "params": ["foo", "bar"]}
|
||||
|
||||
This will call self.rpc_method("foo", "bar").
|
||||
|
||||
Responses will be encoded like this:
|
||||
|
||||
{"id": 23, "result": "foo"}
|
||||
|
||||
Errors will be encoded like this:
|
||||
|
||||
{"id": 23, "error": "Simple error message"}
|
||||
|
||||
See http://www.jsonrpc.org/ for the inspiration of the protocol.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, stdin=None, stdout=None):
|
||||
"""Return a new JSON-RPC server object.
|
||||
|
||||
It will read lines of JSON data from stdin, and write the
|
||||
responses to stdout.
|
||||
|
||||
"""
|
||||
if stdin is None:
|
||||
self.stdin = sys.stdin
|
||||
else:
|
||||
self.stdin = stdin
|
||||
if stdout is None:
|
||||
self.stdout = sys.stdout
|
||||
else:
|
||||
self.stdout = stdout
|
||||
|
||||
def read_json(self):
|
||||
"""Read a single line and decode it as JSON.
|
||||
|
||||
Can raise an EOFError() when the input source was closed.
|
||||
|
||||
"""
|
||||
line = self.stdin.readline()
|
||||
if line == '':
|
||||
raise EOFError()
|
||||
return json.loads(line)
|
||||
|
||||
def write_json(self, **kwargs):
|
||||
"""Write an JSON object on a single line.
|
||||
|
||||
The keyword arguments are interpreted as a single JSON object.
|
||||
It's not possible with this method to write non-objects.
|
||||
|
||||
"""
|
||||
self.stdout.write(json.dumps(kwargs) + "\n")
|
||||
self.stdout.flush()
|
||||
|
||||
def handle_request(self):
|
||||
"""Handle a single JSON-RPC request.
|
||||
|
||||
Read a request, call the appropriate handler method, and
|
||||
return the encoded result. Errors in the handler method are
|
||||
caught and encoded as error objects. Errors in the decoding
|
||||
phase are not caught, as we can not respond with an error
|
||||
response to them.
|
||||
|
||||
"""
|
||||
request = self.read_json()
|
||||
if 'method' not in request:
|
||||
raise ValueError("Received a bad request: {0}"
|
||||
.format(request))
|
||||
method_name = request['method']
|
||||
request_id = request.get('id', None)
|
||||
params = request.get('params') or []
|
||||
try:
|
||||
method = getattr(self, "rpc_" + method_name, None)
|
||||
if method is not None:
|
||||
result = method(*params)
|
||||
else:
|
||||
result = self.handle(method_name, params)
|
||||
if request_id is not None:
|
||||
self.write_json(result=result,
|
||||
id=request_id)
|
||||
except Fault as fault:
|
||||
error = {"message": fault.message,
|
||||
"code": fault.code}
|
||||
if fault.data is not None:
|
||||
error["data"] = fault.data
|
||||
self.write_json(error=error, id=request_id)
|
||||
except Exception as e:
|
||||
error = {"message": str(e),
|
||||
"code": 500,
|
||||
"data": {"traceback": traceback.format_exc()}}
|
||||
self.write_json(error=error, id=request_id)
|
||||
|
||||
def handle(self, method_name, args):
|
||||
"""Handle the call to method_name.
|
||||
|
||||
You should overwrite this method in a subclass.
|
||||
"""
|
||||
raise Fault("Unknown method {0}".format(method_name))
|
||||
|
||||
def serve_forever(self):
|
||||
"""Serve requests forever.
|
||||
|
||||
Errors are not caught, so this is a slight misnomer.
|
||||
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
self.handle_request()
|
||||
except (KeyboardInterrupt, EOFError, SystemExit):
|
||||
break
|
||||
|
||||
|
||||
class Fault(Exception):
|
||||
"""RPC Fault instances.
|
||||
|
||||
code defines the severity of the warning.
|
||||
|
||||
2xx: Normal behavior lead to end of operation, i.e. a warning
|
||||
4xx: An expected error occurred
|
||||
5xx: An unexpected error occurred (usually includes a traceback)
|
||||
"""
|
||||
def __init__(self, message, code=500, data=None):
|
||||
super(Fault, self).__init__(message)
|
||||
self.message = message
|
||||
self.code = code
|
||||
self.data = data
|
|
@ -0,0 +1,322 @@
|
|||
"""Method implementations for the Elpy JSON-RPC server.
|
||||
|
||||
This file implements the methods exported by the JSON-RPC server. It
|
||||
handles backend selection and passes methods on to the selected
|
||||
backend.
|
||||
|
||||
"""
|
||||
import io
|
||||
import os
|
||||
import pydoc
|
||||
|
||||
from elpy.pydocutils import get_pydoc_completions
|
||||
from elpy.rpc import JSONRPCServer, Fault
|
||||
from elpy.impmagic import ImportMagic, ImportMagicError
|
||||
from elpy.auto_pep8 import fix_code
|
||||
from elpy.yapfutil import fix_code as fix_code_with_yapf
|
||||
|
||||
|
||||
try:
|
||||
from elpy import jedibackend
|
||||
except ImportError: # pragma: no cover
|
||||
jedibackend = None
|
||||
|
||||
try:
|
||||
from elpy import ropebackend
|
||||
except ImportError: # pragma: no cover
|
||||
ropebackend = None
|
||||
|
||||
|
||||
class ElpyRPCServer(JSONRPCServer):
|
||||
"""The RPC server for elpy.
|
||||
|
||||
See the rpc_* methods for exported method documentation.
|
||||
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ElpyRPCServer, self).__init__(*args, **kwargs)
|
||||
self.backend = None
|
||||
self.import_magic = ImportMagic()
|
||||
self.project_root = None
|
||||
|
||||
def _call_backend(self, method, default, *args, **kwargs):
|
||||
"""Call the backend method with args.
|
||||
|
||||
If there is currently no backend, return default."""
|
||||
meth = getattr(self.backend, method, None)
|
||||
if meth is None:
|
||||
return default
|
||||
else:
|
||||
return meth(*args, **kwargs)
|
||||
|
||||
def rpc_echo(self, *args):
|
||||
"""Return the arguments.
|
||||
|
||||
This is a simple test method to see if the protocol is
|
||||
working.
|
||||
|
||||
"""
|
||||
return args
|
||||
|
||||
def rpc_init(self, options):
|
||||
self.project_root = options["project_root"]
|
||||
|
||||
if self.import_magic.is_enabled:
|
||||
self.import_magic.build_index(self.project_root)
|
||||
|
||||
if ropebackend and options["backend"] == "rope":
|
||||
self.backend = ropebackend.RopeBackend(self.project_root)
|
||||
elif jedibackend and options["backend"] == "jedi":
|
||||
self.backend = jedibackend.JediBackend(self.project_root)
|
||||
elif ropebackend:
|
||||
self.backend = ropebackend.RopeBackend(self.project_root)
|
||||
elif jedibackend:
|
||||
self.backend = jedibackend.JediBackend(self.project_root)
|
||||
else:
|
||||
self.backend = None
|
||||
|
||||
return {
|
||||
'backend': (self.backend.name if self.backend is not None
|
||||
else None)
|
||||
}
|
||||
|
||||
def rpc_get_calltip(self, filename, source, offset):
|
||||
"""Get the calltip for the function at the offset.
|
||||
|
||||
"""
|
||||
return self._call_backend("rpc_get_calltip", None, filename,
|
||||
get_source(source), offset)
|
||||
|
||||
def rpc_get_completions(self, filename, source, offset):
|
||||
"""Get a list of completion candidates for the symbol at offset.
|
||||
|
||||
"""
|
||||
results = self._call_backend("rpc_get_completions", [], filename,
|
||||
get_source(source), offset)
|
||||
# Uniquify by name
|
||||
results = list(dict((res['name'], res) for res in results)
|
||||
.values())
|
||||
results.sort(key=lambda cand: _pysymbol_key(cand["name"]))
|
||||
return results
|
||||
|
||||
def rpc_get_completion_docstring(self, completion):
|
||||
"""Return documentation for a previously returned completion.
|
||||
|
||||
"""
|
||||
return self._call_backend("rpc_get_completion_docstring",
|
||||
None, completion)
|
||||
|
||||
def rpc_get_completion_location(self, completion):
|
||||
"""Return the location for a previously returned completion.
|
||||
|
||||
This returns a list of [file name, line number].
|
||||
|
||||
"""
|
||||
return self._call_backend("rpc_get_completion_location", None,
|
||||
completion)
|
||||
|
||||
def rpc_get_definition(self, filename, source, offset):
|
||||
"""Get the location of the definition for the symbol at the offset.
|
||||
|
||||
"""
|
||||
return self._call_backend("rpc_get_definition", None, filename,
|
||||
get_source(source), offset)
|
||||
|
||||
def rpc_get_assignment(self, filename, source, offset):
|
||||
"""Get the location of the assignment for the symbol at the offset.
|
||||
|
||||
"""
|
||||
return self._call_backend("rpc_get_assignment", None, filename,
|
||||
get_source(source), offset)
|
||||
|
||||
def rpc_get_docstring(self, filename, source, offset):
|
||||
"""Get the docstring for the symbol at the offset.
|
||||
|
||||
"""
|
||||
return self._call_backend("rpc_get_docstring", None, filename,
|
||||
get_source(source), offset)
|
||||
|
||||
def rpc_get_pydoc_completions(self, name=None):
|
||||
"""Return a list of possible strings to pass to pydoc.
|
||||
|
||||
If name is given, the strings are under name. If not, top
|
||||
level modules are returned.
|
||||
|
||||
"""
|
||||
return get_pydoc_completions(name)
|
||||
|
||||
def rpc_get_pydoc_documentation(self, symbol):
|
||||
"""Get the Pydoc documentation for the given symbol.
|
||||
|
||||
Uses pydoc and can return a string with backspace characters
|
||||
for bold highlighting.
|
||||
|
||||
"""
|
||||
try:
|
||||
docstring = pydoc.render_doc(str(symbol),
|
||||
"Elpy Pydoc Documentation for %s",
|
||||
False)
|
||||
except (ImportError, pydoc.ErrorDuringImport):
|
||||
return None
|
||||
else:
|
||||
if isinstance(docstring, bytes):
|
||||
docstring = docstring.decode("utf-8", "replace")
|
||||
return docstring
|
||||
|
||||
def rpc_get_refactor_options(self, filename, start, end=None):
|
||||
"""Return a list of possible refactoring options.
|
||||
|
||||
This list will be filtered depending on whether it's
|
||||
applicable at the point START and possibly the region between
|
||||
START and END.
|
||||
|
||||
"""
|
||||
try:
|
||||
from elpy import refactor
|
||||
except:
|
||||
raise ImportError("Rope not installed, refactorings unavailable")
|
||||
ref = refactor.Refactor(self.project_root, filename)
|
||||
return ref.get_refactor_options(start, end)
|
||||
|
||||
def rpc_refactor(self, filename, method, args):
|
||||
"""Return a list of changes from the refactoring action.
|
||||
|
||||
A change is a dictionary describing the change. See
|
||||
elpy.refactor.translate_changes for a description.
|
||||
|
||||
"""
|
||||
try:
|
||||
from elpy import refactor
|
||||
except:
|
||||
raise ImportError("Rope not installed, refactorings unavailable")
|
||||
if args is None:
|
||||
args = ()
|
||||
ref = refactor.Refactor(self.project_root, filename)
|
||||
return ref.get_changes(method, *args)
|
||||
|
||||
def rpc_get_usages(self, filename, source, offset):
|
||||
"""Get usages for the symbol at point.
|
||||
|
||||
"""
|
||||
source = get_source(source)
|
||||
if hasattr(self.backend, "rpc_get_usages"):
|
||||
return self.backend.rpc_get_usages(filename, source, offset)
|
||||
else:
|
||||
raise Fault("get_usages not implemented by current backend",
|
||||
code=400)
|
||||
|
||||
def rpc_get_names(self, filename, source, offset):
|
||||
"""Get all possible names
|
||||
|
||||
"""
|
||||
source = get_source(source)
|
||||
if hasattr(self.backend, "rpc_get_names"):
|
||||
return self.backend.rpc_get_names(filename, source, offset)
|
||||
else:
|
||||
raise Fault("get_names not implemented by current backend",
|
||||
code=400)
|
||||
|
||||
def _ensure_import_magic(self): # pragma: no cover
|
||||
if not self.import_magic.is_enabled:
|
||||
raise Fault("fixup_imports not enabled; install importmagic module",
|
||||
code=400)
|
||||
if not self.import_magic.symbol_index:
|
||||
raise Fault(self.import_magic.fail_message, code=200) # XXX code?
|
||||
|
||||
def rpc_get_import_symbols(self, filename, source, symbol):
|
||||
"""Return a list of modules from which the given symbol can be imported.
|
||||
|
||||
"""
|
||||
self._ensure_import_magic()
|
||||
try:
|
||||
return self.import_magic.get_import_symbols(symbol)
|
||||
except ImportMagicError as err:
|
||||
raise Fault(str(err), code=200)
|
||||
|
||||
def rpc_add_import(self, filename, source, statement):
|
||||
"""Add an import statement to the module.
|
||||
|
||||
"""
|
||||
self._ensure_import_magic()
|
||||
source = get_source(source)
|
||||
try:
|
||||
return self.import_magic.add_import(source, statement)
|
||||
except ImportMagicError as err:
|
||||
raise Fault(str(err), code=200)
|
||||
|
||||
def rpc_get_unresolved_symbols(self, filename, source):
|
||||
"""Return a list of unreferenced symbols in the module.
|
||||
|
||||
"""
|
||||
self._ensure_import_magic()
|
||||
source = get_source(source)
|
||||
try:
|
||||
return self.import_magic.get_unresolved_symbols(source)
|
||||
except ImportMagicError as err:
|
||||
raise Fault(str(err), code=200)
|
||||
|
||||
def rpc_remove_unreferenced_imports(self, filename, source):
|
||||
"""Remove unused import statements.
|
||||
|
||||
"""
|
||||
self._ensure_import_magic()
|
||||
source = get_source(source)
|
||||
try:
|
||||
return self.import_magic.remove_unreferenced_imports(source)
|
||||
except ImportMagicError as err:
|
||||
raise Fault(str(err), code=200)
|
||||
|
||||
def rpc_fix_code(self, source):
|
||||
"""Formats Python code to conform to the PEP 8 style guide.
|
||||
|
||||
"""
|
||||
source = get_source(source)
|
||||
return fix_code(source)
|
||||
|
||||
def rpc_fix_code_with_yapf(self, source):
|
||||
"""Formats Python code to conform to the PEP 8 style guide.
|
||||
|
||||
"""
|
||||
source = get_source(source)
|
||||
return fix_code_with_yapf(source)
|
||||
|
||||
|
||||
def get_source(fileobj):
|
||||
"""Translate fileobj into file contents.
|
||||
|
||||
fileobj is either a string or a dict. If it's a string, that's the
|
||||
file contents. If it's a string, then the filename key contains
|
||||
the name of the file whose contents we are to use.
|
||||
|
||||
If the dict contains a true value for the key delete_after_use,
|
||||
the file should be deleted once read.
|
||||
|
||||
"""
|
||||
if not isinstance(fileobj, dict):
|
||||
return fileobj
|
||||
else:
|
||||
try:
|
||||
with io.open(fileobj["filename"], encoding="utf-8",
|
||||
errors="ignore") as f:
|
||||
return f.read()
|
||||
finally:
|
||||
if fileobj.get('delete_after_use'):
|
||||
try:
|
||||
os.remove(fileobj["filename"])
|
||||
except: # pragma: no cover
|
||||
pass
|
||||
|
||||
|
||||
def _pysymbol_key(name):
|
||||
"""Return a sortable key index for name.
|
||||
|
||||
Sorting is case-insensitive, with the first underscore counting as
|
||||
worse than any character, but subsequent underscores do not. This
|
||||
means that dunder symbols (like __init__) are sorted after symbols
|
||||
that start with an alphabetic character, but before those that
|
||||
start with only a single underscore.
|
||||
|
||||
"""
|
||||
if name.startswith("_"):
|
||||
name = "~" + name[1:]
|
||||
return name.lower()
|
|
@ -0,0 +1,8 @@
|
|||
"""Unit tests for elpy."""
|
||||
|
||||
try:
|
||||
import unittest2
|
||||
import sys
|
||||
sys.modules['unittest'] = unittest2
|
||||
except:
|
||||
pass
|
|
@ -0,0 +1,18 @@
|
|||
"""Python 2/3 compatibility definitions.
|
||||
|
||||
These are used by the rest of Elpy to keep compatibility definitions
|
||||
in one place.
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
if sys.version_info >= (3, 0):
|
||||
PYTHON3 = True
|
||||
import builtins
|
||||
from io import StringIO
|
||||
else:
|
||||
PYTHON3 = False
|
||||
import __builtin__ as builtins # noqa
|
||||
from StringIO import StringIO # noqa
|
|
@ -0,0 +1,950 @@
|
|||
# coding: utf-8
|
||||
|
||||
"""Support classes and functions for the elpy test code.
|
||||
|
||||
Elpy uses a bit of a peculiar test setup to avoid redundancy. For the
|
||||
tests of the two backends, we provide generic test cases for generic
|
||||
tests and for specific callback tests.
|
||||
|
||||
These mixins can be included in the actual test classes. We can't add
|
||||
these tests to a BackendTestCase subclass directly because the test
|
||||
discovery would find them there and try to run them, which would fail.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from elpy.tests import compat
|
||||
|
||||
|
||||
class BackendTestCase(unittest.TestCase):
|
||||
"""Base class for backend tests.
|
||||
|
||||
This class sets up a project root directory and provides an easy
|
||||
way to create files within the project root.
|
||||
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""Create the project root and make sure it gets cleaned up."""
|
||||
super(BackendTestCase, self).setUp()
|
||||
self.project_root = tempfile.mkdtemp(prefix="elpy-test")
|
||||
self.addCleanup(shutil.rmtree, self.project_root, True)
|
||||
|
||||
def project_file(self, relname, contents):
|
||||
"""Create a file named relname within the project root.
|
||||
|
||||
Write contents into that file.
|
||||
|
||||
"""
|
||||
full_name = os.path.join(self.project_root, relname)
|
||||
try:
|
||||
os.makedirs(os.path.dirname(full_name))
|
||||
except OSError:
|
||||
pass
|
||||
if compat.PYTHON3:
|
||||
fobj = open(full_name, "w", encoding="utf-8")
|
||||
else:
|
||||
fobj = open(full_name, "w")
|
||||
with fobj as f:
|
||||
f.write(contents)
|
||||
return full_name
|
||||
|
||||
|
||||
class GenericRPCTests(object):
|
||||
"""Generic RPC test methods.
|
||||
|
||||
This is a mixin to add tests that should be run for all RPC
|
||||
methods that follow the generic (filename, source, offset) calling
|
||||
conventions.
|
||||
|
||||
"""
|
||||
METHOD = None
|
||||
|
||||
def rpc(self, filename, source, offset):
|
||||
method = getattr(self.backend, self.METHOD)
|
||||
return method(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_on_inexisting_file(self):
|
||||
filename = self.project_root + "/doesnotexist.py"
|
||||
self.rpc(filename, "", 0)
|
||||
|
||||
def test_should_not_fail_on_empty_file(self):
|
||||
filename = self.project_file("test.py", "")
|
||||
self.rpc(filename, "", 0)
|
||||
|
||||
def test_should_not_fail_if_file_is_none(self):
|
||||
self.rpc(None, "", 0)
|
||||
|
||||
def test_should_not_fail_for_module_syntax_errors(self):
|
||||
source, offset = source_and_offset(
|
||||
"class Foo(object):\n"
|
||||
" def bar(self):\n"
|
||||
" foo(_|_"
|
||||
" bar("
|
||||
"\n"
|
||||
" def a(self):\n"
|
||||
" pass\n"
|
||||
"\n"
|
||||
" def b(self):\n"
|
||||
" pass\n"
|
||||
"\n"
|
||||
" def b(self):\n"
|
||||
" pass\n"
|
||||
"\n"
|
||||
" def b(self):\n"
|
||||
" pass\n"
|
||||
"\n"
|
||||
" def b(self):\n"
|
||||
" pass\n"
|
||||
"\n"
|
||||
" def b(self):\n"
|
||||
" pass\n"
|
||||
)
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_for_bad_indentation(self):
|
||||
# Bug in Rope: rope#80
|
||||
source, offset = source_and_offset(
|
||||
"def foo():\n"
|
||||
" print(23)_|_\n"
|
||||
" print(17)\n")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
@unittest.skipIf((3, 3) <= sys.version_info < (3, 4),
|
||||
"Bug in jedi for Python 3.3")
|
||||
def test_should_not_fail_for_relative_import(self):
|
||||
# Bug in Rope: rope#81 and rope#82
|
||||
source, offset = source_and_offset(
|
||||
"from .. import foo_|_"
|
||||
)
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_on_keyword(self):
|
||||
source, offset = source_and_offset(
|
||||
"_|_try:\n"
|
||||
" pass\n"
|
||||
"except:\n"
|
||||
" pass\n")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_with_bad_encoding(self):
|
||||
# Bug in Rope: rope#83
|
||||
source, offset = source_and_offset(
|
||||
u'# coding: utf-8X_|_\n'
|
||||
)
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_with_form_feed_characters(self):
|
||||
# Bug in Jedi: jedi#424
|
||||
source, offset = source_and_offset(
|
||||
"\f\n"
|
||||
"class Test(object):_|_\n"
|
||||
" pass"
|
||||
)
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_for_dictionaries_in_weird_places(self):
|
||||
# Bug in Jedi: jedi#417
|
||||
source, offset = source_and_offset(
|
||||
"import json\n"
|
||||
"\n"
|
||||
"def foo():\n"
|
||||
" json.loads(_|_\n"
|
||||
"\n"
|
||||
" json.load.return_value = {'foo': [],\n"
|
||||
" 'bar': True}\n"
|
||||
"\n"
|
||||
" c = Foo()\n"
|
||||
)
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_break_with_binary_characters_in_docstring(self):
|
||||
# Bug in Jedi: jedi#427
|
||||
template = '''\
|
||||
class Foo(object):
|
||||
def __init__(self):
|
||||
"""
|
||||
COMMUNITY instance that this conversion belongs to.
|
||||
DISPERSY_VERSION is the dispersy conversion identifier (on the wire version; must be one byte).
|
||||
COMMUNIY_VERSION is the community conversion identifier (on the wire version; must be one byte).
|
||||
|
||||
COMMUNIY_VERSION may not be '\\x00' or '\\xff'. '\\x00' is used by the DefaultConversion until
|
||||
a proper conversion instance can be made for the Community. '\\xff' is reserved for when
|
||||
more than one byte is needed as a version indicator.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
x = Foo()
|
||||
x._|_
|
||||
'''
|
||||
source, offset = source_and_offset(template)
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_for_def_without_name(self):
|
||||
# Bug jedi#429
|
||||
source, offset = source_and_offset(
|
||||
"def_|_():\n"
|
||||
" if True:\n"
|
||||
" return True\n"
|
||||
" else:\n"
|
||||
" return False\n"
|
||||
)
|
||||
filename = self.project_file("project.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_on_lambda(self):
|
||||
# Bug #272 / jedi#431, jedi#572
|
||||
source, offset = source_and_offset(
|
||||
"map(lambda_|_"
|
||||
)
|
||||
filename = self.project_file("project.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_on_literals(self):
|
||||
# Bug #314, #344 / jedi#466
|
||||
source = u'lit = u"""\\\n# -*- coding: utf-8 -*-\n"""\n'
|
||||
offset = 0
|
||||
filename = self.project_file("project.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_with_args_as_args(self):
|
||||
# Bug #347 in rope_py3k
|
||||
source, offset = source_and_offset(
|
||||
"def my_function(*args):\n"
|
||||
" ret_|_"
|
||||
)
|
||||
filename = self.project_file("project.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_for_unicode_chars_in_string(self):
|
||||
# Bug #358 / jedi#482
|
||||
source = '''\
|
||||
# coding: utf-8
|
||||
|
||||
logging.info(u"Saving «{}»...".format(title))
|
||||
requests.get(u"https://web.archive.org/save/{}".format(url))
|
||||
'''
|
||||
offset = 57
|
||||
filename = self.project_file("project.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_for_bad_escape_sequence(self):
|
||||
# Bug #360 / jedi#485
|
||||
source = r"v = '\x'"
|
||||
offset = 8
|
||||
filename = self.project_file("project.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_for_coding_declarations_in_strings(self):
|
||||
# Bug #314 / jedi#465 / python#22221
|
||||
source = u'lit = """\\\n# -*- coding: utf-8 -*-\n"""'
|
||||
offset = 8
|
||||
filename = self.project_file("project.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_if_root_vanishes(self):
|
||||
# Bug #353
|
||||
source, offset = source_and_offset(
|
||||
"import foo\n"
|
||||
"foo._|_"
|
||||
)
|
||||
filename = self.project_file("project.py", source)
|
||||
shutil.rmtree(self.project_root)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
# For some reason, this breaks a lot of other tests. Couldn't
|
||||
# figure out why.
|
||||
#
|
||||
# def test_should_not_fail_for_sys_path(self):
|
||||
# # Bug #365 / jedi#486
|
||||
# source, offset = source_and_offset(
|
||||
# "import sys\n"
|
||||
# "\n"
|
||||
# "sys.path.index(_|_\n"
|
||||
# )
|
||||
# filename = self.project_file("project.py", source)
|
||||
#
|
||||
# self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_for_key_error(self):
|
||||
# Bug #561, #564, #570, #588, #593, #599 / jedi#572, jedi#579,
|
||||
# jedi#590
|
||||
source, offset = source_and_offset(
|
||||
"map(lambda_|_"
|
||||
)
|
||||
filename = self.project_file("project.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_for_badly_defined_global_variable(self):
|
||||
# Bug #519 / jedi#610
|
||||
source, offset = source_and_offset(
|
||||
"""\
|
||||
def funct1():
|
||||
global global_dict_var
|
||||
global_dict_var = dict()
|
||||
|
||||
def funct2():
|
||||
global global_dict_var
|
||||
q = global_dict_var.copy_|_()
|
||||
print(q)""")
|
||||
filename = self.project_file("project.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
def test_should_not_fail_with_mergednamesdict(self):
|
||||
# Bug #563 / jedi#589
|
||||
source, offset = source_and_offset(
|
||||
u'from email import message_|_'
|
||||
)
|
||||
filename = self.project_file("project.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
|
||||
class RPCGetCompletionsTests(GenericRPCTests):
|
||||
METHOD = "rpc_get_completions"
|
||||
|
||||
def test_should_complete_builtin(self):
|
||||
source, offset = source_and_offset("o_|_")
|
||||
|
||||
expected = self.BUILTINS
|
||||
actual = [cand['name'] for cand in
|
||||
self.backend.rpc_get_completions("test.py",
|
||||
source, offset)]
|
||||
|
||||
for candidate in expected:
|
||||
self.assertIn(candidate, actual)
|
||||
|
||||
if sys.version_info >= (3, 5):
|
||||
JSON_COMPLETIONS = ["SONDecoder", "SONEncoder", "SONDecodeError"]
|
||||
else:
|
||||
JSON_COMPLETIONS = ["SONDecoder", "SONEncoder"]
|
||||
|
||||
def test_should_complete_imports(self):
|
||||
source, offset = source_and_offset("import json\n"
|
||||
"json.J_|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
completions = self.backend.rpc_get_completions(filename,
|
||||
source,
|
||||
offset)
|
||||
self.assertEqual(
|
||||
sorted([cand['suffix'] for cand in completions]),
|
||||
sorted(self.JSON_COMPLETIONS))
|
||||
|
||||
def test_should_complete_top_level_modules_for_import(self):
|
||||
source, offset = source_and_offset("import multi_|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
completions = self.backend.rpc_get_completions(filename,
|
||||
source,
|
||||
offset)
|
||||
if compat.PYTHON3:
|
||||
expected = ["processing"]
|
||||
else:
|
||||
expected = ["file", "processing"]
|
||||
self.assertEqual(sorted([cand['suffix'] for cand in completions]),
|
||||
sorted(expected))
|
||||
|
||||
def test_should_complete_packages_for_import(self):
|
||||
source, offset = source_and_offset("import email.mi_|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
completions = self.backend.rpc_get_completions(filename,
|
||||
source,
|
||||
offset)
|
||||
self.assertEqual([cand['suffix'] for cand in completions],
|
||||
["me"])
|
||||
|
||||
def test_should_not_complete_for_import(self):
|
||||
source, offset = source_and_offset("import foo.Conf_|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
completions = self.backend.rpc_get_completions(filename,
|
||||
source,
|
||||
offset)
|
||||
self.assertEqual([cand['suffix'] for cand in completions],
|
||||
[])
|
||||
|
||||
@unittest.skipIf((3, 3) <= sys.version_info < (3, 4),
|
||||
"Bug in jedi for Python 3.3")
|
||||
def test_should_not_fail_for_short_module(self):
|
||||
source, offset = source_and_offset("from .. import foo_|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
completions = self.backend.rpc_get_completions(filename,
|
||||
source,
|
||||
offset)
|
||||
self.assertIsNotNone(completions)
|
||||
|
||||
def test_should_complete_sys(self):
|
||||
source, offset = source_and_offset("import sys\nsys._|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
completions = self.backend.rpc_get_completions(filename,
|
||||
source,
|
||||
offset)
|
||||
self.assertIn('path', [cand['suffix'] for cand in completions])
|
||||
|
||||
def test_should_find_with_trailing_text(self):
|
||||
source, offset = source_and_offset(
|
||||
"import threading\nthreading.T_|_mumble mumble")
|
||||
|
||||
expected = ["Thread", "ThreadError", "Timer"]
|
||||
actual = [cand['name'] for cand in
|
||||
self.backend.rpc_get_completions("test.py", source, offset)]
|
||||
|
||||
for candidate in expected:
|
||||
self.assertIn(candidate, actual)
|
||||
|
||||
def test_should_find_completion_different_package(self):
|
||||
# See issue #74
|
||||
self.project_file("project/__init__.py", "")
|
||||
source1 = ("class Add:\n"
|
||||
" def add(self, a, b):\n"
|
||||
" return a + b\n")
|
||||
self.project_file("project/add.py", source1)
|
||||
source2, offset = source_and_offset(
|
||||
"from project.add import Add\n"
|
||||
"class Calculator:\n"
|
||||
" def add(self, a, b):\n"
|
||||
" c = Add()\n"
|
||||
" c.ad_|_\n")
|
||||
file2 = self.project_file("project/calculator.py", source2)
|
||||
proposals = self.backend.rpc_get_completions(file2,
|
||||
source2,
|
||||
offset)
|
||||
self.assertEqual(["add"],
|
||||
[proposal["name"] for proposal in proposals])
|
||||
|
||||
|
||||
class RPCGetCompletionDocstringTests(object):
|
||||
def test_should_return_docstring(self):
|
||||
source, offset = source_and_offset("import json\n"
|
||||
"json.JSONEnc_|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
completions = self.backend.rpc_get_completions(filename,
|
||||
source,
|
||||
offset)
|
||||
completions.sort(key=lambda p: p["name"])
|
||||
prop = completions[0]
|
||||
self.assertEqual(prop["name"], "JSONEncoder")
|
||||
|
||||
docs = self.backend.rpc_get_completion_docstring("JSONEncoder")
|
||||
|
||||
self.assertIn("Extensible JSON", docs)
|
||||
|
||||
def test_should_return_none_if_unknown(self):
|
||||
docs = self.backend.rpc_get_completion_docstring("Foo")
|
||||
|
||||
self.assertIsNone(docs)
|
||||
|
||||
|
||||
class RPCGetCompletionLocationTests(object):
|
||||
def test_should_return_location(self):
|
||||
source, offset = source_and_offset("donaudampfschiff = 1\n"
|
||||
"donau_|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
completions = self.backend.rpc_get_completions(filename,
|
||||
source,
|
||||
offset)
|
||||
prop = completions[0]
|
||||
self.assertEqual(prop["name"], "donaudampfschiff")
|
||||
|
||||
loc = self.backend.rpc_get_completion_location("donaudampfschiff")
|
||||
|
||||
self.assertEqual((filename, 1), loc)
|
||||
|
||||
def test_should_return_none_if_unknown(self):
|
||||
docs = self.backend.rpc_get_completion_location("Foo")
|
||||
|
||||
self.assertIsNone(docs)
|
||||
|
||||
|
||||
class RPCGetDefinitionTests(GenericRPCTests):
|
||||
METHOD = "rpc_get_definition"
|
||||
|
||||
def test_should_return_definition_location_same_file(self):
|
||||
source, offset = source_and_offset("import threading\n"
|
||||
"def test_function(a, b):\n"
|
||||
" return a + b\n"
|
||||
"\n"
|
||||
"test_func_|_tion(\n")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
location = self.backend.rpc_get_definition(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertEqual(location[0], filename)
|
||||
# On def or on the function name
|
||||
self.assertIn(location[1], (17, 21))
|
||||
|
||||
def test_should_return_location_in_same_file_if_not_saved(self):
|
||||
source, offset = source_and_offset(
|
||||
"import threading\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"def other_function():\n"
|
||||
" test_f_|_unction(1, 2)\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"def test_function(a, b):\n"
|
||||
" return a + b\n")
|
||||
filename = self.project_file("test.py", "")
|
||||
|
||||
location = self.backend.rpc_get_definition(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertEqual(location[0], filename)
|
||||
# def or function name
|
||||
self.assertIn(location[1], (67, 71))
|
||||
|
||||
def test_should_return_location_in_different_file(self):
|
||||
source1 = ("def test_function(a, b):\n"
|
||||
" return a + b\n")
|
||||
file1 = self.project_file("test1.py", source1)
|
||||
source2, offset = source_and_offset("from test1 import test_function\n"
|
||||
"test_funct_|_ion(1, 2)\n")
|
||||
file2 = self.project_file("test2.py", source2)
|
||||
|
||||
definition = self.backend.rpc_get_definition(file2,
|
||||
source2,
|
||||
offset)
|
||||
|
||||
self.assertEqual(definition[0], file1)
|
||||
# Either on the def or on the function name
|
||||
self.assertIn(definition[1], (0, 4))
|
||||
|
||||
def test_should_return_none_if_location_not_found(self):
|
||||
source, offset = source_and_offset("test_f_|_unction()\n")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
definition = self.backend.rpc_get_definition(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertIsNone(definition)
|
||||
|
||||
def test_should_return_none_if_outside_of_symbol(self):
|
||||
source, offset = source_and_offset("test_function(_|_)\n")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
definition = self.backend.rpc_get_definition(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertIsNone(definition)
|
||||
|
||||
def test_should_return_definition_location_different_package(self):
|
||||
# See issue #74
|
||||
self.project_file("project/__init__.py", "")
|
||||
source1 = ("class Add:\n"
|
||||
" def add(self, a, b):\n"
|
||||
" return a + b\n")
|
||||
file1 = self.project_file("project/add.py", source1)
|
||||
source2, offset = source_and_offset(
|
||||
"from project.add import Add\n"
|
||||
"class Calculator:\n"
|
||||
" def add(self, a, b):\n"
|
||||
" return Add_|_().add(a, b)\n")
|
||||
file2 = self.project_file("project/calculator.py", source2)
|
||||
|
||||
location = self.backend.rpc_get_definition(file2,
|
||||
source2,
|
||||
offset)
|
||||
|
||||
self.assertEqual(location[0], file1)
|
||||
# class or class name
|
||||
self.assertIn(location[1], (0, 6))
|
||||
|
||||
def test_should_find_variable_definition(self):
|
||||
source, offset = source_and_offset("SOME_VALUE = 1\n"
|
||||
"\n"
|
||||
"variable = _|_SOME_VALUE\n")
|
||||
filename = self.project_file("test.py", source)
|
||||
self.assertEqual(self.backend.rpc_get_definition(filename,
|
||||
source,
|
||||
offset),
|
||||
(filename, 0))
|
||||
|
||||
|
||||
class RPCGetAssignmentTests(GenericRPCTests):
|
||||
METHOD = "rpc_get_assignment"
|
||||
|
||||
def test_should_return_assignment_location_same_file(self):
|
||||
source, offset = source_and_offset("import threading\n"
|
||||
"class TestClass(object):\n"
|
||||
" def __init__(self, a, b):\n"
|
||||
" self.a = a\n"
|
||||
" self.b = b\n"
|
||||
"\n"
|
||||
"testclass = TestClass(2, 4)"
|
||||
"\n"
|
||||
"testcl_|_ass(\n")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
location = self.backend.rpc_get_assignment(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertEqual(location[0], filename)
|
||||
# On def or on the function name
|
||||
self.assertEqual(location[1], 111)
|
||||
|
||||
def test_should_return_location_in_same_file_if_not_saved(self):
|
||||
source, offset = source_and_offset("import threading\n"
|
||||
"class TestClass(object):\n"
|
||||
" def __init__(self, a, b):\n"
|
||||
" self.a = a\n"
|
||||
" self.b = b\n"
|
||||
"\n"
|
||||
"testclass = TestClass(2, 4)"
|
||||
"\n"
|
||||
"testcl_|_ass(\n")
|
||||
filename = self.project_file("test.py", "")
|
||||
|
||||
location = self.backend.rpc_get_assignment(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertEqual(location[0], filename)
|
||||
# def or function name
|
||||
self.assertEqual(location[1], 111)
|
||||
|
||||
def test_should_return_location_in_different_file(self):
|
||||
source1 = ("class TestClass(object):\n"
|
||||
" def __init__(self, a, b):\n"
|
||||
" self.a = a\n"
|
||||
" self.b = b\n"
|
||||
"testclass = TestClass(3, 5)\n")
|
||||
file1 = self.project_file("test1.py", source1)
|
||||
source2, offset = source_and_offset("from test1 import testclass\n"
|
||||
"testcl_|_ass.a\n")
|
||||
file2 = self.project_file("test2.py", source2)
|
||||
# First jump goes to import statement
|
||||
assignment = self.backend.rpc_get_assignment(file2,
|
||||
source2,
|
||||
offset)
|
||||
# Second jump goes to test1 file
|
||||
self.assertEqual(assignment[0], file2)
|
||||
assignment = self.backend.rpc_get_assignment(file2,
|
||||
source2,
|
||||
assignment[1])
|
||||
|
||||
self.assertEqual(assignment[0], file1)
|
||||
self.assertEqual(assignment[1], 93)
|
||||
|
||||
def test_should_return_none_if_location_not_found(self):
|
||||
source, offset = source_and_offset("test_f_|_unction()\n")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
assignment = self.backend.rpc_get_assignment(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertIsNone(assignment)
|
||||
|
||||
def test_should_return_none_if_outside_of_symbol(self):
|
||||
source, offset = source_and_offset("testcl(_|_)ass\n")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
assignment = self.backend.rpc_get_assignment(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertIsNone(assignment)
|
||||
|
||||
def test_should_find_variable_assignment(self):
|
||||
source, offset = source_and_offset("SOME_VALUE = 1\n"
|
||||
"\n"
|
||||
"variable = _|_SOME_VALUE\n")
|
||||
filename = self.project_file("test.py", source)
|
||||
self.assertEqual(self.backend.rpc_get_assignment(filename,
|
||||
source,
|
||||
offset),
|
||||
(filename, 0))
|
||||
|
||||
|
||||
class RPCGetCalltipTests(GenericRPCTests):
|
||||
METHOD = "rpc_get_calltip"
|
||||
|
||||
@unittest.skipIf(sys.version_info >= (3, 0),
|
||||
"Bug in Jedi 0.9.0")
|
||||
def test_should_get_calltip(self):
|
||||
source, offset = source_and_offset(
|
||||
"import threading\nthreading.Thread(_|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
calltip = self.backend.rpc_get_calltip(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
expected = self.THREAD_CALLTIP
|
||||
|
||||
self.assertEqual(calltip, expected)
|
||||
|
||||
@unittest.skipIf(sys.version_info >= (3, 0),
|
||||
"Bug in Jedi 0.9.0")
|
||||
def test_should_get_calltip_even_after_parens(self):
|
||||
source, offset = source_and_offset(
|
||||
"import threading\nthreading.Thread(foo()_|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
actual = self.backend.rpc_get_calltip(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertEqual(self.THREAD_CALLTIP, actual)
|
||||
|
||||
@unittest.skipIf(sys.version_info >= (3, 0),
|
||||
"Bug in Jedi 0.9.0")
|
||||
def test_should_get_calltip_at_closing_paren(self):
|
||||
source, offset = source_and_offset(
|
||||
"import threading\nthreading.Thread(_|_)")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
actual = self.backend.rpc_get_calltip(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertEqual(self.THREAD_CALLTIP, actual)
|
||||
|
||||
def test_should_not_missing_attribute_get_definition(self):
|
||||
# Bug #627 / jedi#573
|
||||
source, offset = source_and_offset(
|
||||
"import threading\nthreading.Thread(_|_)")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
self.backend.rpc_get_calltip(filename, source, offset)
|
||||
|
||||
def test_should_return_none_for_bad_identifier(self):
|
||||
source, offset = source_and_offset(
|
||||
"froblgoo(_|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
calltip = self.backend.rpc_get_calltip(filename,
|
||||
source,
|
||||
offset)
|
||||
self.assertIsNone(calltip)
|
||||
|
||||
def test_should_remove_self_argument(self):
|
||||
source, offset = source_and_offset(
|
||||
"d = dict()\n"
|
||||
"d.keys(_|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
actual = self.backend.rpc_get_calltip(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertEqual(self.KEYS_CALLTIP, actual)
|
||||
|
||||
def test_should_remove_package_prefix(self):
|
||||
source, offset = source_and_offset(
|
||||
"import decimal\n"
|
||||
"d = decimal.Decimal('1.5')\n"
|
||||
"d.radix(_|_")
|
||||
filename = self.project_file("test.py", source)
|
||||
|
||||
actual = self.backend.rpc_get_calltip(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertEqual(self.RADIX_CALLTIP, actual)
|
||||
|
||||
def test_should_return_none_outside_of_all(self):
|
||||
filename = self.project_file("test.py", "")
|
||||
source, offset = source_and_offset("import thr_|_eading\n")
|
||||
calltip = self.backend.rpc_get_calltip(filename,
|
||||
source, offset)
|
||||
self.assertIsNone(calltip)
|
||||
|
||||
def test_should_find_calltip_different_package(self):
|
||||
# See issue #74
|
||||
self.project_file("project/__init__.py", "")
|
||||
source1 = ("class Add:\n"
|
||||
" def add(self, a, b):\n"
|
||||
" return a + b\n")
|
||||
self.project_file("project/add.py", source1)
|
||||
source2, offset = source_and_offset(
|
||||
"from project.add import Add\n"
|
||||
"class Calculator:\n"
|
||||
" def add(self, a, b):\n"
|
||||
" c = Add()\n"
|
||||
" c.add(_|_\n")
|
||||
file2 = self.project_file("project/calculator.py", source2)
|
||||
|
||||
actual = self.backend.rpc_get_calltip(file2,
|
||||
source2,
|
||||
offset)
|
||||
|
||||
self.assertEqual(self.ADD_CALLTIP, actual)
|
||||
|
||||
|
||||
class RPCGetDocstringTests(GenericRPCTests):
|
||||
METHOD = "rpc_get_docstring"
|
||||
|
||||
def check_docstring(self, docstring):
|
||||
|
||||
def first_line(s):
|
||||
return s[:s.index("\n")]
|
||||
|
||||
self.assertEqual(first_line(docstring),
|
||||
self.JSON_LOADS_DOCSTRING)
|
||||
|
||||
def test_should_get_docstring(self):
|
||||
source, offset = source_and_offset(
|
||||
"import json\njson.loads_|_(")
|
||||
filename = self.project_file("test.py", source)
|
||||
docstring = self.backend.rpc_get_docstring(filename,
|
||||
source,
|
||||
offset)
|
||||
self.check_docstring(docstring)
|
||||
|
||||
def test_should_return_none_for_bad_identifier(self):
|
||||
source, offset = source_and_offset(
|
||||
"froblgoo_|_(\n")
|
||||
filename = self.project_file("test.py", source)
|
||||
docstring = self.backend.rpc_get_docstring(filename,
|
||||
source,
|
||||
offset)
|
||||
self.assertIsNone(docstring)
|
||||
|
||||
|
||||
class RPCGetNamesTests(GenericRPCTests):
|
||||
METHOD = "rpc_get_names"
|
||||
|
||||
def test_shouldreturn_names_in_same_file(self):
|
||||
filename = self.project_file("test.py", "")
|
||||
source, offset = source_and_offset(
|
||||
"def foo(x, y):\n"
|
||||
" return x + y\n"
|
||||
"c = _|_foo(5, 2)\n")
|
||||
|
||||
names = self.backend.rpc_get_names(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertEqual(names,
|
||||
[{'name': 'foo',
|
||||
'filename': filename,
|
||||
'offset': 4},
|
||||
{'name': 'x',
|
||||
'filename': filename,
|
||||
'offset': 8},
|
||||
{'name': 'y',
|
||||
'filename': filename,
|
||||
'offset': 11},
|
||||
{'name': 'x',
|
||||
'filename': filename,
|
||||
'offset': 26},
|
||||
{'name': 'y',
|
||||
'filename': filename,
|
||||
'offset': 30},
|
||||
{'name': 'c',
|
||||
'filename': filename,
|
||||
'offset': 32},
|
||||
{'name': 'foo',
|
||||
'filename': filename,
|
||||
'offset': 36}])
|
||||
|
||||
def test_should_not_fail_without_symbol(self):
|
||||
filename = self.project_file("test.py", "")
|
||||
|
||||
names = self.backend.rpc_get_names(filename,
|
||||
"",
|
||||
0)
|
||||
|
||||
self.assertEqual(names, [])
|
||||
|
||||
|
||||
class RPCGetUsagesTests(GenericRPCTests):
|
||||
METHOD = "rpc_get_usages"
|
||||
|
||||
def test_should_return_uses_in_same_file(self):
|
||||
filename = self.project_file("test.py", "")
|
||||
source, offset = source_and_offset(
|
||||
"def foo(x):\n"
|
||||
" return _|_x + x\n")
|
||||
|
||||
usages = self.backend.rpc_get_usages(filename,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertEqual(usages,
|
||||
[{'name': 'x',
|
||||
'offset': 8,
|
||||
'filename': filename},
|
||||
{'name': 'x',
|
||||
'filename': filename,
|
||||
'offset': 23},
|
||||
{'name': u'x',
|
||||
'filename': filename,
|
||||
'offset': 27}])
|
||||
|
||||
def test_should_return_uses_in_other_file(self):
|
||||
file1 = self.project_file("file1.py", "")
|
||||
file2 = self.project_file("file2.py", "\n\n\n\n\nx = 5")
|
||||
source, offset = source_and_offset(
|
||||
"import file2\n"
|
||||
"file2._|_x\n")
|
||||
|
||||
usages = self.backend.rpc_get_usages(file1,
|
||||
source,
|
||||
offset)
|
||||
|
||||
self.assertEqual(usages,
|
||||
[{'name': 'x',
|
||||
'filename': file1,
|
||||
'offset': 19},
|
||||
{'name': 'x',
|
||||
'filename': file2,
|
||||
'offset': 5}])
|
||||
|
||||
def test_should_not_fail_without_symbol(self):
|
||||
filename = self.project_file("file.py", "")
|
||||
|
||||
usages = self.backend.rpc_get_usages(filename,
|
||||
"",
|
||||
0)
|
||||
|
||||
self.assertEqual(usages, [])
|
||||
|
||||
|
||||
def source_and_offset(source):
|
||||
"""Return a source and offset from a source description.
|
||||
|
||||
>>> source_and_offset("hello, _|_world")
|
||||
("hello, world", 7)
|
||||
>>> source_and_offset("_|_hello, world")
|
||||
("hello, world", 0)
|
||||
>>> source_and_offset("hello, world_|_")
|
||||
("hello, world", 12)
|
||||
"""
|
||||
offset = source.index("_|_")
|
||||
return source[:offset] + source[offset + 3:], offset
|
|
@ -0,0 +1,20 @@
|
|||
# coding: utf-8
|
||||
|
||||
"""Tests for the elpy.autopep8 module"""
|
||||
|
||||
import unittest
|
||||
|
||||
from elpy import auto_pep8
|
||||
from elpy.tests.support import BackendTestCase
|
||||
|
||||
|
||||
class Autopep8TestCase(BackendTestCase):
|
||||
|
||||
def setUp(self):
|
||||
if not auto_pep8.autopep8:
|
||||
raise unittest.SkipTest
|
||||
|
||||
def test_fix_code(self):
|
||||
code_block = 'x= 123\n'
|
||||
new_block = auto_pep8.fix_code(code_block)
|
||||
self.assertEqual(new_block, 'x = 123\n')
|
|
@ -0,0 +1,73 @@
|
|||
# coding: utf-8
|
||||
|
||||
"""Tests for the elpy.impmagic module"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from elpy import impmagic
|
||||
from elpy.tests.support import BackendTestCase
|
||||
|
||||
TEST_SOURCE = '''# test file
|
||||
|
||||
import time
|
||||
import logging
|
||||
|
||||
os.getcwd()
|
||||
time.sleep(1)
|
||||
'''
|
||||
|
||||
|
||||
@unittest.skipIf(sys.version_info >= (3, 5), "importmagic fails in 3.5")
|
||||
class ImportMagicTestCase(BackendTestCase):
|
||||
def setUp(self):
|
||||
if not impmagic.importmagic:
|
||||
raise unittest.SkipTest
|
||||
self.importmagic = impmagic.ImportMagic()
|
||||
super(ImportMagicTestCase, self).setUp()
|
||||
|
||||
def build_index(self):
|
||||
self.project_file('mymod.py', 'class AnUncommonName:\n pass\n')
|
||||
self.importmagic.build_index(self.project_root,
|
||||
custom_path=[self.project_root],
|
||||
blacklist_re=re.compile('^$'))
|
||||
self.importmagic._thread.join()
|
||||
|
||||
def test_get_symbols(self):
|
||||
self.build_index()
|
||||
candidates = self.importmagic.get_import_symbols('AnUncommonName')
|
||||
self.assertEqual(candidates, ['from mymod import AnUncommonName'])
|
||||
candidates = self.importmagic.get_import_symbols('mymod')
|
||||
self.assertEqual(candidates, ['import mymod'])
|
||||
|
||||
def test_add_import(self):
|
||||
self.build_index()
|
||||
start, end, newblock = self.importmagic.add_import(
|
||||
TEST_SOURCE, 'from mymod import AnUncommonName')
|
||||
self.assertEqual(start, 2)
|
||||
self.assertEqual(end, 5)
|
||||
self.assertEqual(newblock.strip(),
|
||||
'import logging\n'
|
||||
'import time\n'
|
||||
'from mymod import AnUncommonName')
|
||||
|
||||
start, end, newblock = self.importmagic.add_import(
|
||||
TEST_SOURCE, 'import mymod')
|
||||
self.assertEqual(start, 2)
|
||||
self.assertEqual(end, 5)
|
||||
self.assertEqual(newblock.strip(),
|
||||
'import logging\nimport mymod\nimport time')
|
||||
|
||||
def test_get_unresolved_symbols(self):
|
||||
self.build_index()
|
||||
symbols = self.importmagic.get_unresolved_symbols('x = a + b\ny = c.d')
|
||||
self.assertEqual(sorted(symbols), ['a', 'b', 'c.d'])
|
||||
|
||||
def test_remove_unreferenced_imports(self):
|
||||
self.build_index()
|
||||
start, end, newblock = \
|
||||
self.importmagic.remove_unreferenced_imports(TEST_SOURCE)
|
||||
self.assertEqual(start, 2)
|
||||
self.assertEqual(end, 5)
|
||||
self.assertEqual(newblock.strip(), 'import time')
|
|
@ -0,0 +1,317 @@
|
|||
"""Tests for the elpy.jedibackend module."""
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import jedi
|
||||
import mock
|
||||
|
||||
from elpy import jedibackend
|
||||
from elpy import rpc
|
||||
from elpy.tests import compat
|
||||
from elpy.tests.support import BackendTestCase
|
||||
from elpy.tests.support import RPCGetCompletionsTests
|
||||
from elpy.tests.support import RPCGetCompletionDocstringTests
|
||||
from elpy.tests.support import RPCGetCompletionLocationTests
|
||||
from elpy.tests.support import RPCGetDocstringTests
|
||||
from elpy.tests.support import RPCGetDefinitionTests
|
||||
from elpy.tests.support import RPCGetAssignmentTests
|
||||
from elpy.tests.support import RPCGetCalltipTests
|
||||
from elpy.tests.support import RPCGetUsagesTests
|
||||
from elpy.tests.support import RPCGetNamesTests
|
||||
|
||||
|
||||
class JediBackendTestCase(BackendTestCase):
|
||||
def setUp(self):
|
||||
super(JediBackendTestCase, self).setUp()
|
||||
self.backend = jedibackend.JediBackend(self.project_root)
|
||||
|
||||
|
||||
class TestInit(JediBackendTestCase):
|
||||
def test_should_have_jedi_as_name(self):
|
||||
self.assertEqual(self.backend.name, "jedi")
|
||||
|
||||
|
||||
class TestRPCGetCompletions(RPCGetCompletionsTests,
|
||||
JediBackendTestCase):
|
||||
BUILTINS = ['object', 'oct', 'open', 'ord', 'OSError', 'OverflowError']
|
||||
|
||||
|
||||
class TestRPCGetCompletionDocstring(RPCGetCompletionDocstringTests,
|
||||
JediBackendTestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestRPCGetCompletionLocation(RPCGetCompletionLocationTests,
|
||||
JediBackendTestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestRPCGetDocstring(RPCGetDocstringTests,
|
||||
JediBackendTestCase):
|
||||
JSON_LOADS_DOCSTRING = (
|
||||
'loads(s, encoding=None, cls=None, '
|
||||
'object_hook=None, parse_float=None,'
|
||||
)
|
||||
|
||||
def check_docstring(self, docstring):
|
||||
lines = docstring.splitlines()
|
||||
self.assertEqual(lines[0], 'Documentation for json.loads:')
|
||||
self.assertEqual(lines[2], self.JSON_LOADS_DOCSTRING)
|
||||
|
||||
@mock.patch("elpy.jedibackend.run_with_debug")
|
||||
def test_should_not_return_empty_docstring(self, run_with_debug):
|
||||
location = mock.MagicMock()
|
||||
location.full_name = "testthing"
|
||||
location.docstring.return_value = ""
|
||||
run_with_debug.return_value = [location]
|
||||
filename = self.project_file("test.py", "print")
|
||||
docstring = self.backend.rpc_get_docstring(filename, "print", 0)
|
||||
self.assertIsNone(docstring)
|
||||
|
||||
|
||||
class TestRPCGetDefinition(RPCGetDefinitionTests,
|
||||
JediBackendTestCase):
|
||||
@mock.patch("jedi.Script")
|
||||
def test_should_not_fail_if_module_path_is_none(self, Script):
|
||||
"""Do not fail if loc.module_path is None.
|
||||
|
||||
This can happen under some circumstances I am unsure about.
|
||||
See #537 for the issue that reported this.
|
||||
|
||||
"""
|
||||
locations = [
|
||||
mock.Mock(module_path=None)
|
||||
]
|
||||
script = Script.return_value
|
||||
script.goto_definitions.return_value = locations
|
||||
script.goto_assignments.return_value = locations
|
||||
|
||||
location = self.rpc("", "", 0)
|
||||
|
||||
self.assertIsNone(location)
|
||||
|
||||
|
||||
class TestRPCGetAssignment(RPCGetAssignmentTests,
|
||||
JediBackendTestCase):
|
||||
@mock.patch("jedi.Script")
|
||||
def test_should_not_fail_if_module_path_is_none(self, Script):
|
||||
"""Do not fail if loc.module_path is None.
|
||||
|
||||
"""
|
||||
locations = [
|
||||
mock.Mock(module_path=None)
|
||||
]
|
||||
script = Script.return_value
|
||||
script.goto_assignments.return_value = locations
|
||||
script.goto_assignments.return_value = locations
|
||||
|
||||
location = self.rpc("", "", 0)
|
||||
|
||||
self.assertIsNone(location)
|
||||
|
||||
|
||||
class TestRPCGetCalltip(RPCGetCalltipTests,
|
||||
JediBackendTestCase):
|
||||
KEYS_CALLTIP = {'index': 0,
|
||||
'params': ['param '],
|
||||
'name': u'keys'}
|
||||
RADIX_CALLTIP = {'index': None,
|
||||
'params': [],
|
||||
'name': u'radix'}
|
||||
ADD_CALLTIP = {'index': 0,
|
||||
'params': [u'param a', u'param b'],
|
||||
'name': u'add'}
|
||||
if compat.PYTHON3:
|
||||
THREAD_CALLTIP = {"name": "Thread",
|
||||
"params": ["group=None",
|
||||
"target=None",
|
||||
"name=None",
|
||||
"args=()",
|
||||
"kwargs=None",
|
||||
"daemon=None"],
|
||||
"index": 0}
|
||||
else:
|
||||
THREAD_CALLTIP = {"name": "Thread",
|
||||
"params": ["param group=None",
|
||||
"param target=None",
|
||||
"param name=None",
|
||||
"param args=()",
|
||||
"param kwargs=None",
|
||||
"param verbose=None"],
|
||||
"index": 0}
|
||||
|
||||
def test_should_not_fail_with_get_subscope_by_name(self):
|
||||
# Bug #677 / jedi#628
|
||||
source = (
|
||||
u"my_lambda = lambda x: x+1\n"
|
||||
u"my_lambda(1)"
|
||||
)
|
||||
filename = self.project_file("project.py", source)
|
||||
offset = 37
|
||||
|
||||
sigs = self.backend.rpc_get_calltip(filename, source, offset)
|
||||
sigs["index"]
|
||||
|
||||
|
||||
class TestRPCGetUsages(RPCGetUsagesTests,
|
||||
JediBackendTestCase):
|
||||
def test_should_not_fail_for_missing_module(self):
|
||||
# This causes use.module_path to be None
|
||||
source = "import sys\n\nsys.path.\n" # insert()"
|
||||
offset = 21
|
||||
filename = self.project_file("project.py", source)
|
||||
|
||||
self.rpc(filename, source, offset)
|
||||
|
||||
|
||||
class TestRPCGetNames(RPCGetNamesTests,
|
||||
JediBackendTestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestPosToLinecol(unittest.TestCase):
|
||||
def test_should_handle_beginning_of_string(self):
|
||||
self.assertEqual(jedibackend.pos_to_linecol("foo", 0),
|
||||
(1, 0))
|
||||
|
||||
def test_should_handle_end_of_line(self):
|
||||
self.assertEqual(jedibackend.pos_to_linecol("foo\nbar\nbaz\nqux", 9),
|
||||
(3, 1))
|
||||
|
||||
def test_should_handle_end_of_string(self):
|
||||
self.assertEqual(jedibackend.pos_to_linecol("foo\nbar\nbaz\nqux", 14),
|
||||
(4, 2))
|
||||
|
||||
|
||||
class TestLinecolToPos(unittest.TestCase):
|
||||
def test_should_handle_beginning_of_string(self):
|
||||
self.assertEqual(jedibackend.linecol_to_pos("foo", 1, 0),
|
||||
0)
|
||||
|
||||
def test_should_handle_end_of_string(self):
|
||||
self.assertEqual(jedibackend.linecol_to_pos("foo\nbar\nbaz\nqux",
|
||||
3, 1),
|
||||
9)
|
||||
|
||||
def test_should_return_offset(self):
|
||||
self.assertEqual(jedibackend.linecol_to_pos("foo\nbar\nbaz\nqux",
|
||||
4, 2),
|
||||
14)
|
||||
|
||||
def test_should_fail_for_line_past_text(self):
|
||||
self.assertRaises(ValueError,
|
||||
jedibackend.linecol_to_pos, "foo\n", 3, 1)
|
||||
|
||||
def test_should_fail_for_column_past_text(self):
|
||||
self.assertRaises(ValueError,
|
||||
jedibackend.linecol_to_pos, "foo\n", 1, 10)
|
||||
|
||||
|
||||
class TestRunWithDebug(unittest.TestCase):
|
||||
@mock.patch('jedi.Script')
|
||||
def test_should_call_method(self, Script):
|
||||
Script.return_value.test_method.return_value = "test-result"
|
||||
|
||||
result = jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3)
|
||||
|
||||
Script.assert_called_with(1, 2, arg=3)
|
||||
self.assertEqual(result, 'test-result')
|
||||
|
||||
@mock.patch('jedi.Script')
|
||||
def test_should_re_raise(self, Script):
|
||||
Script.side_effect = RuntimeError
|
||||
|
||||
with self.assertRaises(RuntimeError):
|
||||
jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3,
|
||||
re_raise=(RuntimeError,))
|
||||
|
||||
@mock.patch('jedi.Script')
|
||||
@mock.patch('jedi.set_debug_function')
|
||||
def test_should_keep_debug_info(self, set_debug_function, Script):
|
||||
Script.side_effect = RuntimeError
|
||||
|
||||
try:
|
||||
jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3)
|
||||
except rpc.Fault as e:
|
||||
self.assertGreaterEqual(e.code, 400)
|
||||
self.assertIsNotNone(e.data)
|
||||
self.assertIn("traceback", e.data)
|
||||
jedi_debug_info = e.data["jedi_debug_info"]
|
||||
self.assertIsNotNone(jedi_debug_info)
|
||||
self.assertEqual(jedi_debug_info["script_args"],
|
||||
"1, 2, arg=3")
|
||||
self.assertEqual(jedi_debug_info["source"], None)
|
||||
self.assertEqual(jedi_debug_info["method"], "test_method")
|
||||
self.assertEqual(jedi_debug_info["debug_info"], [])
|
||||
else:
|
||||
self.fail("Fault not thrown")
|
||||
|
||||
@mock.patch('jedi.Script')
|
||||
@mock.patch('jedi.set_debug_function')
|
||||
def test_should_keep_error_text(self, set_debug_function, Script):
|
||||
Script.side_effect = RuntimeError
|
||||
|
||||
try:
|
||||
jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3)
|
||||
except rpc.Fault as e:
|
||||
self.assertEqual(str(e), str(RuntimeError()))
|
||||
self.assertEqual(e.message, str(RuntimeError()))
|
||||
else:
|
||||
self.fail("Fault not thrown")
|
||||
|
||||
@mock.patch('jedi.Script')
|
||||
@mock.patch('jedi.set_debug_function')
|
||||
def test_should_handle_source_special(self, set_debug_function, Script):
|
||||
Script.side_effect = RuntimeError
|
||||
|
||||
try:
|
||||
jedibackend.run_with_debug(jedi, 'test_method', source="foo")
|
||||
except rpc.Fault as e:
|
||||
self.assertEqual(e.data["jedi_debug_info"]["script_args"],
|
||||
"source=source")
|
||||
self.assertEqual(e.data["jedi_debug_info"]["source"], "foo")
|
||||
else:
|
||||
self.fail("Fault not thrown")
|
||||
|
||||
@mock.patch('jedi.Script')
|
||||
@mock.patch('jedi.set_debug_function')
|
||||
def test_should_set_debug_info(self, set_debug_function, Script):
|
||||
the_debug_function = [None]
|
||||
|
||||
def my_set_debug_function(debug_function, **kwargs):
|
||||
the_debug_function[0] = debug_function
|
||||
|
||||
def my_script(*args, **kwargs):
|
||||
the_debug_function[0](jedi.debug.NOTICE, "Notice")
|
||||
the_debug_function[0](jedi.debug.WARNING, "Warning")
|
||||
the_debug_function[0]("other", "Other")
|
||||
raise RuntimeError
|
||||
|
||||
set_debug_function.side_effect = my_set_debug_function
|
||||
Script.return_value.test_method = my_script
|
||||
|
||||
try:
|
||||
jedibackend.run_with_debug(jedi, 'test_method', source="foo")
|
||||
except rpc.Fault as e:
|
||||
self.assertEqual(e.data["jedi_debug_info"]["debug_info"],
|
||||
["[N] Notice",
|
||||
"[W] Warning",
|
||||
"[?] Other"])
|
||||
else:
|
||||
self.fail("Fault not thrown")
|
||||
|
||||
@mock.patch('jedi.set_debug_function')
|
||||
@mock.patch('jedi.Script')
|
||||
def test_should_not_fail_with_bad_data(self, Script, set_debug_function):
|
||||
import jedi.debug
|
||||
|
||||
def set_debug(function, speed=True):
|
||||
if function is not None:
|
||||
function(jedi.debug.NOTICE, u"\xab")
|
||||
|
||||
set_debug_function.side_effect = set_debug
|
||||
Script.return_value.test_method.side_effect = Exception
|
||||
|
||||
with self.assertRaises(rpc.Fault):
|
||||
jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3)
|
|
@ -0,0 +1,88 @@
|
|||
import os
|
||||
import unittest
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import mock
|
||||
|
||||
import elpy.pydocutils
|
||||
|
||||
|
||||
class TestGetPydocCompletions(unittest.TestCase):
|
||||
def test_should_return_top_level_modules(self):
|
||||
modules = elpy.pydocutils.get_pydoc_completions("")
|
||||
self.assertIn('sys', modules)
|
||||
self.assertIn('json', modules)
|
||||
|
||||
def test_should_return_submodules(self):
|
||||
modules = elpy.pydocutils.get_pydoc_completions("elpy")
|
||||
self.assertIn("elpy.rpc", modules)
|
||||
self.assertIn("elpy.server", modules)
|
||||
modules = elpy.pydocutils.get_pydoc_completions("os")
|
||||
self.assertIn("os.path", modules)
|
||||
|
||||
def test_should_find_objects_in_module(self):
|
||||
self.assertIn("elpy.tests.test_pydocutils.TestGetPydocCompletions",
|
||||
elpy.pydocutils.get_pydoc_completions
|
||||
("elpy.tests.test_pydocutils"))
|
||||
|
||||
def test_should_find_attributes_of_objects(self):
|
||||
attribs = elpy.pydocutils.get_pydoc_completions(
|
||||
"elpy.tests.test_pydocutils.TestGetPydocCompletions")
|
||||
self.assertIn("elpy.tests.test_pydocutils.TestGetPydocCompletions."
|
||||
"test_should_find_attributes_of_objects",
|
||||
attribs)
|
||||
|
||||
def test_should_return_none_for_inexisting_module(self):
|
||||
self.assertEqual([],
|
||||
elpy.pydocutils.get_pydoc_completions
|
||||
("does_not_exist"))
|
||||
|
||||
def test_should_work_for_unicode_strings(self):
|
||||
self.assertIsNotNone(elpy.pydocutils.get_pydoc_completions
|
||||
(u"sys"))
|
||||
|
||||
def test_should_find_partial_completions(self):
|
||||
self.assertIn("multiprocessing",
|
||||
elpy.pydocutils.get_pydoc_completions
|
||||
("multiprocess"))
|
||||
self.assertIn("multiprocessing.util",
|
||||
elpy.pydocutils.get_pydoc_completions
|
||||
("multiprocessing.ut"))
|
||||
|
||||
def test_should_ignore_trailing_dot(self):
|
||||
self.assertIn("elpy.pydocutils",
|
||||
elpy.pydocutils.get_pydoc_completions
|
||||
("elpy."))
|
||||
|
||||
|
||||
class TestGetModules(unittest.TestCase):
|
||||
def test_should_return_top_level_modules(self):
|
||||
modules = elpy.pydocutils.get_modules()
|
||||
self.assertIn('sys', modules)
|
||||
self.assertIn('json', modules)
|
||||
|
||||
def test_should_return_submodules(self):
|
||||
modules = elpy.pydocutils.get_modules("elpy")
|
||||
self.assertIn("rpc", modules)
|
||||
self.assertIn("server", modules)
|
||||
|
||||
@mock.patch.object(elpy.pydocutils, 'safeimport')
|
||||
def test_should_catch_import_errors(self, safeimport):
|
||||
def raise_function(message):
|
||||
raise elpy.pydocutils.ErrorDuringImport(message,
|
||||
(None, None, None))
|
||||
safeimport.side_effect = raise_function
|
||||
self.assertEqual([], elpy.pydocutils.get_modules("foo.bar"))
|
||||
|
||||
def test_should_not_fail_for_permission_denied(self):
|
||||
tmpdir = tempfile.mkdtemp(prefix="test-elpy-get-modules-")
|
||||
sys.path.append(tmpdir)
|
||||
os.chmod(tmpdir, 0o000)
|
||||
try:
|
||||
elpy.pydocutils.get_modules()
|
||||
finally:
|
||||
os.chmod(tmpdir, 0o755)
|
||||
shutil.rmtree(tmpdir)
|
||||
sys.path.remove(tmpdir)
|
|
@ -0,0 +1,545 @@
|
|||
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)
|
||||
""")
|
|
@ -0,0 +1,205 @@
|
|||
"""Tests for elpy.ropebackend."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import mock
|
||||
|
||||
from elpy import ropebackend
|
||||
from elpy import rpc
|
||||
from elpy.tests import compat
|
||||
from elpy.tests.support import BackendTestCase
|
||||
from elpy.tests.support import RPCGetCompletionsTests
|
||||
from elpy.tests.support import RPCGetCompletionDocstringTests
|
||||
from elpy.tests.support import RPCGetCompletionLocationTests
|
||||
from elpy.tests.support import RPCGetDefinitionTests
|
||||
from elpy.tests.support import RPCGetCalltipTests
|
||||
from elpy.tests.support import RPCGetDocstringTests
|
||||
|
||||
|
||||
class RopeBackendTestCase(BackendTestCase):
|
||||
def setUp(self):
|
||||
super(RopeBackendTestCase, self).setUp()
|
||||
self.backend = ropebackend.RopeBackend(self.project_root)
|
||||
|
||||
|
||||
class ShouldCallValidateTest(object):
|
||||
def test_should_call_validate(self):
|
||||
with mock.patch.object(self.backend, 'validate') as validate:
|
||||
self.rpc(None, "", 0)
|
||||
|
||||
self.assertTrue(validate.called)
|
||||
|
||||
|
||||
class TestInit(RopeBackendTestCase):
|
||||
def test_should_have_rope_as_name(self):
|
||||
self.assertEqual(self.backend.name, "rope")
|
||||
|
||||
def test_should_patch_project_files(self):
|
||||
self.project_file("foo.txt", "")
|
||||
self.project_file("baddir/file.py", "")
|
||||
|
||||
self.backend.project.validate()
|
||||
|
||||
actual = [f.real_path for f in
|
||||
self.backend.project.file_list.get_files()]
|
||||
self.assertEqual([os.path.join(self.project_root, "foo.txt")],
|
||||
actual)
|
||||
|
||||
def test_should_fail_for_inexisting_project_root(self):
|
||||
with self.assertRaises(rpc.Fault):
|
||||
ropebackend.RopeBackend("/does/not/exist/")
|
||||
|
||||
|
||||
class TestValidate(RopeBackendTestCase):
|
||||
def test_should_call_validate_after_timeout(self):
|
||||
with mock.patch("time.time") as t:
|
||||
t.return_value = 10
|
||||
self.backend.validate()
|
||||
with mock.patch.object(self.backend, 'project') as project:
|
||||
t.return_value = 10 + ropebackend.VALIDATE_EVERY_SECONDS + 1
|
||||
self.backend.validate()
|
||||
|
||||
self.assertTrue(project.validate.called)
|
||||
|
||||
def test_should_not_call_validate_before_timeout(self):
|
||||
with mock.patch("time.time") as t:
|
||||
t.return_value = 10
|
||||
self.backend.validate()
|
||||
with mock.patch.object(self.backend, 'project') as project:
|
||||
t.return_value = 10 + ropebackend.VALIDATE_EVERY_SECONDS - 1
|
||||
self.backend.validate()
|
||||
|
||||
self.assertFalse(project.validate.called)
|
||||
|
||||
def test_should_not_fail_if_root_vanishes(self):
|
||||
# Bug #353
|
||||
tmpdir = tempfile.mkdtemp(prefix="elpy-test-validate-")
|
||||
try:
|
||||
backend = ropebackend.RopeBackend(tmpdir)
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
backend.validate()
|
||||
|
||||
|
||||
class TestRPCGetCompletions(RPCGetCompletionsTests,
|
||||
RopeBackendTestCase):
|
||||
BUILTINS = ["object", "oct", "open", "or", "ord"]
|
||||
|
||||
|
||||
class TestRPCGetCompletionDocstring(RPCGetCompletionDocstringTests,
|
||||
RopeBackendTestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestRPCGetCompletionLocation(RPCGetCompletionLocationTests,
|
||||
RopeBackendTestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestRPCGetDefinition(RPCGetDefinitionTests,
|
||||
ShouldCallValidateTest,
|
||||
RopeBackendTestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestRPCGetCalltip(RPCGetCalltipTests,
|
||||
ShouldCallValidateTest,
|
||||
RopeBackendTestCase):
|
||||
ADD_CALLTIP = 'Add.add(a, b)'
|
||||
RADIX_CALLTIP = "Decimal.radix()"
|
||||
if compat.PYTHON3:
|
||||
THREAD_CALLTIP = (
|
||||
"threading.Thread(group=None, target=None, "
|
||||
"name=None, args=(), kwargs=None, daemon=None, *)"
|
||||
)
|
||||
KEYS_CALLTIP = "builtins.keys()"
|
||||
else:
|
||||
THREAD_CALLTIP = (
|
||||
"threading.Thread(group=None, target=None, "
|
||||
"name=None, args=(), kwargs=None, verbose=None)"
|
||||
)
|
||||
KEYS_CALLTIP = "__builtin__.keys()"
|
||||
|
||||
|
||||
class TestRPCGetDocstring(RPCGetDocstringTests,
|
||||
ShouldCallValidateTest,
|
||||
RopeBackendTestCase):
|
||||
if sys.version_info < (2, 7):
|
||||
JSON_LOADS_DOCSTRING = (
|
||||
'loads(s, encoding=None, cls=None, object_hook=None, '
|
||||
'parse_float=None, parse_int=None, parse_constant=None, '
|
||||
'**kw):'
|
||||
)
|
||||
else:
|
||||
JSON_LOADS_DOCSTRING = (
|
||||
'loads(s, encoding=None, cls=None, object_hook=None, '
|
||||
'parse_float=None, parse_int=None, parse_constant=None, '
|
||||
'object_pairs_hook=None, **kw):'
|
||||
)
|
||||
|
||||
|
||||
class TestGetPythonProjectFiles(RopeBackendTestCase):
|
||||
def test(self):
|
||||
self.project_file("foo.txt", "")
|
||||
self.project_file("gooddir/__init__.py", "")
|
||||
self.project_file("gooddir/file.py", "")
|
||||
self.project_file("baddir/file.py", "")
|
||||
expected = set(os.path.join(self.project_root, name)
|
||||
for name in ["foo.txt", "gooddir/__init__.py",
|
||||
"gooddir/file.py"])
|
||||
project = self.backend.project
|
||||
|
||||
actual = set(resource.real_path
|
||||
for resource
|
||||
in ropebackend.get_python_project_files(project))
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
||||
class TestPatchProjectFiles(RopeBackendTestCase):
|
||||
def test(self):
|
||||
self.project_file("foo.txt", "")
|
||||
self.project_file("gooddir/__init__.py", "")
|
||||
self.project_file("gooddir/file.py", "")
|
||||
self.project_file("baddir/file.py", "")
|
||||
expected = set(os.path.join(self.project_root, name)
|
||||
for name in ["foo.txt", "gooddir/__init__.py",
|
||||
"gooddir/file.py"])
|
||||
|
||||
actual = set(resource.real_path
|
||||
for resource
|
||||
in self.backend.project.get_files())
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
||||
class TestCallRope(RopeBackendTestCase):
|
||||
def test_should_return_value(self):
|
||||
func = mock.MagicMock()
|
||||
func.return_value = 23
|
||||
|
||||
actual = self.backend.call_rope(
|
||||
func, "foo.py", "", 0
|
||||
)
|
||||
|
||||
self.assertEqual(23, actual)
|
||||
|
||||
def test_should_raise_fault_with_data_on_exception(self):
|
||||
func = mock.MagicMock()
|
||||
func.side_effect = RuntimeError("Stuff!")
|
||||
func.__module__ = "rope.test"
|
||||
func.__name__ = "test_function"
|
||||
|
||||
try:
|
||||
self.backend.call_rope(
|
||||
func, "foo.py", "", 0
|
||||
)
|
||||
except rpc.Fault as e:
|
||||
self.assertEqual(500, e.code)
|
||||
self.assertEqual("Stuff!", e.message)
|
||||
self.assertIn("traceback", e.data)
|
||||
self.assertIn("rope_debug_info", e.data)
|
||||
self.assertEqual("rope.test.test_function",
|
||||
e.data["rope_debug_info"]["function_name"])
|
|
@ -0,0 +1,209 @@
|
|||
"""Tests for elpy.rpc."""
|
||||
|
||||
import json
|
||||
import unittest
|
||||
import sys
|
||||
|
||||
from elpy import rpc
|
||||
from elpy.tests.compat import StringIO
|
||||
|
||||
|
||||
class TestFault(unittest.TestCase):
|
||||
def test_should_have_code_and_data(self):
|
||||
fault = rpc.Fault("Hello", code=250, data="Fnord")
|
||||
self.assertEqual(str(fault), "Hello")
|
||||
self.assertEqual(fault.code, 250)
|
||||
self.assertEqual(fault.data, "Fnord")
|
||||
|
||||
def test_should_have_defaults_for_code_and_data(self):
|
||||
fault = rpc.Fault("Hello")
|
||||
self.assertEqual(str(fault), "Hello")
|
||||
self.assertEqual(fault.code, 500)
|
||||
self.assertIsNone(fault.data)
|
||||
|
||||
|
||||
class TestJSONRPCServer(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.stdin = StringIO()
|
||||
self.stdout = StringIO()
|
||||
self.rpc = rpc.JSONRPCServer(self.stdin, self.stdout)
|
||||
|
||||
def write(self, s):
|
||||
self.stdin.seek(0)
|
||||
self.stdin.truncate()
|
||||
self.stdout.seek(0)
|
||||
self.stdout.truncate()
|
||||
self.stdin.write(s)
|
||||
self.stdin.seek(0)
|
||||
|
||||
def read(self):
|
||||
value = self.stdout.getvalue()
|
||||
self.stdin.seek(0)
|
||||
self.stdin.truncate()
|
||||
self.stdout.seek(0)
|
||||
self.stdout.truncate()
|
||||
return value
|
||||
|
||||
|
||||
class TestInit(TestJSONRPCServer):
|
||||
def test_should_use_arguments(self):
|
||||
self.assertEqual(self.rpc.stdin, self.stdin)
|
||||
self.assertEqual(self.rpc.stdout, self.stdout)
|
||||
|
||||
def test_should_default_to_sys(self):
|
||||
testrpc = rpc.JSONRPCServer()
|
||||
self.assertEqual(sys.stdin, testrpc.stdin)
|
||||
self.assertEqual(sys.stdout, testrpc.stdout)
|
||||
|
||||
|
||||
class TestReadJson(TestJSONRPCServer):
|
||||
def test_should_read_json(self):
|
||||
objlist = [{'foo': 'bar'},
|
||||
{'baz': 'qux', 'fnord': 'argl\nbargl'},
|
||||
"beep\r\nbeep\r\nbeep"]
|
||||
self.write("".join([(json.dumps(obj) + "\n")
|
||||
for obj in objlist]))
|
||||
for obj in objlist:
|
||||
self.assertEqual(self.rpc.read_json(),
|
||||
obj)
|
||||
|
||||
def test_should_raise_eof_on_eof(self):
|
||||
self.assertRaises(EOFError, self.rpc.read_json)
|
||||
|
||||
def test_should_fail_on_malformed_json(self):
|
||||
self.write("malformed json\n")
|
||||
self.assertRaises(ValueError,
|
||||
self.rpc.read_json)
|
||||
|
||||
|
||||
class TestWriteJson(TestJSONRPCServer):
|
||||
def test_should_write_json_line(self):
|
||||
objlist = [{'foo': 'bar'},
|
||||
{'baz': 'qux', 'fnord': 'argl\nbargl'},
|
||||
]
|
||||
for obj in objlist:
|
||||
self.rpc.write_json(**obj)
|
||||
self.assertEqual(json.loads(self.read()),
|
||||
obj)
|
||||
|
||||
|
||||
class TestHandleRequest(TestJSONRPCServer):
|
||||
def test_should_fail_if_json_does_not_contain_a_method(self):
|
||||
self.write(json.dumps(dict(params=[],
|
||||
id=23)))
|
||||
self.assertRaises(ValueError,
|
||||
self.rpc.handle_request)
|
||||
|
||||
def test_should_call_right_method(self):
|
||||
self.write(json.dumps(dict(method='foo',
|
||||
params=[1, 2, 3],
|
||||
id=23)))
|
||||
self.rpc.rpc_foo = lambda *params: params
|
||||
self.rpc.handle_request()
|
||||
self.assertEqual(json.loads(self.read()),
|
||||
dict(id=23,
|
||||
result=[1, 2, 3]))
|
||||
|
||||
def test_should_pass_defaults_for_missing_parameters(self):
|
||||
def test_method(*params):
|
||||
self.args = params
|
||||
|
||||
self.write(json.dumps(dict(method='foo')))
|
||||
self.rpc.rpc_foo = test_method
|
||||
self.rpc.handle_request()
|
||||
self.assertEqual(self.args, ())
|
||||
self.assertEqual(self.read(), "")
|
||||
|
||||
def test_should_return_error_for_missing_method(self):
|
||||
self.write(json.dumps(dict(method='foo',
|
||||
id=23)))
|
||||
self.rpc.handle_request()
|
||||
result = json.loads(self.read())
|
||||
|
||||
self.assertEqual(result["id"], 23)
|
||||
self.assertEqual(result["error"]["message"],
|
||||
"Unknown method foo")
|
||||
|
||||
def test_should_return_error_for_exception_in_method(self):
|
||||
def test_method():
|
||||
raise ValueError("An error was raised")
|
||||
|
||||
self.write(json.dumps(dict(method='foo',
|
||||
id=23)))
|
||||
self.rpc.rpc_foo = test_method
|
||||
|
||||
self.rpc.handle_request()
|
||||
result = json.loads(self.read())
|
||||
|
||||
self.assertEqual(result["id"], 23)
|
||||
self.assertEqual(result["error"]["message"], "An error was raised")
|
||||
self.assertIn("traceback", result["error"]["data"])
|
||||
|
||||
def test_should_not_include_traceback_for_faults(self):
|
||||
def test_method():
|
||||
raise rpc.Fault("This is a fault")
|
||||
|
||||
self.write(json.dumps(dict(method="foo",
|
||||
id=23)))
|
||||
self.rpc.rpc_foo = test_method
|
||||
|
||||
self.rpc.handle_request()
|
||||
result = json.loads(self.read())
|
||||
|
||||
self.assertEqual(result["id"], 23)
|
||||
self.assertEqual(result["error"]["message"], "This is a fault")
|
||||
self.assertNotIn("traceback", result["error"])
|
||||
|
||||
def test_should_add_data_for_faults(self):
|
||||
def test_method():
|
||||
raise rpc.Fault("St. Andreas' Fault",
|
||||
code=12345, data="Yippieh")
|
||||
|
||||
self.write(json.dumps(dict(method="foo", id=23)))
|
||||
self.rpc.rpc_foo = test_method
|
||||
|
||||
self.rpc.handle_request()
|
||||
result = json.loads(self.read())
|
||||
|
||||
self.assertEqual(result["error"]["data"], "Yippieh")
|
||||
|
||||
def test_should_call_handle_for_unknown_method(self):
|
||||
def test_handle(method_name, args):
|
||||
return "It works"
|
||||
self.write(json.dumps(dict(method="doesnotexist",
|
||||
id=23)))
|
||||
self.rpc.handle = test_handle
|
||||
self.rpc.handle_request()
|
||||
self.assertEqual(json.loads(self.read()),
|
||||
dict(id=23,
|
||||
result="It works"))
|
||||
|
||||
|
||||
class TestServeForever(TestJSONRPCServer):
|
||||
def handle_request(self):
|
||||
self.hr_called += 1
|
||||
if self.hr_called > 10:
|
||||
raise self.error()
|
||||
|
||||
def setUp(self):
|
||||
super(TestServeForever, self).setUp()
|
||||
self.hr_called = 0
|
||||
self.error = KeyboardInterrupt
|
||||
self.rpc.handle_request = self.handle_request
|
||||
|
||||
def test_should_call_handle_request_repeatedly(self):
|
||||
self.rpc.serve_forever()
|
||||
self.assertEqual(self.hr_called, 11)
|
||||
|
||||
def test_should_return_on_some_errors(self):
|
||||
self.error = KeyboardInterrupt
|
||||
self.rpc.serve_forever()
|
||||
self.error = EOFError
|
||||
self.rpc.serve_forever()
|
||||
self.error = SystemExit
|
||||
self.rpc.serve_forever()
|
||||
|
||||
def test_should_fail_on_most_errors(self):
|
||||
self.error = RuntimeError
|
||||
self.assertRaises(RuntimeError,
|
||||
self.rpc.serve_forever)
|
|
@ -0,0 +1,441 @@
|
|||
# coding: utf-8
|
||||
|
||||
"""Tests for the elpy.server module"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from elpy import rpc
|
||||
from elpy import server
|
||||
from elpy.tests import compat
|
||||
from elpy.tests.support import BackendTestCase
|
||||
import elpy.refactor
|
||||
|
||||
|
||||
class ServerTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.srv = server.ElpyRPCServer()
|
||||
|
||||
|
||||
class BackendCallTestCase(ServerTestCase):
|
||||
def assert_calls_backend(self, method):
|
||||
with mock.patch("elpy.server.get_source") as get_source:
|
||||
with mock.patch.object(self.srv, "backend") as backend:
|
||||
get_source.return_value = "transformed source"
|
||||
|
||||
getattr(self.srv, method)("filename", "source", "offset")
|
||||
|
||||
get_source.assert_called_with("source")
|
||||
getattr(backend, method).assert_called_with(
|
||||
"filename", "transformed source", "offset"
|
||||
)
|
||||
|
||||
|
||||
class TestInit(ServerTestCase):
|
||||
def test_should_not_select_a_backend_by_default(self):
|
||||
self.assertIsNone(self.srv.backend)
|
||||
|
||||
|
||||
class TestRPCEcho(ServerTestCase):
|
||||
def test_should_return_arguments(self):
|
||||
self.assertEqual(("hello", "world"),
|
||||
self.srv.rpc_echo("hello", "world"))
|
||||
|
||||
|
||||
class TestRPCInit(ServerTestCase):
|
||||
@mock.patch("elpy.jedibackend.JediBackend")
|
||||
@mock.patch("elpy.ropebackend.RopeBackend")
|
||||
def test_should_set_project_root(self, RopeBackend, JediBackend):
|
||||
self.srv.rpc_init({"project_root": "/project/root",
|
||||
"backend": "rope"})
|
||||
|
||||
self.assertEqual("/project/root", self.srv.project_root)
|
||||
|
||||
@mock.patch("elpy.jedibackend.JediBackend")
|
||||
@mock.patch("elpy.ropebackend.RopeBackend")
|
||||
def test_should_initialize_rope(self, RopeBackend, JediBackend):
|
||||
self.srv.rpc_init({"project_root": "/project/root",
|
||||
"backend": "rope"})
|
||||
|
||||
RopeBackend.assert_called_with("/project/root")
|
||||
|
||||
@mock.patch("elpy.jedibackend.JediBackend")
|
||||
@mock.patch("elpy.ropebackend.RopeBackend")
|
||||
def test_should_initialize_jedi(self, RopeBackend, JediBackend):
|
||||
self.srv.rpc_init({"project_root": "/project/root",
|
||||
"backend": "jedi"})
|
||||
|
||||
JediBackend.assert_called_with("/project/root")
|
||||
|
||||
@mock.patch("elpy.jedibackend.JediBackend")
|
||||
@mock.patch("elpy.ropebackend.RopeBackend")
|
||||
def test_should_use_rope_if_available_and_requested(
|
||||
self, RopeBackend, JediBackend):
|
||||
RopeBackend.return_value.name = "rope"
|
||||
JediBackend.return_value.name = "jedi"
|
||||
|
||||
self.srv.rpc_init({"project_root": "/project/root",
|
||||
"backend": "rope"})
|
||||
|
||||
self.assertEqual("rope", self.srv.backend.name)
|
||||
|
||||
@mock.patch("elpy.jedibackend.JediBackend")
|
||||
@mock.patch("elpy.ropebackend.RopeBackend")
|
||||
def test_should_use_jedi_if_available_and_requested(
|
||||
self, RopeBackend, JediBackend):
|
||||
RopeBackend.return_value.name = "rope"
|
||||
JediBackend.return_value.name = "jedi"
|
||||
|
||||
self.srv.rpc_init({"project_root": "/project/root",
|
||||
"backend": "jedi"})
|
||||
|
||||
self.assertEqual("jedi", self.srv.backend.name)
|
||||
|
||||
@mock.patch("elpy.jedibackend.JediBackend")
|
||||
@mock.patch("elpy.ropebackend.RopeBackend")
|
||||
def test_should_use_rope_if_available_and_nothing_requested(
|
||||
self, RopeBackend, JediBackend):
|
||||
RopeBackend.return_value.name = "rope"
|
||||
JediBackend.return_value.name = "jedi"
|
||||
|
||||
self.srv.rpc_init({"project_root": "/project/root",
|
||||
"backend": None})
|
||||
|
||||
self.assertEqual("rope", self.srv.backend.name)
|
||||
|
||||
@mock.patch("elpy.jedibackend.JediBackend")
|
||||
@mock.patch("elpy.ropebackend.RopeBackend")
|
||||
def test_should_use_jedi_if_rope_not_available_and_nothing_requested(
|
||||
self, RopeBackend, JediBackend):
|
||||
RopeBackend.return_value.name = "rope"
|
||||
JediBackend.return_value.name = "jedi"
|
||||
old_rope = server.ropebackend
|
||||
server.ropebackend = None
|
||||
|
||||
try:
|
||||
self.srv.rpc_init({"project_root": "/project/root",
|
||||
"backend": None})
|
||||
finally:
|
||||
server.ropebackend = old_rope
|
||||
|
||||
self.assertEqual("jedi", self.srv.backend.name)
|
||||
|
||||
@mock.patch("elpy.jedibackend.JediBackend")
|
||||
@mock.patch("elpy.ropebackend.RopeBackend")
|
||||
def test_should_use_none_if_nothing_available(
|
||||
self, RopeBackend, JediBackend):
|
||||
RopeBackend.return_value.name = "rope"
|
||||
JediBackend.return_value.name = "jedi"
|
||||
old_rope = server.ropebackend
|
||||
old_jedi = server.jedibackend
|
||||
server.ropebackend = None
|
||||
server.jedibackend = None
|
||||
|
||||
try:
|
||||
self.srv.rpc_init({"project_root": "/project/root",
|
||||
"backend": None})
|
||||
finally:
|
||||
server.ropebackend = old_rope
|
||||
server.jedibackend = old_jedi
|
||||
|
||||
self.assertIsNone(self.srv.backend)
|
||||
|
||||
|
||||
class TestRPCGetCalltip(BackendCallTestCase):
|
||||
def test_should_call_backend(self):
|
||||
self.assert_calls_backend("rpc_get_calltip")
|
||||
|
||||
def test_should_handle_no_backend(self):
|
||||
self.srv.backend = None
|
||||
self.assertIsNone(self.srv.rpc_get_calltip("filname", "source",
|
||||
"offset"))
|
||||
|
||||
|
||||
class TestRPCGetCompletions(BackendCallTestCase):
|
||||
def test_should_call_backend(self):
|
||||
self.assert_calls_backend("rpc_get_completions")
|
||||
|
||||
def test_should_handle_no_backend(self):
|
||||
self.srv.backend = None
|
||||
self.assertEqual([],
|
||||
self.srv.rpc_get_completions("filname", "source",
|
||||
"offset"))
|
||||
|
||||
def test_should_sort_results(self):
|
||||
with mock.patch.object(self.srv, 'backend') as backend:
|
||||
backend.rpc_get_completions.return_value = [
|
||||
{'name': '_e'},
|
||||
{'name': '__d'},
|
||||
{'name': 'c'},
|
||||
{'name': 'B'},
|
||||
{'name': 'a'},
|
||||
]
|
||||
expected = list(reversed(backend.rpc_get_completions.return_value))
|
||||
|
||||
actual = self.srv.rpc_get_completions("filename", "source",
|
||||
"offset")
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_should_uniquify_results(self):
|
||||
with mock.patch.object(self.srv, 'backend') as backend:
|
||||
backend.rpc_get_completions.return_value = [
|
||||
{'name': 'a'},
|
||||
{'name': 'a'},
|
||||
]
|
||||
expected = [{'name': 'a'}]
|
||||
|
||||
actual = self.srv.rpc_get_completions("filename", "source",
|
||||
"offset")
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
||||
class TestRPCGetCompletionDocs(ServerTestCase):
|
||||
def test_should_call_backend(self):
|
||||
with mock.patch.object(self.srv, "backend") as backend:
|
||||
self.srv.rpc_get_completion_docstring("completion")
|
||||
|
||||
(backend.rpc_get_completion_docstring
|
||||
.assert_called_with("completion"))
|
||||
|
||||
def test_should_handle_no_backend(self):
|
||||
self.srv.backend = None
|
||||
self.assertIsNone(self.srv.rpc_get_completion_docstring("foo"))
|
||||
|
||||
|
||||
class TestRPCGetCompletionLocation(ServerTestCase):
|
||||
def test_should_call_backend(self):
|
||||
with mock.patch.object(self.srv, "backend") as backend:
|
||||
self.srv.rpc_get_completion_location("completion")
|
||||
|
||||
(backend.rpc_get_completion_location
|
||||
.assert_called_with("completion"))
|
||||
|
||||
def test_should_handle_no_backend(self):
|
||||
self.srv.backend = None
|
||||
self.assertIsNone(self.srv.rpc_get_completion_location("foo"))
|
||||
|
||||
|
||||
class TestRPCGetDefinition(BackendCallTestCase):
|
||||
def test_should_call_backend(self):
|
||||
self.assert_calls_backend("rpc_get_definition")
|
||||
|
||||
def test_should_handle_no_backend(self):
|
||||
self.srv.backend = None
|
||||
self.assertIsNone(self.srv.rpc_get_definition("filname", "source",
|
||||
"offset"))
|
||||
|
||||
|
||||
class TestRPCGetAssignment(BackendCallTestCase):
|
||||
def test_should_call_backend(self):
|
||||
self.assert_calls_backend("rpc_get_assignment")
|
||||
|
||||
def test_should_handle_no_backend(self):
|
||||
self.srv.backend = None
|
||||
self.assertIsNone(self.srv.rpc_get_assignment("filname", "source",
|
||||
"offset"))
|
||||
|
||||
|
||||
class TestRPCGetDocstring(BackendCallTestCase):
|
||||
def test_should_call_backend(self):
|
||||
self.assert_calls_backend("rpc_get_docstring")
|
||||
|
||||
def test_should_handle_no_backend(self):
|
||||
self.srv.backend = None
|
||||
self.assertIsNone(self.srv.rpc_get_docstring("filname", "source",
|
||||
"offset"))
|
||||
|
||||
|
||||
class TestRPCGetPydocCompletions(ServerTestCase):
|
||||
@mock.patch.object(server, 'get_pydoc_completions')
|
||||
def test_should_call_pydoc_completions(self, get_pydoc_completions):
|
||||
srv = server.ElpyRPCServer()
|
||||
srv.rpc_get_pydoc_completions()
|
||||
get_pydoc_completions.assert_called_with(None)
|
||||
srv.rpc_get_pydoc_completions("foo")
|
||||
get_pydoc_completions.assert_called_with("foo")
|
||||
|
||||
|
||||
class TestGetPydocDocumentation(ServerTestCase):
|
||||
@mock.patch("pydoc.render_doc")
|
||||
def test_should_find_documentation(self, render_doc):
|
||||
render_doc.return_value = "expected"
|
||||
|
||||
actual = self.srv.rpc_get_pydoc_documentation("open")
|
||||
|
||||
render_doc.assert_called_with("open",
|
||||
"Elpy Pydoc Documentation for %s",
|
||||
False)
|
||||
self.assertEqual("expected", actual)
|
||||
|
||||
def test_should_return_none_for_unknown_module(self):
|
||||
actual = self.srv.rpc_get_pydoc_documentation("frob.open")
|
||||
|
||||
self.assertIsNone(actual)
|
||||
|
||||
def test_should_return_valid_unicode(self):
|
||||
import json
|
||||
|
||||
docstring = self.srv.rpc_get_pydoc_documentation("tarfile")
|
||||
|
||||
json.dumps(docstring)
|
||||
|
||||
|
||||
class TestRPCGetRefactorOptions(BackendTestCase):
|
||||
@mock.patch.object(compat.builtins, '__import__')
|
||||
def test_should_fail_if_rope_is_not_available(self, import_):
|
||||
import_.side_effect = ImportError
|
||||
filename = self.project_file("foo.py", "")
|
||||
srv = server.ElpyRPCServer()
|
||||
self.assertRaises(ImportError, srv.rpc_get_refactor_options,
|
||||
filename, 0)
|
||||
|
||||
@mock.patch.object(elpy.refactor, 'Refactor')
|
||||
def test_should_initialize_and_call_refactor_object(self, Refactor):
|
||||
filename = self.project_file("foo.py", "import foo")
|
||||
srv = server.ElpyRPCServer()
|
||||
srv.project_root = self.project_root
|
||||
|
||||
srv.rpc_get_refactor_options(filename, 5)
|
||||
|
||||
Refactor.assert_called_with(self.project_root, filename)
|
||||
Refactor.return_value.get_refactor_options.assert_called_with(5, None)
|
||||
|
||||
|
||||
class TestRPCRefactor(BackendTestCase):
|
||||
@mock.patch.object(compat.builtins, '__import__')
|
||||
def test_should_fail_if_rope_is_not_available(self, import_):
|
||||
import_.side_effect = ImportError
|
||||
filename = self.project_file("foo.py", "")
|
||||
srv = server.ElpyRPCServer()
|
||||
self.assertRaises(ImportError, srv.rpc_refactor,
|
||||
filename, 'foo', ())
|
||||
|
||||
@mock.patch.object(elpy.refactor, 'Refactor')
|
||||
def test_should_initialize_and_call_refactor_object_with_args(
|
||||
self, Refactor):
|
||||
filename = self.project_file("foo.py", "import foo")
|
||||
srv = server.ElpyRPCServer()
|
||||
srv.project_root = self.project_root
|
||||
|
||||
srv.rpc_refactor(filename, 'foo', (1, 2, 3))
|
||||
|
||||
Refactor.assert_called_with(self.project_root, filename)
|
||||
Refactor.return_value.get_changes.assert_called_with('foo', 1, 2, 3)
|
||||
|
||||
@mock.patch.object(elpy.refactor, 'Refactor')
|
||||
def test_should_initialize_and_call_refactor_object_without_args(
|
||||
self, Refactor):
|
||||
filename = self.project_file("foo.py", "import foo")
|
||||
srv = server.ElpyRPCServer()
|
||||
srv.project_root = self.project_root
|
||||
|
||||
srv.rpc_refactor(filename, 'foo', None)
|
||||
|
||||
Refactor.assert_called_with(self.project_root, filename)
|
||||
Refactor.return_value.get_changes.assert_called_with('foo')
|
||||
|
||||
|
||||
class TestRPCGetUsages(BackendCallTestCase):
|
||||
def test_should_call_backend(self):
|
||||
self.assert_calls_backend("rpc_get_usages")
|
||||
|
||||
def test_should_handle_no_backend(self):
|
||||
self.srv.backend = None
|
||||
with self.assertRaises(rpc.Fault):
|
||||
self.assertIsNone(self.srv.rpc_get_usages("filname", "source",
|
||||
"offset"))
|
||||
|
||||
|
||||
class TestRPCGetNames(BackendCallTestCase):
|
||||
def test_should_call_backend(self):
|
||||
self.assert_calls_backend("rpc_get_names")
|
||||
|
||||
def test_should_handle_no_backend(self):
|
||||
self.srv.backend = None
|
||||
with self.assertRaises(rpc.Fault):
|
||||
self.assertIsNone(self.srv.rpc_get_names("filname", "source", 0))
|
||||
|
||||
|
||||
class TestRPCImportMagic(ServerTestCase):
|
||||
def test_should_call_importmagic(self):
|
||||
with mock.patch.object(self.srv, "import_magic") as impmagic:
|
||||
self.srv.rpc_get_import_symbols("filename", "source", "os")
|
||||
impmagic.get_import_symbols.assert_called_with("os")
|
||||
self.srv.rpc_add_import("filename", "source", "import os")
|
||||
impmagic.add_import.assert_called_with("source", "import os")
|
||||
self.srv.rpc_get_unresolved_symbols("filename", "source")
|
||||
impmagic.get_unresolved_symbols.assert_called_with("source")
|
||||
self.srv.rpc_remove_unreferenced_imports("filename", "source")
|
||||
impmagic.remove_unreferenced_imports.assert_called_with("source")
|
||||
|
||||
|
||||
class TestGetSource(unittest.TestCase):
|
||||
def test_should_return_string_by_default(self):
|
||||
self.assertEqual(server.get_source("foo"),
|
||||
"foo")
|
||||
|
||||
def test_should_return_file_contents(self):
|
||||
fd, filename = tempfile.mkstemp(prefix="elpy-test-")
|
||||
self.addCleanup(os.remove, filename)
|
||||
with open(filename, "w") as f:
|
||||
f.write("file contents")
|
||||
|
||||
fileobj = {'filename': filename}
|
||||
|
||||
self.assertEqual(server.get_source(fileobj),
|
||||
"file contents")
|
||||
|
||||
def test_should_clean_up_tempfile(self):
|
||||
fd, filename = tempfile.mkstemp(prefix="elpy-test-")
|
||||
with open(filename, "w") as f:
|
||||
f.write("file contents")
|
||||
|
||||
fileobj = {'filename': filename,
|
||||
'delete_after_use': True}
|
||||
|
||||
self.assertEqual(server.get_source(fileobj),
|
||||
"file contents")
|
||||
self.assertFalse(os.path.exists(filename))
|
||||
|
||||
def test_should_support_utf8(self):
|
||||
fd, filename = tempfile.mkstemp(prefix="elpy-test-")
|
||||
self.addCleanup(os.remove, filename)
|
||||
with open(filename, "wb") as f:
|
||||
f.write(u"möp".encode("utf-8"))
|
||||
|
||||
source = server.get_source({'filename': filename})
|
||||
|
||||
self.assertEqual(source, u"möp")
|
||||
|
||||
|
||||
class TestPysymbolKey(BackendTestCase):
|
||||
def keyLess(self, a, b):
|
||||
self.assertLess(b, a)
|
||||
self.assertLess(server._pysymbol_key(a),
|
||||
server._pysymbol_key(b))
|
||||
|
||||
def test_should_be_case_insensitive(self):
|
||||
self.keyLess("bar", "Foo")
|
||||
|
||||
def test_should_sort_private_symbols_after_public_symbols(self):
|
||||
self.keyLess("foo", "_bar")
|
||||
|
||||
def test_should_sort_private_symbols_after_dunder_symbols(self):
|
||||
self.assertLess(server._pysymbol_key("__foo__"),
|
||||
server._pysymbol_key("_bar"))
|
||||
|
||||
def test_should_sort_dunder_symbols_after_public_symbols(self):
|
||||
self.keyLess("bar", "__foo")
|
||||
|
||||
|
||||
class Autopep8TestCase(ServerTestCase):
|
||||
|
||||
def test_rpc_fix_code_should_return_formatted_string(self):
|
||||
code_block = 'x= 123\n'
|
||||
new_block = self.srv.rpc_fix_code(code_block)
|
||||
self.assertEqual(new_block, 'x = 123\n')
|
|
@ -0,0 +1,19 @@
|
|||
"""Tests for elpy.tests.support. Yep, we test test code."""
|
||||
|
||||
import unittest
|
||||
|
||||
from elpy.tests.support import source_and_offset
|
||||
|
||||
|
||||
class TestSourceAndOffset(unittest.TestCase):
|
||||
def test_should_return_source_and_offset(self):
|
||||
self.assertEqual(source_and_offset("hello, _|_world"),
|
||||
("hello, world", 7))
|
||||
|
||||
def test_should_handle_beginning_of_string(self):
|
||||
self.assertEqual(source_and_offset("_|_hello, world"),
|
||||
("hello, world", 0))
|
||||
|
||||
def test_should_handle_end_of_string(self):
|
||||
self.assertEqual(source_and_offset("hello, world_|_"),
|
||||
("hello, world", 12))
|
|
@ -0,0 +1,32 @@
|
|||
# coding: utf-8
|
||||
"""Tests for the elpy.yapf module"""
|
||||
|
||||
import unittest
|
||||
|
||||
from elpy import yapfutil
|
||||
from elpy.rpc import Fault
|
||||
from elpy.tests.support import BackendTestCase
|
||||
|
||||
|
||||
@unittest.skipIf(yapfutil.YAPF_NOT_SUPPORTED,
|
||||
'yapf not supported for current python version')
|
||||
class YAPFTestCase(BackendTestCase):
|
||||
def setUp(self):
|
||||
if yapfutil.YAPF_NOT_SUPPORTED:
|
||||
raise unittest.SkipTest
|
||||
|
||||
def test_fix_code_should_throw_error_for_invalid_code(self):
|
||||
src = 'x = '
|
||||
self.assertRaises(Fault, yapfutil.fix_code, src)
|
||||
|
||||
def test_fix_code(self):
|
||||
testdata = [
|
||||
('x= 123\n', 'x = 123\n'),
|
||||
('x=1; \ny=2 \n', 'x = 1\ny = 2\n'),
|
||||
]
|
||||
for src, expected in testdata:
|
||||
self._assert_format(src, expected)
|
||||
|
||||
def _assert_format(self, src, expected):
|
||||
new_block = yapfutil.fix_code(src)
|
||||
self.assertEqual(new_block, expected)
|
|
@ -0,0 +1,38 @@
|
|||
"""Glue for the "yapf" library.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from elpy.rpc import Fault
|
||||
|
||||
YAPF_NOT_SUPPORTED = sys.version_info < (2, 7) or (
|
||||
sys.version_info >= (3, 0) and sys.version_info < (3, 4))
|
||||
|
||||
try:
|
||||
if YAPF_NOT_SUPPORTED:
|
||||
yapf_api = None
|
||||
else:
|
||||
from yapf.yapflib import yapf_api
|
||||
from yapf.yapflib import file_resources
|
||||
except ImportError: # pragma: no cover
|
||||
yapf_api = None
|
||||
|
||||
|
||||
def fix_code(code):
|
||||
"""Formats Python code to conform to the PEP 8 style guide.
|
||||
|
||||
"""
|
||||
if not yapf_api:
|
||||
raise Fault('yapf not installed', code=400)
|
||||
style_config = file_resources.GetDefaultStyleForDir(os.getcwd())
|
||||
try:
|
||||
reformatted_source, _ = yapf_api.FormatCode(code,
|
||||
filename='<stdin>',
|
||||
style_config=style_config,
|
||||
verify=False)
|
||||
return reformatted_source
|
||||
except Exception as e:
|
||||
raise Fault("Error during formatting: {}".format(e),
|
||||
code=400)
|
|
@ -0,0 +1,68 @@
|
|||
(defun elpy-snippet-split-args (arg-string)
|
||||
"Split a python argument string into ((name, default)..) tuples"
|
||||
(mapcar (lambda (x)
|
||||
(split-string x "[[:blank:]]*=[[:blank:]]*" t))
|
||||
(split-string arg-string "[[:blank:]]*,[[:blank:]]*" t)))
|
||||
|
||||
(defun elpy-snippet-current-method-and-args ()
|
||||
"Return information on the current definition."
|
||||
(let ((current-defun (python-info-current-defun))
|
||||
(current-arglist
|
||||
(save-excursion
|
||||
(python-nav-beginning-of-defun)
|
||||
(when (re-search-forward "(" nil t)
|
||||
(let* ((start (point))
|
||||
(end (progn
|
||||
(forward-char -1)
|
||||
(forward-sexp)
|
||||
(- (point) 1))))
|
||||
(elpy-snippet-split-args
|
||||
(buffer-substring-no-properties start end))))))
|
||||
class method args)
|
||||
(when (not current-arglist)
|
||||
(setq current-arglist '(("self"))))
|
||||
(if (and current-defun
|
||||
(string-match "^\\(.*\\)\\.\\(.*\\)$" current-defun))
|
||||
(setq class (match-string 1 current-defun)
|
||||
method (match-string 2 current-defun))
|
||||
(setq class "Class"
|
||||
method "method"))
|
||||
(setq args (mapcar #'car current-arglist))
|
||||
(list class method args)))
|
||||
|
||||
(defun elpy-snippet-init-assignments (arg-string)
|
||||
"Return the typical __init__ assignments for arguments."
|
||||
(let ((indentation (make-string (save-excursion
|
||||
(goto-char start-point)
|
||||
(current-indentation))
|
||||
?\s)))
|
||||
(mapconcat (lambda (arg)
|
||||
(if (string-match "^\\*" (car arg))
|
||||
""
|
||||
(format "self.%s = %s\n%s"
|
||||
(car arg)
|
||||
(car arg)
|
||||
indentation)))
|
||||
(elpy-snippet-split-args arg-string)
|
||||
"")))
|
||||
|
||||
(defun elpy-snippet-super-form ()
|
||||
"Return (Class, first-arg).method if Py2.
|
||||
Else return ().method for Py3."
|
||||
(let* ((defun-info (elpy-snippet-current-method-and-args))
|
||||
(class (nth 0 defun-info))
|
||||
(method (nth 1 defun-info))
|
||||
(args (nth 2 defun-info))
|
||||
(first-arg (nth 0 args))
|
||||
(py-version-command " -c 'import sys ; print(sys.version_info.major)'")
|
||||
;; Get the python version. Either 2 or 3
|
||||
(py-version-num (substring (shell-command-to-string (concat elpy-rpc-python-command py-version-command))0 1)))
|
||||
(if (string-match py-version-num "2")
|
||||
(format "(%s, %s).%s" class first-arg method)
|
||||
(format "().%s" method))))
|
||||
|
||||
(defun elpy-snippet-super-arguments ()
|
||||
"Return the argument list for the current method."
|
||||
(mapconcat (lambda (x) x)
|
||||
(cdr (nth 2 (elpy-snippet-current-method-and-args)))
|
||||
", "))
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
|||
# -*- mode: snippet -*-
|
||||
# name: __abs__
|
||||
# key: __abs__
|
||||
# group: Special methods
|
||||
# --
|
||||
def __abs__(self):
|
||||
return $0
|
|
@ -0,0 +1,7 @@
|
|||
# -*- mode: snippet -*-
|
||||
# name: __add__
|
||||
# key: __add__
|
||||
# group: Special methods
|
||||
# --
|
||||
def __add__(self, other):
|
||||
return $0
|
|
@ -0,0 +1,7 @@
|
|||
# -*- mode: snippet -*-
|
||||
# name: __and__
|
||||
# key: __and__
|
||||
# group: Special methods
|
||||
# --
|
||||
def __and__(self, other):
|
||||
return $0
|
|
@ -0,0 +1,7 @@
|
|||
# -*- mode: snippet -*-
|
||||
# name: __bool__
|
||||
# key: __bool__
|
||||
# group: Special methods
|
||||
# --
|
||||
def __bool__(self):
|
||||
return $0
|
|
@ -0,0 +1,7 @@
|
|||
# -*- mode: snippet -*-
|
||||
# name: __call__
|
||||
# key: __call__
|
||||
# group: Special methods
|
||||
# --
|
||||
def __call__(self, ${1:*args}):
|
||||
return $0
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue