add lisp packages

This commit is contained in:
2020-12-05 21:05:39 +01:00
parent 7ae2bd594f
commit 85e20365ae
70 changed files with 90996 additions and 0 deletions

852
lisp/anaconda-mode.el Normal file
View File

@@ -0,0 +1,852 @@
;;; anaconda-mode.el --- Code navigation, documentation lookup and completion for Python -*- lexical-binding: t; -*-
;; Copyright (C) 2013-2018 by Artem Malyshev
;; Author: Artem Malyshev <proofit404@gmail.com>
;; URL: https://github.com/proofit404/anaconda-mode
;; Package-Version: 20200129.1718
;; Package-Commit: 10299bd9ff38c4f0da1d892905d02ef828e7fdce
;; Version: 0.1.13
;; Package-Requires: ((emacs "25.1") (pythonic "0.1.0") (dash "2.6.0") (s "1.9") (f "0.16.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:
;; See the README for more details.
;;; Code:
(require 'ansi-color)
(require 'pythonic)
(require 'tramp)
(require 'xref)
(require 'json)
(require 'dash)
(require 'url)
(require 's)
(require 'f)
(defgroup anaconda nil
"Code navigation, documentation lookup and completion for Python."
:group 'programming)
(defcustom anaconda-mode-installation-directory
(locate-user-emacs-file "anaconda-mode")
"Installation directory for `anaconda-mode' server."
:type 'directory)
(defcustom anaconda-mode-eldoc-as-single-line nil
"If not nil, trim eldoc string to frame width."
:type 'boolean)
(defcustom anaconda-mode-lighter " Anaconda"
"Text displayed in the mode line when `anaconda-mode is active."
:type 'sexp)
(defcustom anaconda-mode-localhost-address "127.0.0.1"
"Address used by `anaconda-mode' to resolve localhost."
:type 'string)
(defcustom anaconda-mode-doc-frame-background (face-attribute 'default :background)
"Doc frame background color, default color is current theme's background."
:type 'string)
(defcustom anaconda-mode-doc-frame-foreground (face-attribute 'default :foreground)
"Doc frame foreground color, default color is current theme's foreground."
:type 'string)
(defcustom anaconda-mode-use-posframe-show-doc nil
"If the value is not nil, use posframe to show eldoc."
:type 'boolean)
(defcustom anaconda-mode-tunnel-setup-sleep 2
"Time in seconds `anaconda-mode' waits after tunnel creation before first RPC call."
:group 'anaconda-mode
:type 'integer)
;;; Compatibility
;; Functions from posframe which is an optional dependency
(declare-function posframe-workable-p "posframe")
(declare-function posframe-hide "posframe")
(declare-function posframe-show "posframe")
;;; Server.
(defvar anaconda-mode-server-version "0.1.13"
"Server version needed to run `anaconda-mode'.")
(defvar anaconda-mode-server-command "
from __future__ import print_function
# CLI arguments.
import sys
assert len(sys.argv) > 3, 'CLI arguments: %s' % sys.argv
server_directory = sys.argv[-3]
server_address = sys.argv[-2]
virtual_environment = sys.argv[-1]
# Ensure directory.
import os
server_directory = os.path.expanduser(server_directory)
virtual_environment = os.path.expanduser(virtual_environment)
if not os.path.exists(server_directory):
os.makedirs(server_directory)
# Installation check.
jedi_dep = ('jedi', '0.13.0')
service_factory_dep = ('service_factory', '0.1.5')
missing_dependencies = []
def instrument_installation():
for package in (jedi_dep, service_factory_dep):
package_is_installed = False
for path in os.listdir(server_directory):
path = os.path.join(server_directory, path)
if path.endswith('.egg') and os.path.isdir(path):
if path not in sys.path:
sys.path.insert(0, path)
if package[0] in path:
package_is_installed = True
if not package_is_installed:
missing_dependencies.append('>='.join(package))
instrument_installation()
# Installation.
def install_deps():
import site
import setuptools.command.easy_install
site.addsitedir(server_directory)
cmd = ['--install-dir', server_directory,
'--site-dirs', server_directory,
'--always-copy','--always-unzip']
cmd.extend(missing_dependencies)
setuptools.command.easy_install.main(cmd)
instrument_installation()
if missing_dependencies:
install_deps()
del missing_dependencies[:]
try:
import jedi
except ImportError:
missing_dependencies.append('>='.join(jedi_dep))
try:
import service_factory
except ImportError:
missing_dependencies.append('>='.join(service_factory_dep))
# Try one more time in case if anaconda installation gets broken somehow
if missing_dependencies:
install_deps()
import jedi
import service_factory
# Setup server.
assert jedi.__version__ >= jedi_dep[1], 'Jedi version should be >= %s, current version: %s' % (jedi_dep[1], jedi.__version__,)
if virtual_environment:
virtual_environment = jedi.create_environment(virtual_environment, safe=False)
else:
virtual_environment = None
# Define JSON-RPC application.
import functools
import threading
def script_method(f):
@functools.wraps(f)
def wrapper(source, line, column, path):
timer = threading.Timer(30.0, sys.exit)
timer.start()
result = f(jedi.Script(source, line, column, path, environment=virtual_environment))
timer.cancel()
return result
return wrapper
def process_definitions(f):
@functools.wraps(f)
def wrapper(script):
definitions = f(script)
if len(definitions) == 1 and not definitions[0].module_path:
return '%s is defined in %s compiled module' % (
definitions[0].name, definitions[0].module_name)
return [[definition.module_path,
definition.line,
definition.column,
definition.get_line_code().strip()]
for definition in definitions
if definition.module_path] or None
return wrapper
@script_method
def complete(script):
return [[definition.name, definition.type]
for definition in script.completions()]
@script_method
def company_complete(script):
return [[definition.name,
definition.type,
definition.docstring(),
definition.module_path,
definition.line]
for definition in script.completions()]
@script_method
def show_doc(script):
return [[definition.module_name, definition.docstring()]
for definition in script.goto_definitions()]
@script_method
@process_definitions
def goto_definitions(script):
return script.goto_definitions()
@script_method
@process_definitions
def goto_assignments(script):
return script.goto_assignments()
@script_method
@process_definitions
def usages(script):
return script.usages()
@script_method
def eldoc(script):
signatures = script.call_signatures()
if len(signatures) == 1:
signature = signatures[0]
return [signature.name,
signature.index,
[param.description[6:] for param in signature.params]]
# Run.
app = [complete, company_complete, show_doc, goto_definitions, goto_assignments, usages, eldoc]
service_factory.service_factory(app, server_address, 0, 'anaconda_mode port {port}')
" "Run `anaconda-mode' server.")
(defvar anaconda-mode-process-name "anaconda-mode"
"Process name for `anaconda-mode' processes.")
(defvar anaconda-mode-process-buffer "*anaconda-mode*"
"Buffer name for `anaconda-mode' process.")
(defvar anaconda-mode-process nil
"Currently running `anaconda-mode' process.")
(defvar anaconda-mode-response-buffer "*anaconda-response*"
"Buffer name for error report when `anaconda-mode' fail to read server response.")
(defvar anaconda-mode-socat-process-name "anaconda-socat"
"Process name for `anaconda-mode' socat companion process.")
(defvar anaconda-mode-socat-process-buffer "*anaconda-socat*"
"Buffer name for `anaconda-mode' socat companion process.")
(defvar anaconda-mode-socat-process nil
"Currently running `anaconda-mode' socat companion process.")
(defvar anaconda-mode-ssh-process-name "anaconda-ssh"
"Process name for `anaconda-mode' ssh port forward companion process.")
(defvar anaconda-mode-ssh-process-buffer "*anaconda-ssh*"
"Buffer name for `anaconda-mode' ssh port forward companion process.")
(defvar anaconda-mode-ssh-process nil
"Currently running `anaconda-mode' ssh port forward companion process.")
(defvar anaconda-mode-doc-frame-name "*Anaconda Posframe*"
"The posframe to show anaconda documentation.")
(defvar anaconda-mode-frame-last-point 0
"The last point of anaconda doc view frame, use for hide frame after move point.")
(defvar anaconda-mode-frame-last-scroll-offset 0
"The last scroll offset when show doc view frame, use for hide frame after window scroll.")
(defun anaconda-mode-server-directory ()
"Anaconda mode installation directory."
(f-short (f-join anaconda-mode-installation-directory
anaconda-mode-server-version)))
(defun anaconda-mode-host ()
"Target host with `anaconda-mode' server."
(cond
((pythonic-remote-docker-p)
anaconda-mode-localhost-address)
((pythonic-remote-p)
(pythonic-remote-host))
(t
anaconda-mode-localhost-address)))
(defun anaconda-mode-port ()
"Port for `anaconda-mode' connection."
(process-get anaconda-mode-process 'port))
(defun anaconda-mode-start (&optional callback)
"Start `anaconda-mode' server.
CALLBACK function will be called when `anaconda-mode-port' will
be bound."
(when (anaconda-mode-need-restart)
(anaconda-mode-stop))
(if (anaconda-mode-running-p)
(and callback
(anaconda-mode-bound-p)
(funcall callback))
(anaconda-mode-bootstrap callback)))
(defun anaconda-mode-stop ()
"Stop `anaconda-mode' server."
(when (anaconda-mode-running-p)
(set-process-filter anaconda-mode-process nil)
(set-process-sentinel anaconda-mode-process nil)
(kill-process anaconda-mode-process)
(setq anaconda-mode-process nil))
(when (anaconda-mode-socat-running-p)
(kill-process anaconda-mode-socat-process)
(setq anaconda-mode-socat-process nil))
(when (anaconda-mode-ssh-running-p)
(kill-process anaconda-mode-ssh-process)
(setq anaconda-mode-ssh-process nil)))
(defun anaconda-mode-running-p ()
"Is `anaconda-mode' server running."
(and anaconda-mode-process
(process-live-p anaconda-mode-process)))
(defun anaconda-mode-socat-running-p ()
"Is `anaconda-mode' socat companion process running."
(and anaconda-mode-socat-process
(process-live-p anaconda-mode-socat-process)))
(defun anaconda-mode-ssh-running-p ()
"Is `anaconda-mode' ssh port forward companion process running."
(and anaconda-mode-ssh-process
(process-live-p anaconda-mode-ssh-process)))
(defun anaconda-mode-bound-p ()
"Is `anaconda-mode' port bound."
(numberp (anaconda-mode-port)))
(defun anaconda-mode-need-restart ()
"Check if we need to restart `anaconda-mode-server'."
(when (and (anaconda-mode-running-p)
(anaconda-mode-bound-p))
(not (and (equal (process-get anaconda-mode-process 'interpreter)
python-shell-interpreter)
(equal (process-get anaconda-mode-process 'virtualenv)
python-shell-virtualenv-root)
(equal (process-get anaconda-mode-process 'remote-p)
(pythonic-remote-p))
(if (pythonic-local-p)
t
(equal (process-get anaconda-mode-process 'remote-method)
(pythonic-remote-method))
(equal (process-get anaconda-mode-process 'remote-user)
(pythonic-remote-user))
(equal (process-get anaconda-mode-process 'remote-host)
(pythonic-remote-host))
(equal (process-get anaconda-mode-process 'remote-port)
(pythonic-remote-port)))))))
(defun anaconda-mode-bootstrap (&optional callback)
"Run `anaconda-mode' server.
CALLBACK function will be called when `anaconda-mode-port' will
be bound."
(setq anaconda-mode-process
(pythonic-start-process :process anaconda-mode-process-name
:buffer (get-buffer-create anaconda-mode-process-buffer)
:query-on-exit nil
:filter (lambda (process output)
(anaconda-mode-bootstrap-filter process output callback))
:sentinel (lambda (_process _event))
:args `("-c"
,anaconda-mode-server-command
,(anaconda-mode-server-directory)
,(if (pythonic-remote-p)
"0.0.0.0"
anaconda-mode-localhost-address)
,(or python-shell-virtualenv-root ""))))
(process-put anaconda-mode-process 'interpreter python-shell-interpreter)
(process-put anaconda-mode-process 'virtualenv python-shell-virtualenv-root)
(process-put anaconda-mode-process 'port nil)
(when (pythonic-remote-p)
(process-put anaconda-mode-process 'remote-p t)
(process-put anaconda-mode-process 'remote-method (pythonic-remote-method))
(process-put anaconda-mode-process 'remote-user (pythonic-remote-user))
(process-put anaconda-mode-process 'remote-host (pythonic-remote-host))
(process-put anaconda-mode-process 'remote-port (pythonic-remote-port))))
(defun anaconda-jump-proxy-string ()
"Create -J option string for SSH tunnel."
(let ((dfn
(tramp-dissect-file-name (pythonic-aliased-path default-directory))))
(when (tramp-file-name-hop dfn)
(let ((hop-list (split-string (tramp-file-name-hop dfn) "|"))
(result "-J "))
(delete "" hop-list) ;; remove empty string after final pipe
(dolist (elt hop-list result)
;; tramp-dissect-file-name expects a filename so give it dummy.file
(let ((ts (tramp-dissect-file-name (concat "/" elt ":/dummy.file"))))
(setq result (concat result
(format "%s@%s:%s,"
(tramp-file-name-user ts)
(tramp-file-name-host ts)
(or (tramp-file-name-port-or-default ts) 22))))))
;; Remove final comma
(substring result 0 -1)))))
(defun anaconda-mode-bootstrap-filter (process output &optional callback)
"Set `anaconda-mode-port' from PROCESS OUTPUT.
Connect to the `anaconda-mode' server. CALLBACK function will be
called when `anaconda-mode-port' will be bound."
;; Mimic default filter.
(when (buffer-live-p (process-buffer process))
(with-current-buffer (process-buffer process)
(save-excursion
(goto-char (process-mark process))
(insert (ansi-color-apply output))
(set-marker (process-mark process) (point)))))
(unless (anaconda-mode-bound-p)
(--when-let (s-match "anaconda_mode port \\([0-9]+\\)" output)
(process-put anaconda-mode-process 'port (string-to-number (cadr it)))
(cond ((pythonic-remote-docker-p)
(let* ((container-raw-description (with-output-to-string
(with-current-buffer
standard-output
(call-process "docker" nil t nil "inspect" (pythonic-remote-host)))))
(container-description (let ((json-array-type 'list))
(json-read-from-string container-raw-description)))
(container-ip (cdr (assoc 'IPAddress
(cdadr (assoc 'Networks
(cdr (assoc 'NetworkSettings
(car container-description)))))))))
(setq anaconda-mode-socat-process
(start-process anaconda-mode-socat-process-name
anaconda-mode-socat-process-buffer
"socat"
(format "TCP4-LISTEN:%d" (anaconda-mode-port))
(format "TCP4:%s:%d" container-ip (anaconda-mode-port))))
(set-process-query-on-exit-flag anaconda-mode-socat-process nil)))
((pythonic-remote-ssh-p)
(let ((jump (anaconda-jump-proxy-string)))
(message (format "Anaconda Jump Proxy: %s" jump))
(setq anaconda-mode-ssh-process
(if jump
(start-process anaconda-mode-ssh-process-name
anaconda-mode-ssh-process-buffer
"ssh" jump "-nNT"
"-L" (format "%s:localhost:%s" (anaconda-mode-port) (anaconda-mode-port))
(format "%s@%s" (pythonic-remote-user) (pythonic-remote-host))
"-p" (number-to-string (or (pythonic-remote-port) 22)))
(start-process anaconda-mode-ssh-process-name
anaconda-mode-ssh-process-buffer
"ssh" "-nNT"
"-L" (format "%s:localhost:%s" (anaconda-mode-port) (anaconda-mode-port))
(format "%s@%s" (pythonic-remote-user) (pythonic-remote-host))
"-p" (number-to-string (or (pythonic-remote-port) 22)))))
;; prevent race condition between tunnel setup and first use
(sleep-for anaconda-mode-tunnel-setup-sleep)
(set-process-query-on-exit-flag anaconda-mode-ssh-process nil))))
(when callback
(funcall callback)))))
;;; Interaction.
(defun anaconda-mode-call (command callback)
"Make remote procedure call for COMMAND.
Apply CALLBACK to it result."
(anaconda-mode-start
(lambda () (anaconda-mode-jsonrpc command callback))))
(defun anaconda-mode-jsonrpc (command callback)
"Perform JSONRPC call for COMMAND.
Apply CALLBACK to the call result when retrieve it. Remote
COMMAND must expect four arguments: python buffer content, line
number position, column number position and file path."
(let ((url-request-method "POST")
(url-request-data (anaconda-mode-jsonrpc-request command)))
(url-retrieve
(format "http://%s:%s" anaconda-mode-localhost-address (anaconda-mode-port))
(anaconda-mode-create-response-handler callback)
nil
t)))
(defun anaconda-mode-jsonrpc-request (command)
"Prepare JSON encoded buffer data for COMMAND call."
(encode-coding-string (json-encode (anaconda-mode-jsonrpc-request-data command)) 'utf-8))
(defun anaconda-mode-jsonrpc-request-data (command)
"Prepare buffer data for COMMAND call."
`((jsonrpc . "2.0")
(id . 1)
(method . ,command)
(params . ((source . ,(buffer-substring-no-properties (point-min) (point-max)))
(line . ,(line-number-at-pos (point)))
(column . ,(- (point) (line-beginning-position)))
(path . ,(when (buffer-file-name)
(pythonic-python-readable-file-name (buffer-file-name))))))))
(defun anaconda-mode-create-response-handler (callback)
"Create server response handler based on CALLBACK function."
(let ((anaconda-mode-request-point (point))
(anaconda-mode-request-buffer (current-buffer))
(anaconda-mode-request-window (selected-window))
(anaconda-mode-request-tick (buffer-chars-modified-tick)))
(lambda (status)
(let ((http-buffer (current-buffer)))
(unwind-protect
(if (or (not (equal anaconda-mode-request-window (selected-window)))
(with-current-buffer (window-buffer anaconda-mode-request-window)
(or (not (equal anaconda-mode-request-buffer (current-buffer)))
(not (equal anaconda-mode-request-point (point)))
(not (equal anaconda-mode-request-tick (buffer-chars-modified-tick))))))
nil
(search-forward-regexp "\r?\n\r?\n" nil t)
(let ((response (condition-case nil
(json-read)
((json-readtable-error json-end-of-file end-of-file)
(let ((response (concat (format "# status: %s\n# point: %s\n" status (point))
(buffer-string))))
(with-current-buffer (get-buffer-create anaconda-mode-response-buffer)
(erase-buffer)
(insert response)
(goto-char (point-min)))
nil)))))
(if (null response)
(message "Cannot read anaconda-mode server response")
(if (assoc 'error response)
(let* ((error-structure (cdr (assoc 'error response)))
(error-message (cdr (assoc 'message error-structure)))
(error-data (cdr (assoc 'data error-structure)))
(error-template (concat (if error-data "%s: %s" "%s")
" - see " anaconda-mode-process-buffer
" for more information.")))
(apply 'message error-template (delq nil (list error-message error-data))))
(with-current-buffer anaconda-mode-request-buffer
(let ((result (cdr (assoc 'result response))))
;; Terminate `apply' call with empty list so response
;; will be treated as single argument.
(apply callback result nil)))))))
(kill-buffer http-buffer))))))
;;; Code completion.
(defun anaconda-mode-complete ()
"Request completion candidates."
(interactive)
(unless (python-syntax-comment-or-string-p)
(anaconda-mode-call "complete" 'anaconda-mode-complete-callback)))
(defun anaconda-mode-complete-callback (result)
"Start interactive completion on RESULT receiving."
(let* ((bounds (bounds-of-thing-at-point 'symbol))
(start (or (car bounds) (point)))
(stop (or (cdr bounds) (point)))
(collection (anaconda-mode-complete-extract-names result))
(completion-extra-properties '(:annotation-function anaconda-mode-complete-annotation)))
(completion-in-region start stop collection)))
(defun anaconda-mode-complete-extract-names (result)
"Extract completion names from `anaconda-mode' RESULT."
(--map (let ((name (aref it 0))
(type (aref it 1)))
(put-text-property 0 1 'type type name)
name)
result))
(defun anaconda-mode-complete-annotation (candidate)
"Get annotation for CANDIDATE."
(--when-let (get-text-property 0 'type candidate)
(concat " <" it ">")))
;;; View documentation.
(defun anaconda-mode-show-doc ()
"Show documentation for context at point."
(interactive)
(anaconda-mode-call "show_doc" 'anaconda-mode-show-doc-callback))
(defun anaconda-mode-show-doc-callback (result)
"Process view doc RESULT."
(if (> (length result) 0)
(if (and anaconda-mode-use-posframe-show-doc
(require 'posframe nil 'noerror)
(posframe-workable-p))
(anaconda-mode-documentation-posframe-view result)
(pop-to-buffer (anaconda-mode-documentation-view result) t))
(message "No documentation available")))
(defun anaconda-mode-documentation-view (result)
"Show documentation view for rpc RESULT, and return buffer."
(let ((buf (get-buffer-create "*Anaconda*")))
(with-current-buffer buf
(view-mode -1)
(erase-buffer)
(mapc
(lambda (it)
(insert (propertize (aref it 0) 'face 'bold))
(insert "\n")
(insert (s-trim-right (aref it 1)))
(insert "\n\n"))
result)
(view-mode 1)
(goto-char (point-min))
buf)))
(defun anaconda-mode-documentation-posframe-view (result)
"Show documentation view in posframe for rpc RESULT."
(with-current-buffer (get-buffer-create anaconda-mode-doc-frame-name)
(erase-buffer)
(mapc
(lambda (it)
(insert (propertize (aref it 0) 'face 'bold))
(insert "\n")
(insert (s-trim-left (aref it 1)))
(insert "\n\n"))
result))
(posframe-show anaconda-mode-doc-frame-name
:position (point)
:internal-border-width 10
:background-color anaconda-mode-doc-frame-background
:foreground-color anaconda-mode-doc-frame-foreground)
(add-hook 'post-command-hook 'anaconda-mode-hide-frame)
(setq anaconda-mode-frame-last-point (point))
(setq anaconda-mode-frame-last-scroll-offset (window-start)))
(defun anaconda-mode-hide-frame ()
"Hide posframe when window scroll or move point."
(ignore-errors
(when (get-buffer anaconda-mode-doc-frame-name)
(unless (and (equal (point) anaconda-mode-frame-last-point)
(equal (window-start) anaconda-mode-frame-last-scroll-offset))
(posframe-hide anaconda-mode-doc-frame-name)
(remove-hook 'post-command-hook 'anaconda-mode-hide-frame)))))
;;; Find definitions.
(defun anaconda-mode-find-definitions ()
"Find definitions for thing at point."
(interactive)
(anaconda-mode-call
"goto_definitions"
(lambda (result)
(anaconda-mode-show-xrefs result nil "No definitions found"))))
(defun anaconda-mode-find-definitions-other-window ()
"Find definitions for thing at point."
(interactive)
(anaconda-mode-call
"goto_definitions"
(lambda (result)
(anaconda-mode-show-xrefs result 'window "No definitions found"))))
(defun anaconda-mode-find-definitions-other-frame ()
"Find definitions for thing at point."
(interactive)
(anaconda-mode-call
"goto_definitions"
(lambda (result)
(anaconda-mode-show-xrefs result 'frame "No definitions found"))))
;;; Find assignments.
(defun anaconda-mode-find-assignments ()
"Find assignments for thing at point."
(interactive)
(anaconda-mode-call
"goto_assignments"
(lambda (result)
(anaconda-mode-show-xrefs result nil "No assignments found"))))
(defun anaconda-mode-find-assignments-other-window ()
"Find assignments for thing at point."
(interactive)
(anaconda-mode-call
"goto_assignments"
(lambda (result)
(anaconda-mode-show-xrefs result 'window "No assignments found"))))
(defun anaconda-mode-find-assignments-other-frame ()
"Find assignments for thing at point."
(interactive)
(anaconda-mode-call
"goto_assignments"
(lambda (result)
(anaconda-mode-show-xrefs result 'frame "No assignments found"))))
;;; Find references.
(defun anaconda-mode-find-references ()
"Find references for thing at point."
(interactive)
(anaconda-mode-call
"usages"
(lambda (result)
(anaconda-mode-show-xrefs result nil "No references found"))))
(defun anaconda-mode-find-references-other-window ()
"Find references for thing at point."
(interactive)
(anaconda-mode-call
"usages"
(lambda (result)
(anaconda-mode-show-xrefs result 'window "No references found"))))
(defun anaconda-mode-find-references-other-frame ()
"Find references for thing at point."
(interactive)
(anaconda-mode-call
"usages"
(lambda (result)
(anaconda-mode-show-xrefs result 'frame "No references found"))))
;;; Xref.
(defun anaconda-mode-show-xrefs (result display-action error-message)
"Show xref from RESULT using DISPLAY-ACTION.
Show ERROR-MESSAGE if result is empty."
(if result
(if (stringp result)
(message result)
(let ((xrefs (anaconda-mode-make-xrefs result)))
(if (not (cdr xrefs))
(progn
(xref-push-marker-stack)
(funcall (if (fboundp 'xref-pop-to-location)
'xref-pop-to-location
'xref--pop-to-location)
(cl-first xrefs)
display-action))
(xref--show-xrefs (if (functionp 'xref--create-fetcher)
(lambda (&rest _) xrefs)
xrefs)
display-action))))
(message error-message)))
(defun anaconda-mode-make-xrefs (result)
"Return a list of x-reference candidates created from RESULT."
(--map
(xref-make
(aref it 3)
(xref-make-file-location (pythonic-emacs-readable-file-name (aref it 0)) (aref it 1) (aref it 2)))
result))
;;; Eldoc.
(defun anaconda-mode-eldoc-function ()
"Show eldoc for context at point."
(anaconda-mode-call "eldoc" 'anaconda-mode-eldoc-callback)
;; Don't show response buffer name as ElDoc message.
nil)
(defun anaconda-mode-eldoc-callback (result)
"Display eldoc from server RESULT."
(eldoc-message (anaconda-mode-eldoc-format result)))
(defun anaconda-mode-eldoc-format (result)
"Format eldoc string from RESULT."
(when result
(let ((doc (anaconda-mode-eldoc-format-definition
(aref result 0)
(aref result 1)
(aref result 2))))
(if anaconda-mode-eldoc-as-single-line
(substring doc 0 (min (frame-width) (length doc)))
doc))))
(defun anaconda-mode-eldoc-format-definition (name index params)
"Format function definition from NAME, INDEX and PARAMS."
(when index
(aset params index (propertize (aref params index) 'face 'eldoc-highlight-function-argument)))
(concat (propertize name 'face 'font-lock-function-name-face) "(" (mapconcat 'identity params ", ") ")"))
;;; Anaconda minor mode.
(defvar anaconda-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-M-i") 'anaconda-mode-complete)
(define-key map (kbd "M-.") 'anaconda-mode-find-definitions)
(define-key map (kbd "C-x 4 .") 'anaconda-mode-find-definitions-other-window)
(define-key map (kbd "C-x 5 .") 'anaconda-mode-find-definitions-other-frame)
(define-key map (kbd "M-=") 'anaconda-mode-find-assignments)
(define-key map (kbd "C-x 4 =") 'anaconda-mode-find-assignments-other-window)
(define-key map (kbd "C-x 5 =") 'anaconda-mode-find-assignments-other-frame)
(define-key map (kbd "M-r") 'anaconda-mode-find-references)
(define-key map (kbd "C-x 4 r") 'anaconda-mode-find-references-other-window)
(define-key map (kbd "C-x 5 r") 'anaconda-mode-find-references-other-frame)
(define-key map (kbd "M-,") 'xref-pop-marker-stack)
(define-key map (kbd "M-?") 'anaconda-mode-show-doc)
map)
"Keymap for `anaconda-mode'.")
;;;###autoload
(define-minor-mode anaconda-mode
"Code navigation, documentation lookup and completion for Python.
\\{anaconda-mode-map}"
:lighter anaconda-mode-lighter
:keymap anaconda-mode-map
(setq-local url-http-attempt-keepalives nil))
;;;###autoload
(define-minor-mode anaconda-eldoc-mode
"Toggle echo area display of Python objects at point."
:lighter ""
(if anaconda-eldoc-mode
(turn-on-anaconda-eldoc-mode)
(turn-off-anaconda-eldoc-mode)))
(defun turn-on-anaconda-eldoc-mode ()
"Turn on `anaconda-eldoc-mode'."
(make-local-variable 'eldoc-documentation-function)
(setq-local eldoc-documentation-function 'anaconda-mode-eldoc-function)
(eldoc-mode +1))
(defun turn-off-anaconda-eldoc-mode ()
"Turn off `anaconda-eldoc-mode'."
(kill-local-variable 'eldoc-documentation-function)
(eldoc-mode -1))
(provide 'anaconda-mode)
;;; anaconda-mode.el ends here