Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

186 lines
7.4 KiB

  1. ;;; -*- mode: emacs-lisp; lexical-binding: t -*-
  2. ;;; ein-completer.el --- Completion module
  3. ;; Copyright (C) 2018- Takafumi Arakaki / John Miller
  4. ;; Author: Takafumi Arakaki <aka.tkf at gmail.com> / John Miller <millejoh at mac.com>
  5. ;; This file is NOT part of GNU Emacs.
  6. ;; ein-completer.el is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; ein-completer.el is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with ein-completer.el. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;;
  18. ;;; Code:
  19. (declare-function ac-cursor-on-diable-face-p "auto-complete")
  20. (require 'ein-core)
  21. (require 'ein-log)
  22. (require 'ein-subpackages)
  23. (require 'ein-kernel)
  24. (require 'ein-pytools)
  25. (require 'ein-ac)
  26. (require 'dash)
  27. (make-obsolete-variable 'ein:complete-on-dot nil "0.15.0")
  28. (defun ein:completer-choose ()
  29. (cond
  30. ((eq ein:completion-backend 'ein:use-none-backend) #'ignore)
  31. ((ein:eval-if-bound 'auto-complete-mode) #'ein:completer-finish-completing-ac)
  32. (t #'ein:completer-finish-completing-default)))
  33. (defun ein:completer-beginning (matched-text)
  34. (save-excursion
  35. (re-search-backward (concat matched-text "\\="))))
  36. (defun ein:completer-finish-completing (args content _metadata)
  37. (ein:log 'debug "COMPLETER-FINISH-COMPLETING: content=%S" content)
  38. (let* ((beg (point))
  39. (delta (- (plist-get content :cursor_end)
  40. (plist-get content :cursor_start)))
  41. (matched-text (buffer-substring beg (- beg delta)))
  42. (matches (plist-get content :matches))
  43. (completer (ein:completer-choose)))
  44. (ein:log 'debug "COMPLETER-FINISH-COMPLETING: completer=%s" completer)
  45. (apply completer matched-text matches args)))
  46. (defun ein:completer-finish-completing-default (matched-text matches
  47. &rest _ignore)
  48. (let* ((end (point))
  49. (beg (ein:completer-beginning matched-text))
  50. (word (if (and beg matches)
  51. (ein:completing-read "Complete: " matches
  52. nil nil matched-text))))
  53. (when word
  54. (delete-region beg end)
  55. (insert word))))
  56. (defun ein:completer-complete (kernel callbacks errback)
  57. "Start completion for the code at point.
  58. EXPAND keyword argument is supported by
  59. `ein:completer-finish-completing-ac'. When it is specified,
  60. it overrides `ac-expand-on-auto-complete' when calling
  61. `auto-complete'."
  62. (interactive (list (ein:get-kernel)
  63. (list :complete_reply
  64. (cons #'ein:completer-finish-completing '(:expand nil)))
  65. #'ignore))
  66. (multiple-value-bind (code pos) (ein:get-completion-context (ein:$kernel-api-version kernel))
  67. (ein:log 'debug (format "EIN:COMPLETER-COMPLETE Code block: %s at position :%s" code pos))
  68. (ein:kernel-complete kernel
  69. code ;; (thing-at-point 'line)
  70. pos ;; (current-column)
  71. callbacks errback)))
  72. (defun ein:get-completion-context (api-version)
  73. (cond ((< api-version 5)
  74. (values (thing-at-point 'line) (current-column)))
  75. ((and (ein:get-kernel) (ein:get-cell-at-point))
  76. (let* ((cell (ein:get-cell-at-point))
  77. (code (ein:cell-get-text cell))
  78. (beg (ein:cell-input-pos-min cell)))
  79. (values code (- (point) beg))))
  80. ((ein:get-kernel)
  81. (values (buffer-string) (1- (point))))))
  82. ;;; Retrieving Python Object Info
  83. (defun ein:completions--reset-oinfo-cache (kernel)
  84. (setf (ein:$kernel-oinfo-cache kernel) (make-hash-table :test #'equal)))
  85. (defun ein:dev-clear-oinfo-cache (kernel)
  86. (interactive (list (ein:get-kernel)))
  87. (ein:completions--reset-oinfo-cache kernel))
  88. (defun ein:completions-get-cached (partial oinfo-cache)
  89. (cl-loop for candidate being the hash-keys of oinfo-cache
  90. when (string-prefix-p partial candidate)
  91. collect candidate))
  92. (defun ein:completions--get-oinfo (objs)
  93. (let ((d (deferred:new #'identity))
  94. (kernel (ein:get-kernel)))
  95. (ein:case-equal (ein:kernel-language kernel)
  96. (("python")
  97. (if (ein:kernel-live-p kernel)
  98. (ein:kernel-execute
  99. kernel
  100. (format "__ein_generate_oinfo_data(%s, locals())" objs)
  101. (list
  102. :output `(,(lambda (d* &rest args) (deferred:callback-post d* args)) . ,d)))
  103. (deferred:callback-post d "kernel not live"))))
  104. d))
  105. (defvar ein:oinfo-chunk-size 50)
  106. (defun ein:completions--build-oinfo-cache (objects)
  107. (cl-labels ((object-string (o)
  108. (format "'%s'" (ein:trim o "\\s-\\|\n\\|\\.")))
  109. (to-ostrings (objs)
  110. (s-join ", " (-map #'(lambda (x) (object-string x))
  111. objs)))
  112. (do-completions (ostrings kernel)
  113. (deferred:$
  114. (deferred:next
  115. (lambda ()
  116. (ein:completions--get-oinfo ostrings)))
  117. (deferred:nextc it
  118. (lambda (output)
  119. (if (stringp output)
  120. (ein:display-warning output :error)
  121. (ein:completions--prepare-oinfo output objects kernel)))))))
  122. (if (< (length objects) ein:oinfo-chunk-size)
  123. (do-completions (format "[%s]" (to-ostrings (-non-nil objects))) (ein:get-kernel))
  124. (dolist (chunk (-partition-all ein:oinfo-chunk-size (-non-nil objects)))
  125. (do-completions (format "[%s]" (to-ostrings chunk)) (ein:get-kernel))))))
  126. (defun ein:completions--prepare-oinfo (output objs kernel)
  127. (condition-case err
  128. (cl-destructuring-bind (msg-type content _) output
  129. (ein:case-equal msg-type
  130. (("stream" "display_data" "pyout" "execute_result")
  131. (ein:aif (plist-get content :text)
  132. (let ((all-oinfo (ein:json-read-from-string it)))
  133. (cl-loop for oinfo in all-oinfo
  134. for obj in objs
  135. doing (unless (string= (plist-get oinfo :string_form) "None")
  136. (setf (gethash obj (ein:$kernel-oinfo-cache kernel))
  137. oinfo))))))
  138. (("error" "pyerr")
  139. (ein:log 'verbose "ein:completions--prepare-oinfo: %s"
  140. (plist-get content :traceback)))))
  141. (error
  142. (ein:log 'verbose "ein:completions--prepare-oinfo: [%s]"
  143. (error-message-string err))
  144. (let (eval-expression-print-length eval-expression-print-level)
  145. (prin1 output #'external-debugging-output)))))
  146. ;;; Support for Eldoc
  147. (defun ein:completer--get-eldoc-signature ()
  148. (ein:and-let* ((func (ein:function-at-point))
  149. (kernel (ein:get-kernel)))
  150. (ein:aif (gethash func (ein:$kernel-oinfo-cache kernel))
  151. (ein:kernel-construct-defstring it)
  152. (ein:completions--build-oinfo-cache (list func))
  153. nil)))
  154. (provide 'ein-completer)
  155. ;;; ein-completer.el ends here