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.

355 lines
15 KiB

  1. (require 'polymode)
  2. (require 'ein-cell)
  3. (require 'jit-lock)
  4. (declare-function polymode-inhibit-during-initialization "polymode-core")
  5. (defcustom ein:polymode nil
  6. "When enabled ein will use polymode to provide multi-major mode
  7. support in a notebook buffer, otherwise ein's custom and outdated
  8. multi-major mode support will be used. Emacs must be restarted
  9. after changing this setting!"
  10. :type 'boolean
  11. :set (lambda (symbol value)
  12. (set-default symbol value)
  13. (when value
  14. (if (featurep 'poly-ein)
  15. (poly-ein--decorate-functions)
  16. (with-eval-after-load 'poly-ein
  17. (poly-ein--decorate-functions)))))
  18. :group 'ein)
  19. (defmacro poly-ein--remove-hook (label functions)
  20. "Remove any hooks saying LABEL from FUNCTIONS"
  21. `(mapc (lambda (x) (when (cl-search ,label (symbol-name x))
  22. (remove-hook (quote ,functions) x t)))
  23. ,functions))
  24. (defsubst poly-ein--neuter-markdown-mode ()
  25. "Consolidate fragility here."
  26. (when (eq major-mode 'markdown-mode)
  27. (poly-ein--remove-hook "markdown" after-change-functions)
  28. (poly-ein--remove-hook "markdown" jit-lock-after-change-extend-region-functions)
  29. (poly-ein--remove-hook "markdown" window-configuration-change-hook)
  30. (poly-ein--remove-hook "markdown" syntax-propertize-extend-region-functions)))
  31. (defun poly-ein--narrow-to-inner (modifier f &rest args)
  32. (if (or pm-initialization-in-progress (not poly-ein-mode))
  33. (apply f args)
  34. (save-restriction
  35. (widen)
  36. (let ((range (pm-innermost-range
  37. (or (when (car args) (max (point-min) (funcall modifier (car args))))
  38. (point)))))
  39. (narrow-to-region (car range) (cdr range))
  40. (apply f args)))))
  41. (defun poly-ein--decorate-functions ()
  42. "Affect global definitions of ppss and jit-lock rather intrusively."
  43. (advice-remove 'jit-lock-mode #'poly-lock-no-jit-lock-in-polymode-buffers)
  44. (advice-remove 'font-lock-fontify-region #'polymode-inhibit-during-initialization)
  45. (advice-remove 'font-lock-fontify-buffer #'polymode-inhibit-during-initialization)
  46. (advice-remove 'font-lock-ensure #'polymode-inhibit-during-initialization)
  47. (add-hook 'after-change-major-mode-hook #'poly-ein--neuter-markdown-mode t)
  48. ;; https://github.com/millejoh/emacs-ipython-notebook/issues/537
  49. ;; alternatively, filter-args on ad-should-compile but then we'd have to
  50. ;; match on function name
  51. (custom-set-default 'ad-default-compilation-action 'never)
  52. (add-function
  53. :before-until (symbol-function 'pm-select-buffer)
  54. (lambda (span &optional visibly)
  55. (prog1 poly-ein-mode
  56. (when poly-ein-mode
  57. (let ((src-buf (current-buffer))
  58. (dest-buf (pm-span-buffer span)))
  59. ;; (font-lock-flush)
  60. (poly-ein--set-buffer src-buf dest-buf visibly))))))
  61. (fmakunbound 'poly-lock-mode)
  62. (defalias 'poly-lock-mode (symbol-function (default-value 'font-lock-function)))
  63. (add-function
  64. :before-until (symbol-function 'syntax-propertize)
  65. (lambda (pos)
  66. (prog1 poly-ein-mode
  67. (when (and poly-ein-mode (< syntax-propertize--done pos))
  68. (save-excursion
  69. (with-silent-modifications
  70. (let ((parse-sexp-lookup-properties t)
  71. (start (point-min))
  72. (end (point-max)))
  73. ;; (dolist (fun syntax-propertize-extend-region-functions)
  74. ;; (ein:and-let* ((new (funcall fun start end)))
  75. ;; (setq start (min start (car new)))
  76. ;; (setq end (max end (cdr new)))))
  77. (setq syntax-propertize--done end)
  78. (remove-text-properties start end
  79. '(syntax-table nil syntax-multiline nil))
  80. ;; avoid recursion if syntax-propertize-function calls me (syntax-propertize)
  81. (when syntax-propertize-function
  82. (let ((syntax-propertize--done most-positive-fixnum))
  83. (funcall syntax-propertize-function start end))))))))))
  84. (add-function
  85. :around (symbol-function 'syntax-propertize)
  86. (apply-partially #'poly-ein--narrow-to-inner #'1-))
  87. (add-function
  88. :around (symbol-function 'syntax-ppss)
  89. (apply-partially #'poly-ein--narrow-to-inner #'1-))
  90. (add-function
  91. :around (symbol-function 'pm--mode-setup)
  92. (lambda (f &rest args)
  93. ;; global-font-lock-mode will call an after-change-mode-hook
  94. ;; that calls font-lock-initial-fontify, which fontifies the entire buffer!
  95. (cl-letf (((symbol-function 'global-font-lock-mode-enable-in-buffers) #'ignore))
  96. (apply f args))))
  97. (add-function
  98. :around (symbol-function 'jit-lock-mode)
  99. (lambda (f &rest args)
  100. ;; Override jit-lock.el.gz deliberately skipping indirect buffers
  101. (cl-letf (((symbol-function 'buffer-base-buffer) #'ignore)) (apply f args))))
  102. ;; :before-until before :filter-args (reversed order when executed)
  103. (add-function :before-until (symbol-function 'jit-lock-refontify)
  104. #'poly-ein--unrelated-span)
  105. (add-function :before-until (symbol-function 'jit-lock-fontify-now)
  106. #'poly-ein--unrelated-span)
  107. (add-function :filter-args (symbol-function 'jit-lock-refontify)
  108. #'poly-ein--span-start-end)
  109. (add-function :filter-args (symbol-function 'jit-lock-fontify-now)
  110. #'poly-ein--span-start-end)
  111. (add-function :filter-args (symbol-function 'font-lock-flush)
  112. #'poly-ein--span-start-end)
  113. (add-function :filter-args (symbol-function 'jit-lock-after-change)
  114. #'poly-ein--span-start-end)
  115. (if (fboundp 'markdown-unfontify-region-wiki-links)
  116. (fset 'markdown-unfontify-region-wiki-links #'ignore)
  117. (with-eval-after-load "markdown-mode"
  118. (fset 'markdown-unfontify-region-wiki-links #'ignore)))
  119. (add-function :before-until
  120. (symbol-function 'pm--synchronize-points)
  121. (lambda (&rest args) poly-ein-mode)))
  122. (defmacro poly-ein-base (&rest body)
  123. "Copy the undo accounting to the base buffer and run BODY in it."
  124. `(let ((base-buffer (pm-base-buffer))
  125. (derived-buffer (current-buffer))
  126. (pm-allow-post-command-hook nil)
  127. (pm-initialization-in-progress t))
  128. (poly-ein--set-buffer derived-buffer base-buffer)
  129. (condition-case err
  130. (prog1 (progn ,@body)
  131. (poly-ein--set-buffer base-buffer derived-buffer))
  132. (error (message "%s" (error-message-string err))
  133. (poly-ein--set-buffer base-buffer derived-buffer)))))
  134. (defclass pm-inner-overlay-chunkmode (pm-inner-auto-chunkmode)
  135. ()
  136. "Inner chunkmode delimited by cell overlays.")
  137. (cl-defmethod pm-get-span ((cm pm-inner-overlay-chunkmode) &optional pos)
  138. "Return a list of the form (TYPE POS-START POS-END RESULT-CM).
  139. TYPE can be 'body, nil."
  140. (poly-ein-base
  141. (setq pos (or pos (point)))
  142. ;; Assume: ein:worksheet-get-current-cell always returns non-nil
  143. (let ((result-cm cm)
  144. (span `(nil ,(point-min) ,(point-min)))
  145. (cell (ein:worksheet-get-current-cell :pos pos :noerror nil)))
  146. ;; Change :mode if necessary
  147. (ein:and-let* ((lang
  148. (condition-case err
  149. (ein:$kernelspec-language
  150. (ein:$notebook-kernelspec
  151. (ein:get-notebook)))
  152. (error (message "%s: defaulting language to python"
  153. (error-message-string err))
  154. "python")))
  155. (mode
  156. (pm-get-mode-symbol-from-name
  157. (cond ((ein:codecell-p cell) lang)
  158. ((ein:markdowncell-p cell) "markdown")
  159. (t "fundamental"))))
  160. ((not (equal mode (ein:oref-safe cm :mode)))))
  161. (when (eq mode 'poly-fallback-mode)
  162. (ein:display-warning
  163. (format "pm:get-span: no major mode for kernelspec language '%s'" lang)))
  164. (setq result-cm
  165. (cl-loop for ocm in (eieio-oref pm/polymode '-auto-innermodes)
  166. when (equal mode (ein:oref-safe ocm :mode))
  167. return ocm
  168. finally return (let ((new-mode (clone cm :mode mode)))
  169. (object-add-to-list pm/polymode '-auto-innermodes
  170. new-mode)
  171. new-mode))))
  172. ;; Span is a zebra pattern of "body" (within input cell) and "nil"
  173. ;; (outside input cell). Decide boundaries of span and return it.
  174. (let ((rel (poly-ein--relative-to-input pos cell)))
  175. (cond ((zerop rel)
  176. (setq span `(body
  177. ,(ein:cell-input-pos-min cell)
  178. ,(1+ (ein:cell-input-pos-max cell)))))
  179. ((< rel 0)
  180. (setq span `(nil
  181. ,(or (ein:aand (ein:cell-prev cell)
  182. (1+ (ein:cell-input-pos-max it)))
  183. (point-min))
  184. ,(ein:cell-input-pos-min cell))))
  185. (t
  186. (setq span `(nil
  187. ,(1+ (ein:cell-input-pos-max cell))
  188. ,(or (ein:aand (ein:cell-next cell)
  189. (ein:cell-input-pos-min it))
  190. (point-max)))))))
  191. (append span (list result-cm)))))
  192. (defun poly-ein-fontify-buffer (buffer)
  193. "Called from `ein:notebook--worksheet-render'"
  194. (with-current-buffer buffer
  195. (save-excursion
  196. (pm-map-over-spans
  197. (lambda (span)
  198. (with-current-buffer (pm-span-buffer span)
  199. (cl-assert (eq font-lock-function 'poly-lock-mode))
  200. (jit-lock-function (nth 1 span))))))))
  201. (defun poly-ein--relative-to-input (pos cell)
  202. "Return -1 if POS before input, 1 if after input, 0 if within"
  203. (let* ((input-pos-min (ein:cell-input-pos-min cell))
  204. (input-pos-max (ein:cell-input-pos-max cell)))
  205. (cond ((< pos input-pos-min) -1)
  206. ((> pos input-pos-max) 1)
  207. (t 0))))
  208. (defvar jit-lock-start)
  209. (defvar jit-lock-end)
  210. (defun poly-ein--hem-jit-lock (start end _old-len)
  211. (when (and poly-ein-mode (not pm-initialization-in-progress))
  212. (let ((range (pm-innermost-range (or start (point)))))
  213. (setq jit-lock-start (max jit-lock-start (car range)))
  214. (setq jit-lock-end (min jit-lock-end (cdr range))))))
  215. (defun poly-ein-undo-damage (type)
  216. (remove-hook 'after-change-functions 'polymode-flush-syntax-ppss-cache t)
  217. (add-hook 'jit-lock-after-change-extend-region-functions #'poly-ein--hem-jit-lock t t)
  218. (setq jit-lock-contextually nil) ; else recenter font-lock-fontify-keywords-region
  219. (setq jit-lock-context-unfontify-pos nil)
  220. (if (eq type 'host)
  221. (setq syntax-propertize-function nil)
  222. (ein:aif pm--syntax-propertize-function-original
  223. (progn
  224. (setq syntax-propertize-function it)
  225. (add-function :before-until (local 'syntax-propertize-function)
  226. #'poly-ein--unrelated-span)
  227. (add-function :filter-args (local 'syntax-propertize-function)
  228. #'poly-ein--span-start-end)))))
  229. (defun poly-ein-init-input-cell (_type)
  230. (mapc (lambda (f) (add-to-list 'after-change-functions f))
  231. (buffer-local-value 'after-change-functions (pm-base-buffer)))
  232. (poly-ein-copy-state (pm-base-buffer) (current-buffer))
  233. (ein:notebook-mode))
  234. (defcustom pm-host/ein
  235. (pm-host-chunkmode :name "ein"
  236. :init-functions '(poly-ein-undo-damage))
  237. "EIN host chunkmode"
  238. :group 'poly-hostmodes
  239. :type 'object)
  240. (defcustom pm-inner/ein-input-cell
  241. (pm-inner-overlay-chunkmode :name "ein-input-cell"
  242. :init-functions '(poly-ein-undo-damage poly-ein-init-input-cell))
  243. "EIN input cell."
  244. :group 'poly-innermodes
  245. :type 'object)
  246. (defcustom poly-ein-mode-hook nil
  247. "Hook for poly-ein-mode"
  248. :type 'hook :group 'poly-ein)
  249. ;;;###autoload (autoload 'poly-ein-mode "poly-ein")
  250. (define-polymode poly-ein-mode
  251. :lighter " PM-ipynb"
  252. :hostmode 'pm-host/ein
  253. :innermodes '(pm-inner/ein-input-cell))
  254. (defun poly-ein-copy-state (src-buf dest-buf)
  255. "Consolidate fragility here."
  256. (unless (eq src-buf dest-buf)
  257. (with-current-buffer dest-buf (remove-overlays nil nil 'face 'ein:cell-input-area))
  258. (mapc (lambda (ol)
  259. (if (eq 'ein:cell-input-area (overlay-get ol 'face))
  260. (move-overlay (copy-overlay ol)
  261. (overlay-start ol) (overlay-end ol)
  262. dest-buf)))
  263. (with-current-buffer src-buf (overlays-in (point-min) (point-max))))
  264. (pm--move-vars (append ein:local-variables '(header-line-format buffer-undo-list))
  265. src-buf dest-buf)))
  266. (defsubst poly-ein--set-buffer (src-buf dest-buf &optional switch)
  267. (when (and (not (eq src-buf dest-buf))
  268. (buffer-live-p src-buf)
  269. (buffer-live-p dest-buf))
  270. (cl-destructuring-bind (point window-start region-begin pos-visible _)
  271. (with-current-buffer src-buf (list (point) (window-start)
  272. (and switch (region-active-p) (mark))
  273. (pos-visible-in-window-p)
  274. (when switch (deactivate-mark))))
  275. (poly-ein-copy-state src-buf dest-buf)
  276. (if switch
  277. (switch-to-buffer dest-buf)
  278. (set-buffer dest-buf))
  279. (when region-begin
  280. (setq deactivate-mark nil) ;; someone is setting this, I don't know who
  281. (push-mark region-begin t t))
  282. (goto-char point)
  283. (setq syntax-propertize--done (point-min))
  284. (when switch
  285. (when pos-visible
  286. (set-window-start (get-buffer-window) window-start))
  287. (bury-buffer-internal src-buf)
  288. (set-window-prev-buffers
  289. nil
  290. (assq-delete-all src-buf (window-prev-buffers nil)))
  291. (run-hook-with-args 'polymode-switch-buffer-hook src-buf dest-buf)
  292. (pm--run-hooks pm/polymode :switch-buffer-functions src-buf dest-buf)
  293. (pm--run-hooks pm/chunkmode :switch-buffer-functions src-buf dest-buf)))))
  294. (defsubst poly-ein--span-start-end (args)
  295. (if (or pm-initialization-in-progress (not poly-ein-mode))
  296. args
  297. (let* ((span-start (car args))
  298. (span-end (cadr args))
  299. (range (pm-innermost-range (or span-start (point)))))
  300. (setq span-start (max (or span-start (car range)) (car range)))
  301. (setq span-end (min (or span-end (cdr range)) (cdr range)))
  302. (append (list span-start span-end) (cddr args)))))
  303. (defsubst poly-ein--unrelated-span (&optional beg end)
  304. (or pm-initialization-in-progress
  305. (and poly-ein-mode
  306. (let* ((span (pm-innermost-span (or beg (point))))
  307. (span-mode (eieio-oref (nth 3 span) :mode)))
  308. ;; only fontify type 'body (the other type is nil)
  309. (or (null (nth 0 span)) (not (eq major-mode span-mode)))))))
  310. (make-variable-buffer-local 'parse-sexp-lookup-properties)
  311. (provide 'poly-ein)