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.

334 lines
12 KiB

  1. ;;; ein-notification.el --- Notification widget for Notebook
  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-notification.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-notification.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-notification.el. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;;
  17. ;;; Code:
  18. (require 'eieio)
  19. (require 'ein-notebook)
  20. (require 'ein-core)
  21. (require 'ein-classes)
  22. (require 'ein-events)
  23. ;; Class and variable
  24. (ein:deflocal ein:%notification% nil
  25. "Buffer local variable to hold an instance of `ein:notification'.")
  26. (define-obsolete-variable-alias 'ein:@notification 'ein:%notification% "0.1.2")
  27. (defvar ein:header-line-format '(:eval (ein:header-line)))
  28. (defvar ein:header-line-tab-map (make-sparse-keymap))
  29. (defvar ein:header-line-insert-tab-map (make-sparse-keymap))
  30. (defvar ein:header-line-switch-kernel-map (make-sparse-keymap))
  31. (defvar ein:header-line-tab-help
  32. "\
  33. mouse-1 (left click) : switch to this tab
  34. mouse-3 (right click) : pop to this tab
  35. mouse-2 (middle click) : delete this tab
  36. M-mouse-1/3 (Alt + left/right click): insert new tab to left/right
  37. S-mouse-1/3 (Shift + left/right click): move this tab to left/right"
  38. "Help message.")
  39. ;; Note: can't put this below of `ein:notification-setup'...
  40. (cl-defmethod ein:notification-status-set ((ns ein:notification-status) status)
  41. (let* ((message (cdr (assoc status (slot-value ns 's2m)))))
  42. (setf (slot-value ns 'status) status)
  43. (setf (slot-value ns 'message) (substitute-command-keys message))
  44. (force-mode-line-update t)))
  45. (cl-defmethod ein:notification-bind-events ((notification ein:notification) events)
  46. "Bind a callback to events of the event handler EVENTS which
  47. just set the status (= event-type):
  48. (ein:notification-status-set NS EVENT-TYPE)
  49. where NS is `:kernel' or `:notebook' slot of NOTIFICATION."
  50. (cl-loop for ns in (list (slot-value notification 'kernel)
  51. (slot-value notification 'notebook))
  52. for statuses = (mapcar #'car (slot-value ns 's2m))
  53. do (cl-loop for st in statuses
  54. do (ein:events-on events
  55. st ; = event-type
  56. #'ein:notification--callback
  57. (cons ns st))))
  58. (ein:events-on events
  59. 'notebook_checkpoint_created.Notebook
  60. #'ein:notification--fadeout-callback
  61. (list (slot-value notification 'notebook)
  62. "Checkpoint created."
  63. 'notebook_checkpoint_created.Notebook
  64. nil))
  65. (ein:events-on events
  66. 'notebook_saved.Notebook
  67. #'ein:notification--fadeout-callback
  68. (list (slot-value notification 'notebook)
  69. "Notebook is saved"
  70. 'notebook_saved.Notebook
  71. nil))
  72. (ein:events-on events
  73. 'execution_count.Kernel
  74. #'ein:notification--set-execution-count
  75. notification))
  76. (defun ein:notification--callback (packed data)
  77. (let ((ns (car packed))
  78. (status (cdr packed)))
  79. (ein:notification-status-set ns status)))
  80. (defun ein:notification--set-execution-count (notification count)
  81. (oset notification :execution-count count))
  82. (defun ein:notification--fadeout-callback (packed data)
  83. ;; FIXME: I can simplify this.
  84. ;; Do not pass around message, for exmaple.
  85. (let ((ns (nth 0 packed))
  86. (message (nth 1 packed))
  87. (status (nth 2 packed))
  88. (next (nth 3 packed)))
  89. (oset ns :status status)
  90. (oset ns :message message)
  91. (apply #'run-at-time
  92. 1 nil
  93. (lambda (ns message status next)
  94. (when (equal (slot-value ns 'status) status)
  95. (ein:notification-status-set ns next)
  96. ;; (ein:with-live-buffer (slot-value ns :buffer)
  97. ;; (force-mode-line-update))
  98. ))
  99. packed)))
  100. (defun ein:notification-setup (buffer events &rest tab-slots)
  101. "Setup a new notification widget in the BUFFER.
  102. This function saves the new notification widget instance in the
  103. local variable of the BUFFER.
  104. Rest of the arguments are for TABs in `header-line'.
  105. GET-LIST : function
  106. Return a list of worksheets.
  107. GET-CURRENT : function
  108. Return the current worksheet.
  109. GET-NAME : function
  110. Return a name of the worksheet given as its argument.
  111. GET-BUFFER : function
  112. Get a buffer of given worksheet. Render it if needed.
  113. DELETE : function
  114. Remove a given worksheet.
  115. INSERT-PREV / INSERT-NEXT : function
  116. Insert new worksheet before/after the specified worksheet.
  117. MOVE-PREV / MOVE-NEXT : function
  118. Switch this worksheet to the previous/next one.
  119. \(fn buffer events &key get-list get-current get-name get-buffer delete \
  120. insert-prev insert-next move-prev move-next)"
  121. (with-current-buffer buffer
  122. (setq ein:%notification%
  123. (make-instance 'ein:notification
  124. :buffer buffer))
  125. (setq header-line-format ein:header-line-format)
  126. (ein:notification-bind-events ein:%notification% events)
  127. (oset ein:%notification% :tab
  128. (apply #'make-instance 'ein:notification-tab tab-slots))
  129. ein:%notification%))
  130. ;;; Tabs
  131. (defface ein:notification-tab-selected
  132. '((t :inherit (header-line match) :underline t))
  133. "Face for headline selected tab."
  134. :group 'ein)
  135. (defface ein:notification-tab-normal
  136. '((t :inherit (header-line) :underline t :height 0.8))
  137. "Face for headline selected tab."
  138. :group 'ein)
  139. (cl-defmethod ein:notification-tab-create-line ((tab ein:notification-tab))
  140. (let ((list (funcall (slot-value tab 'get-list)))
  141. (current (funcall (slot-value tab 'get-current)))
  142. (get-name (slot-value tab 'get-name)))
  143. (ein:join-str
  144. " "
  145. (append
  146. (cl-loop for i from 1
  147. for elem in list
  148. if (eq elem current)
  149. collect (propertize
  150. (or (ein:and-let* ((name (funcall get-name elem)))
  151. (format "/%d: %s\\" i name))
  152. (format "/%d\\" i))
  153. 'ein:worksheet elem
  154. 'keymap ein:header-line-tab-map
  155. 'help-echo ein:header-line-tab-help
  156. 'mouse-face 'highlight
  157. 'face 'ein:notification-tab-selected)
  158. else
  159. collect (propertize
  160. (format "/%d\\" i)
  161. 'ein:worksheet elem
  162. 'keymap ein:header-line-tab-map
  163. 'help-echo ein:header-line-tab-help
  164. 'mouse-face 'highlight
  165. 'face 'ein:notification-tab-normal))
  166. (list
  167. (propertize "[+]"
  168. 'keymap ein:header-line-insert-tab-map
  169. 'help-echo "Click (mouse-1) to insert a new tab."
  170. 'mouse-face 'highlight
  171. 'face 'ein:notification-tab-normal)
  172. (propertize (ein:aif (and ein:%notebook% (ein:$notebook-kernelspec ein:%notebook%))
  173. (format "|%s|" (ein:$kernelspec-name it))
  174. "|unknown: please click and select a kernel|")
  175. 'keymap ein:header-line-switch-kernel-map
  176. 'help-echo "Click (mouse-1) to change the running kernel."
  177. 'mouse-face 'highlight
  178. 'face 'ein:notification-tab-normal))))))
  179. ;;; Header line
  180. (let ((map ein:header-line-tab-map))
  181. (define-key map [header-line M-mouse-1] 'ein:header-line-insert-prev-tab)
  182. (define-key map [header-line M-mouse-3] 'ein:header-line-insert-next-tab)
  183. (define-key map [header-line S-mouse-1] 'ein:header-line-move-prev-tab)
  184. (define-key map [header-line S-mouse-3] 'ein:header-line-move-next-tab)
  185. (define-key map [header-line mouse-1] 'ein:header-line-switch-to-this-tab)
  186. (define-key map [header-line mouse-2] 'ein:header-line-delete-this-tab)
  187. (define-key map [header-line mouse-3] 'ein:header-line-pop-to-this-tab))
  188. (define-key ein:header-line-insert-tab-map
  189. [header-line mouse-1] 'ein:header-line-insert-new-tab)
  190. (define-key ein:header-line-switch-kernel-map
  191. [header-line mouse-1] 'ein:header-line-switch-kernel)
  192. (defmacro ein:with-destructuring-bind-key-event (key-event &rest body)
  193. (declare (debug (form &rest form))
  194. (indent 1))
  195. ;; See: (info "(elisp) Click Events")
  196. `(cl-destructuring-bind
  197. (event-type
  198. (window pos-or-area (x . y) timestamp
  199. object text-pos (col . row)
  200. image (dx . dy) (width . height)))
  201. ,key-event
  202. ,@body))
  203. (defun ein:header-line-select-window (key-event)
  204. (ein:with-destructuring-bind-key-event key-event (select-window window)))
  205. (defun ein:header-line-key-event-get-worksheet (key-event)
  206. (ein:with-destructuring-bind-key-event key-event
  207. (get-char-property (cdr object) 'ein:worksheet (car object))))
  208. (defun ein:header-line-key-event-get-buffer (key-event)
  209. (funcall (slot-value (slot-value ein:%notification% 'tab) 'get-buffer)
  210. (ein:header-line-key-event-get-worksheet key-event)))
  211. (defun ein:header-line-switch-to-this-tab (key-event)
  212. (interactive "e")
  213. (ein:header-line-select-window key-event)
  214. (switch-to-buffer (ein:header-line-key-event-get-buffer key-event)))
  215. (defun ein:header-line-pop-to-this-tab (key-event)
  216. (interactive "e")
  217. (ein:header-line-select-window key-event)
  218. (pop-to-buffer (ein:header-line-key-event-get-buffer key-event)))
  219. (defun ein:header-line-do-slot-function (key-event slot)
  220. "Call SLOT function on worksheet instance fetched from KEY-EVENT."
  221. (ein:header-line-select-window key-event)
  222. (funcall (slot-value (slot-value ein:%notification% 'tab) slot)
  223. (ein:header-line-key-event-get-worksheet key-event)))
  224. (defmacro ein:header-line-define-mouse-commands (&rest name-slot-list)
  225. `(progn
  226. ,@(cl-loop for (name slot) on name-slot-list by 'cddr
  227. collect
  228. `(defun ,name (key-event)
  229. ,(format "Run slot %s
  230. Generated by `ein:header-line-define-mouse-commands'" slot)
  231. (interactive "e")
  232. (ein:header-line-do-slot-function key-event ,slot)))))
  233. (ein:header-line-define-mouse-commands
  234. ein:header-line-delete-this-tab :delete
  235. ein:header-line-insert-prev-tab :insert-prev
  236. ein:header-line-insert-next-tab :insert-next
  237. ein:header-line-move-prev-tab :move-prev
  238. ein:header-line-move-next-tab :move-next
  239. )
  240. (defun ein:header-line-insert-new-tab (key-event)
  241. "Insert new tab."
  242. (interactive "e")
  243. (ein:header-line-select-window key-event)
  244. (let ((notification (slot-value ein:%notification% 'tab)))
  245. (funcall (slot-value notification 'insert-next)
  246. (car (last (funcall (slot-value notification 'get-list)))))))
  247. (defun ein:header-line-switch-kernel (key-event)
  248. (interactive "e")
  249. (let* ((notebook (or (ein:get-notebook)
  250. (ein:completing-read
  251. "Select notebook: "
  252. (ein:notebook-opened-buffer-names))))
  253. (kernel-name (ein:completing-read
  254. "Select kernel: "
  255. (ein:list-available-kernels (ein:$notebook-url-or-port notebook)))))
  256. (ein:notebook-switch-kernel notebook kernel-name)))
  257. (defun ein:header-line ()
  258. (format
  259. "IP[%s]: %s"
  260. (slot-value ein:%notification% 'execution-count)
  261. (ein:join-str
  262. " | "
  263. (delete nil
  264. (list (slot-value (slot-value ein:%notification% 'notebook) 'message)
  265. (slot-value (slot-value ein:%notification% 'kernel) 'message)
  266. (ein:notification-tab-create-line
  267. (slot-value ein:%notification% 'tab)))))))
  268. (defun ein:header-line-setup-maybe ()
  269. "Setup `header-line-format' for mumamo.
  270. As `header-line-format' is buffer local variable, it must be set
  271. for each chunk when in
  272. See also `ein:ac-setup-maybe'."
  273. (and (ein:eval-if-bound 'ein:notebook-mode)
  274. (ein:eval-if-bound 'mumamo-multi-major-mode)
  275. (setq header-line-format ein:header-line-format)))
  276. (add-hook 'after-change-major-mode-hook 'ein:header-line-setup-maybe)
  277. (provide 'ein-notification)
  278. ;;; ein-notification.el ends here