emacs/layers.personal/misctools/my-polymode/local/polymode/polymode-compat.el

252 lines
9.7 KiB
EmacsLisp
Raw Normal View History

2018-04-07 10:54:04 +08:00
;;; COMPATIBILITY and FIXES
(require 'polymode-core)
(require 'advice nil t)
(defgroup polymode-compat nil
"Polymode compatibility settings."
:group 'polymode)
;;; Various Wrappers for Around Advice
(defvar *span* nil)
;; advice doesn't provide named symbols. So we need to define specialized
;; wrappers for some key functions (unfinished)
(defmacro pm-define-wrapp-protected (fun)
"Declare protected function with the name fun--pm-wrapped.
Return new name (symbol). FUN is an unquoted name of a function."
(let* ((fun-name (symbol-name fun))
(new-fun (intern (format "%s--pm-wrapped" fun-name)))
(new-doc (format " Error Protected function created with `pm-define-protected-wrapp'.\n\n%s"
(or (documentation fun) ""))))
`(progn
(defun ,new-fun (&rest args)
,new-doc
(condition-case err
(apply ',fun args)
(error (message "(%s %s): %s"
,fun-name
(mapconcat (lambda (x) (format "%s" x)) args " ")
(error-message-string err)))))
',new-fun)))
(defun pm-apply-protected (fun args)
(when fun
(condition-case-unless-debug err
(apply fun args)
(error (message "(%s %s): %s %s"
(if (symbolp fun)
(symbol-name fun)
"anonymous")
(mapconcat (lambda (x) (format "%s" x)) args " ")
(error-message-string err)
;; (or (and (symbolp fun) "")
;; (replace-regexp-in-string "\n" "" (format "[%s]" fun)))
"[M-x pm-debug-mode RET for more info]"
)
(when pm-debug-mode
(backtrace))
nil))))
(defun pm-override-output-position (orig-fun &rest args)
"Restrict returned value of ORIG-FUN to fall into the current span.
*span* in `pm-map-over-spans` has precedence over span at point.'"
(if (and polymode-mode pm/polymode)
(let ((range (or (pm-span-to-range *span*)
(pm-get-innermost-range)))
(pos (pm-apply-protected orig-fun args)))
(and pos
(min (max pos (car range))
(cdr range))))
(apply orig-fun args)))
(defun pm-override-output-cons (orig-fun &rest args)
"Restrict returned (beg . end) of ORIG-FUN to fall into the current span.
*span* in `pm-map-over-spans` has precedence over span at point.'"
(if (and polymode-mode pm/polymode)
(let ((range (or (pm-span-to-range *span*)
(pm-get-innermost-range)))
(be (pm-apply-protected orig-fun args)))
(and be
(cons (min (max (car be) (car range))
(cdr range))
(max (min (cdr be) (cdr range))
(car range)))))
(apply orig-fun args)))
(defun pm-substitute-beg-end (orig-fun beg end &rest args)
"Execute orig-fun with first two arguments limited to current span.
*span* in `pm-map-over-spans` has precedence over span at point."
(if (and polymode-mode pm/polymode)
(let* ((pos (if (and (<= (point) end) (>= (point) beg))
(point)
end))
(range (or (pm-span-to-range *span*)
(pm-get-innermost-range pos)))
(new-beg (max beg (car range)))
(new-end (min end (cdr range))))
(pm-apply-protected orig-fun (append (list new-beg new-end) args)))
(apply orig-fun beg end args)))
(defun pm-execute-narrowed-to-span (orig-fun &rest args)
"Execute ORIG-FUN narrowed to the current span.
*span* in `pm-map-over-spans` has precedence over span at point."
(if (and polymode-mode pm/polymode)
(pm-with-narrowed-to-span *span*
(pm-apply-protected orig-fun args))
(apply orig-fun args)))
(defun pm-execute-with-no-polymode-hooks (orig-fun &rest args)
"Execute ORIG-FUN without allowing polymode core hooks.
That is, bind `pm-allow-post-command-hook' and
`pm-allow-after-change-hook' to nil. *span* in
`pm-map-over-spans' has precedence over span at point."
;; this advice is nowhere used yet
(if (and polymode-mode pm/polymode)
(let ((pm-allow-post-command-hook t)
(pm-allow-after-change-hook t))
;; This advice might be useful when functions can switch buffers to work
;; inside the base buffer (like basic-save-buffer does). Thus, we sync
;; points first.
(pm--synchronize-points)
;; save-excursion might be also often necessary
(apply orig-fun args))
(apply orig-fun args)))
(defun pm-execute-with-save-excursion (orig-fun &rest args)
"Execute ORIG-FUN within save-excursion."
;; This advice is required when other functions switch buffers to work inside
;; base buffer and don't restore the point. For some not very clear reason
;; this seem to be necessary for save-buffer which saves buffer but not point.
(if (and polymode-mode pm/polymode)
(progn
(pm--synchronize-points)
(save-excursion
(apply orig-fun args)))
(apply orig-fun args)))
(defun pm-around-advice (fun advice)
"Apply around ADVICE to FUN.
Check for if new advice is available and if FUN is a symbol, do
nothing otherwise. If FUN is a list, apply advice to each element
in a list. "
(when (and fun (fboundp 'advice-add))
(cond ((listp fun)
(dolist (el fun) (pm-around-advice el advice)))
((and (symbolp fun)
(not (advice-member-p advice fun)))
(advice-add fun :around advice)))))
;;; Syntax
(defun pm-execute-syntax-propertize-narrowed-to-span (orig-fun pos)
"Execute `syntax-propertize' narrowed to the current span.
Don't throw errors, but give relevant messages instead."
;; in emacs 25.1 internal--syntax-propertize is called from C. We
;; cannot advice it, but we can check for its argument. Very hackish
;; but I don't see another way besides re-defining that function.
(if (and polymode-mode pm/polymode)
(condition-case err
(save-excursion
(when (< syntax-propertize--done pos)
(pm-map-over-spans
(lambda ()
(when (< syntax-propertize--done pos)
(pm-with-narrowed-to-span *span*
(funcall orig-fun (min pos (point-max)))
(let ((new--done syntax-propertize--done))
(dolist (buff (oref pm/polymode -buffers))
(with-current-buffer buff
(setq-local syntax-propertize--done new--done)))))))
syntax-propertize--done pos)))
(error (message "(syntax-propertize %s): %s [M-x pm-debug-info RET to see backtrace]"
pos (error-message-string err))
(and pm-debug-mode
(backtrace))))
(funcall orig-fun pos)))
(pm-around-advice 'syntax-propertize 'pm-execute-syntax-propertize-narrowed-to-span)
;;; Flyspel
(defun pm--flyspel-dont-highlight-in-chunkmodes (beg end poss)
(or (get-text-property beg :pm-span-type)
(get-text-property end :pm-span-type)))
;;; C/C++/Java
(pm-around-advice 'c-before-context-fl-expand-region #'pm-override-output-cons)
(pm-around-advice 'c-state-semi-safe-place #'pm-override-output-position)
;; (advice-remove 'c-state-semi-safe-place #'pm-override-output-position)
;; c-font-lock-fontify-region calls it directly
;; (pm-around-advice 'font-lock-default-fontify-region #'pm-substitute-beg-end)
(pm-around-advice 'c-determine-limit #'pm-execute-narrowed-to-span)
;;; Python
(defun pm--python-dont-indent-to-0 (fun)
"Don't cycle to 0 indentation in polymode chunks."
(if (and polymode-mode pm/type)
(let ((last-command (unless (eq (pm--first-line-indent) (current-indentation))
last-command)))
(funcall fun))
(funcall fun)))
(pm-around-advice 'python-indent-line-function #'pm--python-dont-indent-to-0)
;;; Core Font Lock
(defun pm-check-for-real-change-in-extend-multiline (fun)
"Fix `font-lock-extend-region-multiline' which causes infloops on point-max.
Propagate only real change."
;; fixme: report this ASAP!
(let ((obeg font-lock-beg)
(oend font-lock-end)
(change (funcall fun)))
(and change
(not (eq obeg font-lock-beg))
(not (eq oend font-lock-end)))))
(pm-around-advice 'font-lock-extend-region-multiline #'pm-check-for-real-change-in-extend-multiline)
;;; Editing
(pm-around-advice 'fill-paragraph #'pm-execute-narrowed-to-span)
;; `save-buffer` misbehaves because after each replacement modification hooks
;; are triggered and poly buffer is switched in unpredictable fashion.
;;
;; https://github.com/vspinu/polymode/issues/93 It can be
;; reproduced with (add-hook 'before-save-hook 'delete-trailing-whitespace nil
;; t) in the base buffer.
;;
;; save-excursion is probably not quite right fix for this but it seem to work
(pm-around-advice 'basic-save-buffer #'pm-execute-with-save-excursion)
;; Query replace were probably misbehaving due to unsaved match data.
;; (https://github.com/vspinu/polymode/issues/92) The following is probably not
;; necessary.
;; (pm-around-advice 'perform-replace 'pm-execute-inhibit-modification-hooks)
;;; EVIL
(defun polymode-switch-buffer-keep-evil-state-maybe (old-buffer new-buffer)
(when (and (boundp 'evil-state)
evil-state)
(let ((old-state (buffer-local-value 'evil-state old-buffer))
(new-state (buffer-local-value 'evil-state new-buffer)))
(unless (eq old-state new-state)
(with-current-buffer new-buffer
(evil-change-state old-state))))))
(eval-after-load 'evil-core
'(add-hook 'polymode-switch-buffer-hook 'polymode-switch-buffer-keep-evil-state-maybe))
(provide 'polymode-compat)