ein-core.el 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. ;;; ein-core.el --- EIN core
  2. ;; Copyright (C) 2012 Takafumi Arakaki
  3. ;; Author: Takafumi Arakaki <aka.tkf at gmail.com>
  4. ;; This file is NOT part of GNU Emacs.
  5. ;; ein-core.el is free software: you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; ein-core.el is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with ein-core.el.
  15. ;; If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;;
  18. ;;; Code:
  19. (eval-when-compile (require 'cl))
  20. ;; Optional dependency on tramp:
  21. (declare-function tramp-make-tramp-file-name "tramp")
  22. (declare-function tramp-file-name-localname "tramp")
  23. (declare-function tramp-dissect-file-name "tramp")
  24. (require 'ein) ; get autoloaded functions into namespace
  25. (require 'ein-utils)
  26. (defgroup ein nil
  27. "IPython notebook client in Emacs"
  28. :group 'applications
  29. :prefix "ein:")
  30. (defvar ein:version "0.2.1alpha2"
  31. "Version number for Emacs IPython Notebook (EIN).")
  32. ;;; Configuration
  33. (defcustom ein:url-or-port '(8888)
  34. "List of default url-or-port values.
  35. This will be used for completion. So put your IPython servers.
  36. You can connect to servers not in this list \(but you will need
  37. to type every time)."
  38. :type '(repeat (choice (integer :tag "Port number" 8888)
  39. (string :tag "URL" "http://127.0.0.1:8888")))
  40. :group 'ein)
  41. (defcustom ein:default-url-or-port nil
  42. "Default URL or port. This should be your main IPython
  43. Notebook server."
  44. :type '(choice (integer :tag "Port number" 8888)
  45. (string :tag "URL" "http://127.0.0.1:8888")
  46. (const :tag "First value of `ein:url-or-port'" nil))
  47. :group 'ein)
  48. (defcustom ein:filename-translations nil
  49. "Convert file paths between Emacs and Python process.
  50. This value can take these form:
  51. alist
  52. Its key specifies URL-OR-PORT and value must be a list of two
  53. functions: (TO-PYTHON FROM-PYTHON). Key (URL-OR-PORT) can be
  54. string (URL), integer (port), or `default' (symbol). The
  55. value of `default' is used when other key does not much.
  56. function
  57. Called with an argument URL-OR-PORT (integer or string).
  58. This function must return a list of two functions:
  59. (TO-PYTHON FROM-PYTHON).
  60. Here, the functions TO-PYTHON and FROM-PYTHON are defined as:
  61. TO-PYTHON
  62. A function which converts a file name (returned by
  63. `buffer-file-name') to the one Python understands.
  64. FROM-PYTHON
  65. A function which converts a file path returned by
  66. Python process to the one Emacs understands.
  67. Use `ein:tramp-create-filename-translator' to easily generate the
  68. pair of TO-PYTHON and FROM-PYTHON."
  69. ;; I've got the idea from `slime-filename-translations'.
  70. :type '(choice
  71. (alist :tag "Translations mapping"
  72. :key-type (choice :tag "URL or PORT"
  73. (string :tag "URL" "http://127.0.0.1:8888")
  74. (integer :tag "PORT" 8888)
  75. (const default))
  76. :value-type (list (function :tag "TO-PYTHON")
  77. (function :tag "FROM-PYTHON")))
  78. (function :tag "Translations getter"))
  79. :group 'ein)
  80. ;;; Constants
  81. (defvar ein:source-dir (file-name-directory load-file-name)
  82. "Directory in which ``ein*.el`` locate.")
  83. ;;; Configuration getter
  84. (defun ein:default-url-or-port ()
  85. (or ein:default-url-or-port (car ein:url-or-port) 8888))
  86. (defun ein:version ()
  87. "Return a string containing `ein:version' and git revision if
  88. the source is in git repository."
  89. (ein:aif (when (ein:git-root-p
  90. (concat (file-name-as-directory ein:source-dir) ".."))
  91. (let ((default-directory ein:source-dir))
  92. (ein:git-revision-dirty)))
  93. (concat ein:version "." it)
  94. ein:version))
  95. ;;; File name translation (tramp support)
  96. ;; Probably it's better to define `ein:filename-translations-get' as
  97. ;; an EIEIO method so that I don't have to re-define functions such as
  98. ;; `ein:kernel-filename-to-python' and `ein:kernel-filename-from-python'.
  99. (defun ein:filename-translations-get (url-or-port)
  100. (ein:choose-setting 'ein:filename-translations url-or-port))
  101. (defun ein:filename-to-python (url-or-port filename)
  102. (ein:aif (car (ein:filename-translations-get url-or-port))
  103. (funcall it filename)
  104. filename))
  105. (defun ein:filename-from-python (url-or-port filename)
  106. (ein:aif (cadr (ein:filename-translations-get url-or-port))
  107. (funcall it filename)
  108. filename))
  109. (defun ein:make-tramp-file-name (username remote-host python-filename)
  110. "Old (with multi-hops) tramp compatibility function.
  111. Adapted from `slime-make-tramp-file-name'."
  112. (if (boundp 'tramp-multi-methods)
  113. (tramp-make-tramp-file-name nil nil
  114. username
  115. remote-host
  116. python-filename)
  117. (tramp-make-tramp-file-name nil
  118. username
  119. remote-host
  120. python-filename)))
  121. (defun ein:tramp-create-filename-translator (remote-host &optional username)
  122. "Generate a pair of TO-PYTHON and FROM-PYTHON for
  123. `ein:filename-translations'.
  124. Usage::
  125. (setq ein:filename-translations
  126. `((8888
  127. . ,(ein:tramp-create-filename-translator \"MY-HOSTNAME\"))))
  128. ;; Equivalently:
  129. (setq ein:filename-translations
  130. (lambda (url-or-port)
  131. (when (equal url-or-port 8888)
  132. (ein:tramp-create-filename-translator \"MY-HOSTNAME\"))))
  133. This setting assumes that the IPython server which can be
  134. connected using the port 8888 in localhost is actually running in
  135. the host named MY-HOSTNAME.
  136. Adapted from `slime-create-filename-translator'."
  137. (require 'tramp)
  138. (lexical-let ((remote-host remote-host)
  139. (username (or username (user-login-name))))
  140. (list (lambda (emacs-filename)
  141. (tramp-file-name-localname
  142. (tramp-dissect-file-name emacs-filename)))
  143. (lambda (python-filename)
  144. (ein:make-tramp-file-name username remote-host python-filename)))))
  145. ;;; Generic getter
  146. (defun ein:generic-getter (func-list)
  147. "Internal function for generic getter functions (`ein:get-*').
  148. FUNC-LIST is a list of function which takes no argument and
  149. return what is desired or nil. Each function in FUNC-LIST is
  150. called one by one and the first non-nil result will be used. The
  151. function is not called when it is not bound. So, it is safe to
  152. give functions defined in lazy-loaded sub-modules.
  153. This is something similar to dispatching in generic function such
  154. as `defgeneric' in EIEIO, but it takes no argument. Actual
  155. implementation is chosen based on context (buffer, point, etc.).
  156. This helps writing generic commands which requires same object
  157. but can operate in different contexts."
  158. (loop for func in func-list
  159. if (and (functionp func) (funcall func))
  160. return it))
  161. (defun ein:get-url-or-port ()
  162. (ein:generic-getter '(ein:get-url-or-port--notebooklist
  163. ein:get-url-or-port--notebook
  164. ein:get-url-or-port--worksheet
  165. ein:get-url-or-port--shared-output
  166. ein:get-url-or-port--connect)))
  167. (defun ein:get-notebook ()
  168. (ein:generic-getter '(ein:get-notebook--notebook
  169. ;; ein:get-notebook--shared-output
  170. ein:get-notebook--connect)))
  171. (defun ein:get-notebook-or-error ()
  172. (or (ein:get-notebook)
  173. (error "No notebook related to the current buffer.")))
  174. (defun ein:get-kernel ()
  175. (ein:generic-getter '(ein:get-kernel--notebook
  176. ein:get-kernel--worksheet
  177. ein:get-kernel--shared-output
  178. ein:get-kernel--connect)))
  179. (defun ein:get-kernel-or-error ()
  180. (or (ein:get-kernel)
  181. (error "No kernel related to the current buffer.")))
  182. (defun ein:get-cell-at-point ()
  183. (ein:generic-getter '(ein:get-cell-at-point--worksheet
  184. ein:get-cell-at-point--shared-output)))
  185. (defun ein:get-traceback-data ()
  186. (ein:generic-getter '(ein:get-traceback-data--worksheet
  187. ein:get-traceback-data--shared-output
  188. ein:get-traceback-data--connect)))
  189. ;;; Emacs utilities
  190. (defun ein:byte-compile-ein ()
  191. "Byte compile EIN files."
  192. (interactive)
  193. (let* ((files (directory-files ein:source-dir 'full "^ein-.*\\.el$"))
  194. (errors (ein:filter
  195. 'identity
  196. (mapcar (lambda (f) (unless (byte-compile-file f) f))
  197. files))))
  198. (ein:aif errors
  199. (error "Got %s errors while compiling these files: %s"
  200. (length errors)
  201. (ein:join-str " " (mapcar #'file-name-nondirectory it))))
  202. (message "Compiled %s files" (length files))))
  203. (provide 'ein-core)
  204. ;;; ein-core.el ends here