mirror of
https://github.com/vale981/emacs-ipython-notebook
synced 2025-03-06 01:21:38 -05:00

The modern Jupyter messaging spec (> 5.0) simplified `complete_request` to allow sending blocks of text as completion context. EIN now takes advantage of this by sending the entire contents of the worksheet cell, when in a notebook buffer, or, when in a non-notebook buffer, the entire contents of the buffer. This allows backends like jedi to do better context-sensitive completion without having to execute code. In general this should improve completion behavior, though may result in an initial performance hit when working in large buffers while EIN builds the oinfo cache. Note that oinfo (i.e., function call signatures) will not be available until code in the buffer/cell is executed.
172 lines
6.5 KiB
EmacsLisp
172 lines
6.5 KiB
EmacsLisp
;;; -*- mode: emacs-lisp; lexical-binding: t -*-
|
|
;;; ein-completer.el --- Completion module
|
|
|
|
;; Copyright (C) 2018- Takafumi Arakaki / John Miller
|
|
|
|
;; Author: Takafumi Arakaki <aka.tkf at gmail.com> / John Miller <millejoh at mac.com>
|
|
|
|
;; This file is NOT part of GNU Emacs.
|
|
|
|
;; ein-completer.el 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.
|
|
|
|
;; ein-completer.el 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 ein-completer.el. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
;;; Commentary:
|
|
|
|
;;
|
|
|
|
;;; Code:
|
|
|
|
(declare-function ac-cursor-on-diable-face-p "auto-complete")
|
|
|
|
(require 'ein-core)
|
|
(require 'ein-log)
|
|
(require 'ein-subpackages)
|
|
(require 'ein-kernel)
|
|
(require 'ein-pytools)
|
|
(require 'ein-ac)
|
|
(require 'dash)
|
|
|
|
(make-obsolete-variable 'ein:complete-on-dot nil "0.15.0")
|
|
|
|
(defun ein:completer-choose ()
|
|
(cond
|
|
((eq ein:completion-backend 'ein:use-none-backend) #'ignore)
|
|
((ein:eval-if-bound 'auto-complete-mode) #'ein:completer-finish-completing-ac)
|
|
(t #'ein:completer-finish-completing-default)))
|
|
|
|
(defun ein:completer-beginning (matched-text)
|
|
(save-excursion
|
|
(re-search-backward (concat matched-text "\\="))))
|
|
|
|
(defun ein:completer-finish-completing (args content _metadata)
|
|
(ein:log 'debug "COMPLETER-FINISH-COMPLETING: content=%S" content)
|
|
(let* ((beg (point))
|
|
(delta (- (plist-get content :cursor_end)
|
|
(plist-get content :cursor_start)))
|
|
(matched-text (buffer-substring beg (- beg delta)))
|
|
(matches (plist-get content :matches))
|
|
(completer (ein:completer-choose)))
|
|
(ein:log 'debug "COMPLETER-FINISH-COMPLETING: completer=%s" completer)
|
|
(apply completer matched-text matches args)))
|
|
|
|
(defun ein:completer-finish-completing-default (matched-text matches
|
|
&rest _ignore)
|
|
(let* ((end (point))
|
|
(beg (ein:completer-beginning matched-text))
|
|
(word (if (and beg matches)
|
|
(ein:completing-read "Complete: " matches
|
|
nil nil matched-text))))
|
|
(when word
|
|
(delete-region beg end)
|
|
(insert word))))
|
|
|
|
(defun ein:completer-complete (kernel callbacks errback)
|
|
"Start completion for the code at point.
|
|
|
|
EXPAND keyword argument is supported by
|
|
`ein:completer-finish-completing-ac'. When it is specified,
|
|
it overrides `ac-expand-on-auto-complete' when calling
|
|
`auto-complete'."
|
|
(interactive (list (ein:get-kernel)
|
|
(list :complete_reply
|
|
(cons #'ein:completer-finish-completing '(:expand nil)))
|
|
#'ignore))
|
|
(multiple-value-bind (code pos) (ein:get-completion-context (ein:$kernel-api-version kernel))
|
|
(ein:log 'debug (format "EIN:COMPLETER-COMPLETE Code block: %s at position :%s" code pos))
|
|
(ein:kernel-complete kernel
|
|
code ;; (thing-at-point 'line)
|
|
pos ;; (current-column)
|
|
callbacks errback)))
|
|
|
|
(defun ein:get-completion-context (api-version)
|
|
(cond ((< api-version 5)
|
|
(values (thing-at-point 'line) (current-column)))
|
|
((and (ein:get-kernel) (ein:get-cell-at-point))
|
|
(let* ((cell (ein:get-cell-at-point))
|
|
(code (ein:cell-get-text cell))
|
|
(beg (ein:cell-input-pos-min cell)))
|
|
(values code (- (point) beg))))
|
|
((ein:get-kernel)
|
|
(values (buffer-string) (1- (point))))))
|
|
|
|
;;; Retrieving Python Object Info
|
|
(defun ein:completions--reset-oinfo-cache (kernel)
|
|
(setf (ein:$kernel-oinfo-cache kernel) (make-hash-table :test #'equal)))
|
|
|
|
(defun ein:dev-clear-oinfo-cache (kernel)
|
|
(interactive (list (ein:get-kernel)))
|
|
(ein:completions--reset-oinfo-cache kernel))
|
|
|
|
(defun ein:completions-get-cached (partial oinfo-cache)
|
|
(cl-loop for candidate being the hash-keys of oinfo-cache
|
|
when (string-prefix-p partial candidate)
|
|
collect candidate))
|
|
|
|
(defun ein:completions--get-oinfo (obj)
|
|
(let ((d (deferred:new #'identity))
|
|
(kernel (ein:get-kernel)))
|
|
(if (ein:kernel-live-p kernel)
|
|
(ein:kernel-execute
|
|
kernel
|
|
(format "__ein_print_object_info_for(__ein_maybe_undefined_object(r\"%s\", locals()))" obj)
|
|
(list
|
|
:output `(,(lambda (d* &rest args) (deferred:callback-post d* args)) . ,d)))
|
|
(deferred:callback-post d "kernel not live"))
|
|
d))
|
|
|
|
(defun ein:completions--build-oinfo-cache (objs)
|
|
(let ((kernel (ein:get-kernel)))
|
|
(dolist (o (-non-nil objs))
|
|
(deferred:$
|
|
(deferred:next
|
|
(lambda ()
|
|
(ein:completions--get-oinfo (ein:trim o "\\s-\\|\n\\|\\."))))
|
|
(deferred:nextc it
|
|
(lambda (output)
|
|
(if (stringp output)
|
|
(ein:display-warning output :error)
|
|
(ein:completions--prepare-oinfo output o kernel))))))))
|
|
|
|
(defun ein:completions--prepare-oinfo (output obj kernel)
|
|
(condition-case err
|
|
(cl-destructuring-bind (msg-type content _) output
|
|
(ein:case-equal msg-type
|
|
(("stream" "display_data" "pyout" "execute_result")
|
|
(ein:aif (plist-get content :text)
|
|
(let ((oinfo (ein:json-read-from-string it)))
|
|
(unless (string= (plist-get oinfo :string_form) "None")
|
|
(setf (gethash obj (ein:$kernel-oinfo-cache kernel))
|
|
oinfo)))))
|
|
(("error" "pyerr")
|
|
(ein:log 'verbose "ein:completions--prepare-oinfo: %s"
|
|
(plist-get content :traceback)))))
|
|
(error
|
|
(ein:log 'verbose "ein:completions--prepare-oinfo: [%s]"
|
|
(error-message-string err))
|
|
(let (eval-expression-print-length eval-expression-print-level)
|
|
(prin1 output #'external-debugging-output))
|
|
(setf (gethash obj (ein:$kernel-oinfo-cache kernel)) :json-false))))
|
|
|
|
;;; Support for Eldoc
|
|
|
|
(defun ein:completer--get-eldoc-signature ()
|
|
(ein:and-let* ((func (ein:function-at-point))
|
|
(kernel (ein:get-kernel)))
|
|
(ein:aif (gethash func (ein:$kernel-oinfo-cache kernel))
|
|
(ein:kernel-construct-defstring it)
|
|
(ein:completions--build-oinfo-cache (list func))
|
|
nil)))
|
|
|
|
(provide 'ein-completer)
|
|
|
|
;;; ein-completer.el ends here
|