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.

390 lines
15 KiB

  1. ;;; polymode-compat.el --- Various compatibility fixes for other packages -*- lexical-binding: t -*-
  2. ;;
  3. ;; Author: Vitalie Spinu
  4. ;; Maintainer: Vitalie Spinu
  5. ;; Copyright (C) 2013-2019, Vitalie Spinu
  6. ;; Version: 0.1
  7. ;; URL: https://github.com/polymode/polymode
  8. ;; Keywords: emacs
  9. ;;
  10. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  11. ;;
  12. ;; This file is *NOT* part of GNU Emacs.
  13. ;;
  14. ;; This program is free software; you can redistribute it and/or
  15. ;; modify it under the terms of the GNU General Public License as
  16. ;; published by the Free Software Foundation; either version 3, or
  17. ;; (at your option) any later version.
  18. ;;
  19. ;; This program is distributed in the hope that it will be useful,
  20. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  22. ;; General Public License for more details.
  23. ;;
  24. ;; You should have received a copy of the GNU General Public License
  25. ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
  26. ;;
  27. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  28. ;;
  29. ;;; Commentary:
  30. ;;
  31. ;;
  32. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  33. ;;
  34. ;;; Code:
  35. (require 'polymode-core)
  36. (require 'advice nil t)
  37. (defgroup polymode-compat nil
  38. "Polymode compatibility settings."
  39. :group 'polymode)
  40. ;;; emacs 25 compat
  41. (unless (fboundp 'assoc-delete-all)
  42. (defun assoc-delete-all (key alist &optional test)
  43. "Delete from ALIST all elements whose car is KEY.
  44. Compare keys with TEST. Defaults to `equal'.
  45. Return the modified alist.
  46. Elements of ALIST that are not conses are ignored."
  47. (unless test (setq test #'equal))
  48. (while (and (consp (car alist))
  49. (funcall test (caar alist) key))
  50. (setq alist (cdr alist)))
  51. (let ((tail alist) tail-cdr)
  52. (while (setq tail-cdr (cdr tail))
  53. (if (and (consp (car tail-cdr))
  54. (funcall test (caar tail-cdr) key))
  55. (setcdr tail (cdr tail-cdr))
  56. (setq tail tail-cdr))))
  57. alist)
  58. (defun assq-delete-all (key alist)
  59. "Delete from ALIST all elements whose car is `eq' to KEY.
  60. Return the modified alist.
  61. Elements of ALIST that are not conses are ignored."
  62. (assoc-delete-all key alist #'eq)))
  63. ;;; Various Wrappers for Around Advice
  64. (defvar *span* nil)
  65. ;; advice doesn't provide named symbols. So we need to define specialized
  66. ;; wrappers for some key functions (unfinished)
  67. (defmacro pm-define-wrapp-protected (fun)
  68. "Declare protected function with the name fun--pm-wrapped.
  69. Return new name (symbol). FUN is an unquoted name of a function."
  70. (let* ((fun-name (symbol-name fun))
  71. (new-fun (intern (format "%s--pm-wrapped" fun-name)))
  72. (new-doc (format " Error Protected function created with `pm-define-protected-wrapp'.\n\n%s"
  73. (or (documentation fun) ""))))
  74. `(progn
  75. (defun ,new-fun (&rest args)
  76. ,new-doc
  77. (condition-case err
  78. (apply ',fun args)
  79. (error (message "(%s %s): %s"
  80. ,fun-name
  81. (mapconcat (lambda (x) (format "%s" x)) args " ")
  82. (error-message-string err)))))
  83. ',new-fun)))
  84. (defun pm-apply-protected (fun args)
  85. (when fun
  86. (condition-case-unless-debug err
  87. (apply fun args)
  88. (error (message "(%s %s): %s %s"
  89. (if (symbolp fun)
  90. (symbol-name fun)
  91. "anonymous")
  92. (mapconcat (lambda (x) (format "%s" x)) args " ")
  93. (error-message-string err)
  94. ;; (or (and (symbolp fun) "")
  95. ;; (replace-regexp-in-string "\n" "" (format "[%s]" fun)))
  96. "[M-x pm-debug-mode RET for more info]")
  97. nil))))
  98. (defun pm-override-output-position (orig-fun &rest args)
  99. "Restrict returned value of ORIG-FUN to fall into the current span.
  100. *span* in `pm-map-over-spans` has precedence over span at point.
  101. ARGS are passed to ORIG-FUN."
  102. (if (and polymode-mode pm/polymode)
  103. (let ((range (or (pm-span-to-range *span*)
  104. (pm-innermost-range)))
  105. (pos (pm-apply-protected orig-fun args)))
  106. (and pos
  107. (min (max pos (car range))
  108. (cdr range))))
  109. (apply orig-fun args)))
  110. (defun pm-override-output-cons (orig-fun &rest args)
  111. "Restrict returned (beg . end) of ORIG-FUN to fall into the current span.
  112. *span* in `pm-map-over-spans` has precedence over span at point.
  113. This will break badly if (point) is not inside expected range.
  114. ARGS are passed to ORIG-FUN."
  115. (if (and polymode-mode pm/polymode)
  116. (let ((range (or (pm-span-to-range *span*)
  117. (pm-innermost-range)))
  118. (be (pm-apply-protected orig-fun args)))
  119. (let ((out (and be
  120. (cons (and (car be)
  121. (min (max (car be) (car range))
  122. (cdr range)))
  123. (and (cdr be)
  124. (max (min (cdr be) (cdr range))
  125. (car range)))))))
  126. out))
  127. (apply orig-fun args)))
  128. (defun pm-narrowed-override-output-cons (orig-fun &rest args)
  129. "Restrict returned (beg . end) of ORIG-FUN to fall into the current span.
  130. Run ORIG-FUN with buffer narrowed to span. *span* in
  131. `pm-map-over-spans` has precedence over span at point. ARGS are
  132. passed to ORIG-FUN."
  133. (if (and polymode-mode pm/polymode)
  134. (let ((*span* (or *span* (pm-innermost-span))))
  135. (pm-with-narrowed-to-span *span*
  136. (apply #'pm-override-output-cons orig-fun args)))
  137. (apply orig-fun args)))
  138. (defun pm-substitute-beg-end (orig-fun beg end &rest args)
  139. "Execute ORIG-FUN with first BEG and END arguments limited to current span.
  140. *span* in `pm-map-over-spans` has precedence over span at point.
  141. ARGS are passed to ORIG-FUN."
  142. (if (and polymode-mode pm/polymode)
  143. (let* ((pos (if (and (<= (point) end) (>= (point) beg))
  144. (point)
  145. end))
  146. (range (or (pm-span-to-range *span*)
  147. (pm-innermost-range pos)))
  148. (new-beg (max beg (car range)))
  149. (new-end (min end (cdr range))))
  150. (pm-apply-protected orig-fun (append (list new-beg new-end) args)))
  151. (apply orig-fun beg end args)))
  152. (defun pm-execute-narrowed-to-span (orig-fun &rest args)
  153. "Execute ORIG-FUN narrowed to the current span.
  154. *span* in `pm-map-over-spans` has precedence over span at point.
  155. ARGS are passed to ORIG-FUN."
  156. (if (and polymode-mode pm/polymode)
  157. (pm-with-narrowed-to-span *span*
  158. (pm-apply-protected orig-fun args))
  159. (apply orig-fun args)))
  160. ;;; Flyspel
  161. (defun pm--flyspel-dont-highlight-in-chunkmodes (beg end _poss)
  162. (or (car (get-text-property beg :pm-span))
  163. (car (get-text-property end :pm-span))))
  164. ;;; C/C++/Java
  165. (pm-around-advice 'c-before-context-fl-expand-region #'pm-override-output-cons)
  166. ;; (advice-remove 'c-before-context-fl-expand-region #'pm-override-output-cons)
  167. (pm-around-advice 'c-state-semi-safe-place #'pm-override-output-position)
  168. ;; (advice-remove 'c-state-semi-safe-place #'pm-override-output-position)
  169. ;; c-font-lock-fontify-region calls it directly
  170. ;; (pm-around-advice 'font-lock-default-fontify-region #'pm-substitute-beg-end)
  171. (pm-around-advice 'c-determine-limit #'pm-execute-narrowed-to-span)
  172. ;;; Python
  173. (declare-function pm--first-line-indent "polymode-methods")
  174. (defun pm--python-dont-indent-to-0 (fun)
  175. "Fix indent FUN not to cycle to 0 indentation."
  176. (if (and polymode-mode pm/type)
  177. (let ((last-command (unless (eq (pm--first-line-indent) (current-indentation))
  178. last-command)))
  179. (funcall fun))
  180. (funcall fun)))
  181. (pm-around-advice 'python-indent-line-function #'pm--python-dont-indent-to-0)
  182. ;;; Core Font Lock
  183. (defvar font-lock-beg)
  184. (defvar font-lock-end)
  185. (defun pm-check-for-real-change-in-extend-multiline (fun)
  186. "Protect FUN from inf-looping at ‘point-max’.
  187. FUN is `font-lock-extend-region-multiline'. Propagate only real
  188. changes."
  189. ;; fixme: report this ASAP!
  190. (let ((obeg font-lock-beg)
  191. (oend font-lock-end)
  192. (change (funcall fun)))
  193. (and change
  194. (not (eq obeg font-lock-beg))
  195. (not (eq oend font-lock-end)))))
  196. (pm-around-advice #'font-lock-extend-region-multiline
  197. #'pm-check-for-real-change-in-extend-multiline)
  198. ;;; Editing
  199. ;; (pm-around-advice 'fill-paragraph #'pm-execute-narrowed-to-span)
  200. ;; (advice-remove 'fill-paragraph #'pm-execute-narrowed-to-span)
  201. ;; Synchronization of points does not work always as expected because some low
  202. ;; level functions move indirect buffers' points when operate in the base
  203. ;; buffer. See comment in `polymode-with-current-base-buffer'.
  204. ;; (defun polymode-with-save-excursion (orig-fun &rest args)
  205. ;; "Execute ORIG-FUN surrounded with `save-excursion'.
  206. ;; This function is intended to be used in advises of functions
  207. ;; which modify the buffer in the background and thus trigger
  208. ;; `pm-switch-to-buffer' on next post-command hook in a wrong place.
  209. ;; ARGS are passed to ORIG-FUN."
  210. ;; (if polymode-mode
  211. ;; (save-excursion
  212. ;; (apply orig-fun args))
  213. ;; (apply orig-fun args)))
  214. ;;
  215. ;; `save-buffer` misbehaves because after each replacement modification hooks
  216. ;; are triggered and poly buffer is switched in unpredictable fashion (#93).
  217. ;; This happens because basic-save-buffer uses save-buffer but not
  218. ;; save-excursion. Thus if base and indirect buffer don't have same point, at
  219. ;; the end of the function inner buffer will have the point from the base
  220. ;; buffer. Can be reproduced with (add-hook 'before-save-hook
  221. ;; 'delete-trailing-whitespace nil t) in the base buffer.
  222. ;;
  223. ;; (pm-around-advice 'basic-save-buffer #'polymode-with-save-excursion)
  224. ;; (advice-remove 'basic-save-buffer #'polymode-with-save-excursion)
  225. ;; Query replace were probably misbehaving due to unsaved match data (#92). The
  226. ;; following is probably not necessary. (pm-around-advice 'perform-replace
  227. ;; 'pm-execute-inhibit-modification-hooks)
  228. ;; No longer needed. See comment at pm-switch-to-buffer.
  229. ;; (defun polymode-newline-remove-hook-in-orig-buffer (fn &rest args)
  230. ;; "`newline' temporary sets `post-self-insert-hook' and removes it in wrong buffer.
  231. ;; This ARGS are passed to `newline'."
  232. ;; (if polymode-mode
  233. ;; (let* ((cbuf (current-buffer))
  234. ;; (old-hook (buffer-local-value 'post-self-insert-hook cbuf)))
  235. ;; (prog1 (apply fn args)
  236. ;; (unless (eq cbuf (current-buffer))
  237. ;; (unless (eq old-hook (buffer-local-value 'post-self-insert-hook cbuf))
  238. ;; (with-current-buffer cbuf
  239. ;; (if old-hook
  240. ;; (setq post-self-insert-hook old-hook)
  241. ;; (kill-local-variable 'post-self-insert-hook)))))))
  242. ;; (apply fn args)))
  243. ;; (pm-around-advice 'newline #'polymode-newline-remove-hook-in-orig-buffer)
  244. ;;; DESKTOP SAVE #194 #240
  245. ;; NB: desktop-save will not save indirect buffer.
  246. ;; For base buffer, if it's hidden as per #34, we will save it unhide by removing left whitespaces.
  247. (defun polymode-fix-desktop-buffer-info (fn buffer)
  248. "Unhide poly-mode base buffer which is hidden as per #34.
  249. This is done by modifying `uniquify-buffer-base-name' to `pm--core-buffer-name'."
  250. (with-current-buffer buffer
  251. (let ((out (funcall fn buffer)))
  252. (when (and polymode-mode
  253. (not (buffer-base-buffer))
  254. (not (car out)))
  255. (setf (car out) pm--core-buffer-name))
  256. out)))
  257. (declare-function desktop-buffer-info "desktop")
  258. (with-eval-after-load "desktop"
  259. (advice-add #'desktop-buffer-info :around #'polymode-fix-desktop-buffer-info))
  260. (defun polymode-fix-desktop-save-buffer-p (_ bufname &rest _args)
  261. "Dont save polymode buffers which are indirect buffers."
  262. (with-current-buffer bufname
  263. (not (and polymode-mode
  264. (buffer-base-buffer)))))
  265. (declare-function desktop-save-buffer-p "desktop")
  266. (with-eval-after-load "desktop"
  267. (advice-add #'desktop-save-buffer-p :before-while #'polymode-fix-desktop-save-buffer-p))
  268. ;;; MATLAB #199
  269. ;; matlab-mode is an old non-standard mode which doesn't trigger
  270. ;; `after-change-major-mode-hook`. As a result polymode cannot detect that
  271. ;; font-lock-mode is on and sets the `poly-lock-allow-fontification` to nil.
  272. ;; Explicitly trigger font-lock as a workaround.
  273. (add-hook 'matlab-mode-hook (lambda () (font-lock-mode t)))
  274. ;;; Undo Tree (#230)
  275. ;; Not clear why this fix works, or even why the problem occurs.
  276. (declare-function make-undo-tree "undo-tree")
  277. (defvar buffer-undo-tree)
  278. (defun polymode-init-undo-tree-maybe ()
  279. (when (and (boundp 'undo-tree-mode)
  280. undo-tree-mode
  281. (null buffer-undo-tree))
  282. (setq buffer-undo-tree (make-undo-tree))))
  283. (eval-after-load 'undo-tree
  284. '(add-hook 'polymode-init-inner-hook 'polymode-init-undo-tree-maybe))
  285. ;;; EVIL
  286. (declare-function evil-change-state "evil-core")
  287. (defun polymode-switch-buffer-keep-evil-state-maybe (old-buffer new-buffer)
  288. (when (and (boundp 'evil-state)
  289. evil-state)
  290. (let ((old-state (buffer-local-value 'evil-state old-buffer))
  291. (new-state (buffer-local-value 'evil-state new-buffer)))
  292. (unless (eq old-state new-state)
  293. (with-current-buffer new-buffer
  294. (evil-change-state old-state))))))
  295. (eval-after-load 'evil-core
  296. '(add-hook 'polymode-after-switch-buffer-hook 'polymode-switch-buffer-keep-evil-state-maybe))
  297. ;;; HL line
  298. (defvar hl-line-mode)
  299. (defvar global-hl-line-mode)
  300. (declare-function hl-line-unhighlight "hl-line")
  301. (declare-function global-hl-line-unhighlight "hl-line")
  302. (add-to-list 'polymode-move-these-minor-modes-from-old-buffer 'hl-line-mode)
  303. (defun polymode-switch-buffer-hl-unhighlight (old-buffer _new-buffer)
  304. (with-current-buffer old-buffer
  305. ;; We are moving hl-line-mode already
  306. (when hl-line-mode
  307. (hl-line-unhighlight))
  308. (when global-hl-line-mode
  309. (global-hl-line-unhighlight))))
  310. (eval-after-load 'hl-line
  311. '(add-hook 'polymode-after-switch-buffer-hook 'polymode-switch-buffer-hl-unhighlight))
  312. ;;; YAS
  313. (with-eval-after-load "yasnippet"
  314. (add-hook 'yas-before-expand-snippet-hook #'polymode-disable-post-command)
  315. (add-hook 'yas-after-exit-snippet-hook #'polymode-enable-post-command))
  316. (provide 'polymode-compat)
  317. ;;; Multiple cursors
  318. (defvar mc--executing-command-for-fake-cursor)
  319. (defun polymode-disable-post-command-with-multiple-cursors (orig-fun &rest args)
  320. (unless mc--executing-command-for-fake-cursor
  321. (polymode-disable-post-command)
  322. (apply orig-fun args)
  323. (polymode-enable-post-command)))
  324. (with-no-warnings
  325. (with-eval-after-load "multiple-cursors-core"
  326. (advice-add #'mc/execute-this-command-for-all-cursors :around
  327. #'polymode-disable-post-command-with-multiple-cursors)))
  328. ;;; polymode-compat.el ends here