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.

557 lines
20 KiB

  1. ;;; polymode-debug.el --- Interactive debugging utilities for polymode -*- lexical-binding: t -*-
  2. ;;
  3. ;; Copyright (C) 2016-2018 Vitalie Spinu
  4. ;; Author: Vitalie Spinu
  5. ;; URL: https://github.com/polymode/polymode
  6. ;;
  7. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  8. ;;
  9. ;; This file is *NOT* part of GNU Emacs.
  10. ;;
  11. ;; This program is free software; you can redistribute it and/or
  12. ;; modify it under the terms of the GNU General Public License as
  13. ;; published by the Free Software Foundation; either version 3, or
  14. ;; (at your option) any later version.
  15. ;;
  16. ;; This program is distributed in the hope that it will be useful,
  17. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. ;; General Public License for more details.
  20. ;;
  21. ;; You should have received a copy of the GNU General Public License
  22. ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
  23. ;;
  24. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  25. ;;
  26. ;;; Commentary:
  27. ;;
  28. ;;; Code:
  29. (require 'polymode-core)
  30. (require 'poly-lock)
  31. (require 'trace)
  32. ;;; MINOR MODE
  33. (defvar pm--underline-overlay
  34. (let ((overlay (make-overlay (point) (point))))
  35. (overlay-put overlay 'face '(:underline (:color "tomato" :style wave)))
  36. overlay)
  37. "Overlay used in function `pm-debug-mode'.")
  38. (defvar pm--highlight-overlay
  39. (let ((overlay (make-overlay (point) (point))))
  40. (overlay-put overlay 'face '(:inverse-video t))
  41. overlay)
  42. "Overlay used by `pm-debug-map-over-spans-and-highlight'.")
  43. (defvar pm-debug-minor-mode-map
  44. (let ((map (make-sparse-keymap)))
  45. (define-key map (kbd "M-n M-i") #'pm-debug-info-on-current-span)
  46. (define-key map (kbd "M-n i") #'pm-debug-info-on-current-span)
  47. (define-key map (kbd "M-n M-p") #'pm-debug-relevant-variables)
  48. (define-key map (kbd "M-n p") #'pm-debug-relevant-variables)
  49. (define-key map (kbd "M-n M-h") #'pm-debug-map-over-spans-and-highlight)
  50. (define-key map (kbd "M-n h") #'pm-debug-map-over-spans-and-highlight)
  51. (define-key map (kbd "M-n M-t t") #'pm-toggle-tracing)
  52. (define-key map (kbd "M-n M-t i") #'pm-debug-toogle-info-message)
  53. (define-key map (kbd "M-n M-t f") #'pm-debug-toggle-fontification)
  54. (define-key map (kbd "M-n M-t p") #'pm-debug-toggle-post-command)
  55. (define-key map (kbd "M-n M-t c") #'pm-debug-toggle-after-change)
  56. (define-key map (kbd "M-n M-t a") #'pm-debug-toggle-all)
  57. (define-key map (kbd "M-n M-t M-t") #'pm-toggle-tracing)
  58. (define-key map (kbd "M-n M-t M-i") #'pm-debug-toogle-info-message)
  59. (define-key map (kbd "M-n M-t M-f") #'pm-debug-toggle-fontification)
  60. (define-key map (kbd "M-n M-t M-p") #'pm-debug-toggle-post-command)
  61. (define-key map (kbd "M-n M-t M-c") #'pm-debug-toggle-after-change)
  62. (define-key map (kbd "M-n M-t M-a") #'pm-debug-toggle-all)
  63. (define-key map (kbd "M-n M-f s") #'pm-debug-fontify-current-span)
  64. (define-key map (kbd "M-n M-f b") #'pm-debug-fontify-current-buffer)
  65. (define-key map (kbd "M-n M-f M-t") #'pm-debug-toggle-fontification)
  66. (define-key map (kbd "M-n M-f M-s") #'pm-debug-fontify-current-span)
  67. (define-key map (kbd "M-n M-f M-b") #'pm-debug-fontify-current-buffer)
  68. map))
  69. ;;;###autoload
  70. (define-minor-mode pm-debug-minor-mode
  71. "Turns on/off useful facilities for debugging polymode.
  72. Key bindings:
  73. \\{pm-debug-minor-mode-map}"
  74. nil
  75. " PMDBG"
  76. :group 'polymode
  77. (if pm-debug-minor-mode
  78. (progn
  79. ;; this is global hook. No need to complicate with local hooks
  80. (add-hook 'post-command-hook 'pm-debug-highlight-current-span))
  81. (delete-overlay pm--underline-overlay)
  82. (delete-overlay pm--highlight-overlay)
  83. (remove-hook 'post-command-hook 'pm-debug-highlight-current-span)))
  84. ;;;###autoload
  85. (defun pm-debug-minor-mode-on ()
  86. ;; activating everywhere (in case font-lock infloops in a polymode buffer )
  87. ;; this doesn't activate in fundamental mode
  88. (unless (eq major-mode 'minibuffer-inactive-mode)
  89. (pm-debug-minor-mode t)))
  90. ;;;###autoload
  91. (define-globalized-minor-mode pm-debug-mode pm-debug-minor-mode pm-debug-minor-mode-on)
  92. ;;; INFO
  93. (cl-defgeneric pm-debug-info (chunkmode))
  94. (cl-defmethod pm-debug-info (chunkmode)
  95. (eieio-object-name chunkmode))
  96. (cl-defmethod pm-debug-info ((chunkmode pm-inner-chunkmode))
  97. (format "%s head-matcher:\"%s\" tail-matcher:\"%s\""
  98. (cl-call-next-method)
  99. (eieio-oref chunkmode 'head-matcher)
  100. (eieio-oref chunkmode 'tail-matcher)))
  101. (cl-defmethod pm-debug-info ((_chunkmode pm-inner-auto-chunkmode))
  102. (cl-call-next-method))
  103. (defvar syntax-ppss-wide)
  104. (defvar syntax-ppss-last)
  105. (defun pm--debug-info (&optional span as-list)
  106. (let* ((span (or span (and polymode-mode (pm-innermost-span))))
  107. (message-log-max nil)
  108. (beg (nth 1 span))
  109. (end (nth 2 span))
  110. (obj (nth 3 span))
  111. (type (and span (or (car span) 'host))))
  112. (let ((out (list (current-buffer)
  113. (point-min) (point) (point-max)
  114. major-mode
  115. type beg end
  116. (and obj (pm-debug-info obj))
  117. (format "lppss:%s"
  118. (if pm--emacs>26
  119. (car syntax-ppss-wide)
  120. syntax-ppss-last)))))
  121. (if as-list
  122. out
  123. (apply #'format
  124. "(%s) min:%d pos:%d max:%d || (%s) type:%s span:%s-%s %s %s"
  125. out)))))
  126. (defun pm-debug-info-on-current-span (no-cache)
  127. "Show info on current span.
  128. With NO-CACHE prefix, don't use cached values of the span."
  129. (interactive "P")
  130. (if (not polymode-mode)
  131. (message "not in a polymode buffer")
  132. (let ((span (pm-innermost-span nil no-cache)))
  133. (message (pm--debug-info span))
  134. ;; (move-overlay pm--highlight-overlay (nth 1 span) (nth 2 span) (current-buffer))
  135. (pm-debug-flick-region (nth 1 span) (nth 2 span)))))
  136. (defun pm-debug-report-points (&optional where)
  137. (when polymode-mode
  138. (let* ((bufs (eieio-oref pm/polymode '-buffers))
  139. (poses (mapcar (lambda (b)
  140. (format "%s:%d" b (with-current-buffer b (point))))
  141. bufs)))
  142. (message "<%s> cb:%s %s" (or where "") (current-buffer) poses)))
  143. nil)
  144. ;;; TOGGLING
  145. (defvar pm-debug-display-info-message nil)
  146. (defun pm-debug-toogle-info-message ()
  147. "Toggle permanent info display."
  148. (interactive)
  149. (setq pm-debug-display-info-message (not pm-debug-display-info-message)))
  150. (defvar poly-lock-allow-fontification)
  151. (defun pm-debug-toggle-fontification ()
  152. "Enable or disable fontification in polymode buffers."
  153. (interactive)
  154. (if poly-lock-allow-fontification
  155. (progn
  156. (message "fontificaiton disabled")
  157. (dolist (b (buffer-list))
  158. (with-current-buffer b
  159. (when polymode-mode
  160. (setq poly-lock-allow-fontification nil
  161. font-lock-mode nil
  162. fontification-functions nil)))))
  163. (message "fontificaiton enabled")
  164. (dolist (b (buffer-list))
  165. (with-current-buffer b
  166. (when polymode-mode
  167. (setq poly-lock-allow-fontification t
  168. font-lock-mode t
  169. fontification-functions '(poly-lock-function)))))))
  170. (defun pm-debug-toggle-after-change ()
  171. "Allow or disallow polymode actions in `after-change-functions'."
  172. (interactive)
  173. (if pm-allow-after-change-hook
  174. (progn
  175. (message "after-change disabled")
  176. (setq pm-allow-after-change-hook nil))
  177. (message "after-change enabled")
  178. (setq pm-allow-after-change-hook t)))
  179. (defun pm-debug-toggle-post-command ()
  180. "Allow or disallow polymode actions in `post-command-hook'."
  181. (interactive)
  182. (if pm-allow-post-command-hook
  183. (progn
  184. (message "post-command disabled")
  185. (setq pm-allow-post-command-hook nil))
  186. (message "post-command enabled")
  187. (setq pm-allow-post-command-hook t)))
  188. (defun pm-debug-toggle-all ()
  189. "Toggle all polymode guards back and forth."
  190. (interactive)
  191. (if poly-lock-allow-fontification
  192. (progn
  193. (message "fontificaiton, after-chnage and command-hook disabled")
  194. (setq poly-lock-allow-fontification nil
  195. pm-allow-after-change-hook nil
  196. pm-allow-post-command-hook nil))
  197. (message "fontificaiton, after-change and command-hook enabled")
  198. (setq poly-lock-allow-fontification t
  199. pm-allow-after-change-hook t
  200. pm-allow-post-command-hook t)))
  201. ;;; FONT-LOCK
  202. (defun pm-debug-fontify-current-span ()
  203. "Fontify current span."
  204. (interactive)
  205. (let ((span (pm-innermost-span))
  206. (poly-lock-allow-fontification t))
  207. (poly-lock-flush (nth 1 span) (nth 2 span))
  208. (poly-lock-fontify-now (nth 1 span) (nth 2 span))))
  209. (defun pm-debug-fontify-current-buffer ()
  210. "Fontify current buffer."
  211. (interactive)
  212. (let ((poly-lock-allow-fontification t))
  213. (font-lock-unfontify-buffer)
  214. (poly-lock-flush (point-min) (point-max))
  215. (poly-lock-fontify-now (point-min) (point-max))))
  216. ;;; TRACING
  217. (defvar pm-traced-functions
  218. '(
  219. ;; core initialization
  220. (0 (pm-initialize
  221. pm--common-setup
  222. pm--mode-setup))
  223. ;; core hooks
  224. (1 (polymode-post-command-select-buffer
  225. polymode-after-kill-fixes
  226. ;; this one indicates the start of a sequence
  227. poly-lock-after-change))
  228. ;; advises
  229. (2 (pm-override-output-cons
  230. pm-around-advice
  231. polymode-with-current-base-buffer))
  232. ;; font-lock
  233. (3 (font-lock-default-fontify-region
  234. font-lock-fontify-keywords-region
  235. font-lock-fontify-region
  236. font-lock-fontify-syntactically-region
  237. font-lock-unfontify-region
  238. jit-lock--run-functions
  239. jit-lock-fontify-now
  240. poly-lock--after-change-internal
  241. poly-lock--extend-region
  242. poly-lock--extend-region-span
  243. poly-lock-after-change
  244. poly-lock-flush
  245. poly-lock-fontify-now
  246. poly-lock-function))
  247. ;; syntax
  248. (4 (syntax-ppss
  249. pm--call-syntax-propertize-original
  250. polymode-syntax-propertize
  251. polymode-restrict-syntax-propertize-extension
  252. pm-flush-syntax-ppss-cache
  253. pm--reset-ppss-cache))
  254. ;; core functions
  255. (5 (pm-select-buffer
  256. pm-map-over-spans
  257. pm--get-intersected-span
  258. pm--cached-span))
  259. ;; (13 . "^syntax-")
  260. (14 . "^polymode-")
  261. (15 . "^pm-")))
  262. (defvar pm--do-trace nil)
  263. ;;;###autoload
  264. (defun pm-toggle-tracing (level)
  265. "Toggle polymode tracing.
  266. With numeric prefix toggle tracing for that LEVEL. Currently
  267. universal argument toggles maximum level of tracing (4). Default
  268. level is 3."
  269. (interactive "P")
  270. (setq level (prefix-numeric-value (or level 3)))
  271. (with-current-buffer (get-buffer-create "*Messages*")
  272. (read-only-mode -1))
  273. (setq pm--do-trace (not pm--do-trace))
  274. (if pm--do-trace
  275. (progn (dolist (kv pm-traced-functions)
  276. (when (<= (car kv) level)
  277. (if (stringp (cdr kv))
  278. (pm-trace-functions-by-regexp (cdr kv))
  279. (dolist (fn (cadr kv))
  280. (pm-trace fn)))))
  281. (message "Polymode tracing activated"))
  282. (untrace-all)
  283. (message "Polymode tracing deactivated")))
  284. ;;;###autoload
  285. (defun pm-trace (fn)
  286. "Trace function FN.
  287. Use `untrace-function' to untrace or `untrace-all' to untrace all
  288. currently traced functions."
  289. (interactive (trace--read-args "Trace: "))
  290. (let ((buff (get-buffer "*Messages*")))
  291. (unless (advice-member-p trace-advice-name fn)
  292. (advice-add
  293. fn :around
  294. (let ((advice (trace-make-advice
  295. fn buff 'background
  296. #'pm-trace--tracing-context)))
  297. (lambda (body &rest args)
  298. (when (eq fn 'polymode-flush-syntax-ppss-cache)
  299. (with-current-buffer buff
  300. (save-excursion
  301. (goto-char (point-max))
  302. (insert "\n"))))
  303. (if polymode-mode
  304. (apply advice body args)
  305. (apply body args))))
  306. `((name . ,trace-advice-name)
  307. (depth . -100))))))
  308. (defun pm-trace-functions-by-regexp (regexp)
  309. "Trace all functions whose name matched REGEXP."
  310. (interactive "sRegex: ")
  311. (cl-loop for sym being the symbols
  312. when (and (fboundp sym)
  313. (not (memq sym '(pm-toggle-tracing
  314. pm-trace--tracing-context
  315. pm-format-span
  316. pm-fun-matcher
  317. pm--find-tail-from-head)))
  318. (not (string-match "^pm-\\(trace\\|debug\\)" (symbol-name sym)))
  319. (string-match regexp (symbol-name sym)))
  320. do (pm-trace sym)))
  321. (defun pm-trace--tracing-context ()
  322. (let ((span (or *span*
  323. (get-text-property (point) :pm-span))))
  324. (format " [%s pos:%d(%d-%d) %s%s (%f)]"
  325. (current-buffer) (point) (point-min) (point-max)
  326. (or (when span
  327. (when (not (and (= (point-min) (nth 1 span))
  328. (= (point-max) (nth 2 span))))
  329. "UNPR "))
  330. "")
  331. (when span
  332. (pm-format-span span))
  333. (float-time))))
  334. ;; fix object printing
  335. (defun pm-trace--fix-1-arg-for-tracing (arg)
  336. (cond
  337. ((eieio-object-p arg) (eieio-object-name arg))
  338. ((and (listp arg) (eieio-object-p (nth 3 arg)))
  339. (list (nth 0 arg) (nth 1 arg) (nth 2 arg) (eieio-object-name (nth 3 arg))))
  340. (arg)))
  341. (defun pm-trace--fix-args-for-tracing (orig-fn fn level args context)
  342. (let ((args (or (and (listp args)
  343. (listp (cdr args))
  344. (ignore-errors (mapcar #'pm-trace--fix-1-arg-for-tracing args)))
  345. args)))
  346. (funcall orig-fn fn level args context)))
  347. (advice-add #'trace-entry-message :around #'pm-trace--fix-args-for-tracing)
  348. (advice-add #'trace-exit-message :around #'pm-trace--fix-args-for-tracing)
  349. ;; (advice-remove #'trace-entry-message #'pm-trace--fix-args-for-tracing)
  350. ;; (advice-remove #'trace-exit-message #'pm-trace--fix-args-for-tracing)
  351. ;;; RELEVANT VARIABLES
  352. (defvar pm-debug-relevant-variables
  353. `(:change
  354. (before-change-functions after-change-functions)
  355. :command (pre-command-hook
  356. post-command-hook)
  357. :font-lock (fontification-functions
  358. font-lock-function
  359. font-lock-flush-function
  360. font-lock-ensure-function
  361. font-lock-fontify-region-function
  362. font-lock-fontify-buffer-function
  363. font-lock-unfontify-region-function
  364. font-lock-unfontify-buffer-function
  365. jit-lock-after-change-extend-region-functions
  366. jit-lock-functions
  367. poly-lock-defer-after-change)
  368. ;; If any of these are reset by host mode it can create issues with
  369. ;; font-lock and syntax (e.g. scala-mode in #195)
  370. :search (parse-sexp-lookup-properties
  371. parse-sexp-ignore-comments
  372. ;; (syntax-table)
  373. ;; font-lock-syntax-table
  374. case-fold-search)
  375. :indent (indent-line-function
  376. indent-region-function
  377. pm--indent-line-function-original)
  378. :revert (revert-buffer-function
  379. before-revert-hook
  380. after-revert-hook)
  381. :save (after-save-hook
  382. before-save-hook
  383. write-contents-functions
  384. local-write-file-hooks
  385. write-file-functions)
  386. :syntax (syntax-propertize-function
  387. syntax-propertize-extend-region-functions
  388. pm--syntax-propertize-function-original)))
  389. ;;;###autoload
  390. (defun pm-debug-relevant-variables (&optional out-type)
  391. "Get the relevant polymode variables.
  392. If OUT-TYPE is 'buffer, print the variables in the dedicated
  393. buffer, if 'message issue a message, if nil just return a list of values."
  394. (interactive (list 'buffer))
  395. (let* ((cbuff (current-buffer))
  396. (vars (cl-loop for v on pm-debug-relevant-variables by #'cddr
  397. collect (cons (car v)
  398. (mapcar (lambda (v)
  399. (cons v (buffer-local-value v cbuff)))
  400. (cadr v))))))
  401. (require 'pp)
  402. (cond
  403. ((eq out-type 'buffer)
  404. (with-current-buffer (get-buffer-create "*polymode-vars*")
  405. (erase-buffer)
  406. (goto-char (point-max))
  407. (insert (format "\n================== %s ===================\n" cbuff))
  408. (insert (pp-to-string vars))
  409. (toggle-truncate-lines -1)
  410. (goto-char (point-max))
  411. (view-mode)
  412. (display-buffer (current-buffer))))
  413. ((eq out-type 'message)
  414. (message "%s" (pp-to-string vars)))
  415. (t vars))))
  416. (defun pm-debug-diff-local-vars (&optional buffer1 buffer2)
  417. "Print differences between local variables in BUFFER1 and BUFFER2."
  418. (interactive)
  419. (let* ((buffer1 (or buffer1 (read-buffer "Buffer1: " (buffer-name (current-buffer)))))
  420. (buffer2 (or buffer2 (read-buffer "Buffer2: " (buffer-name (nth 2 (buffer-list))))))
  421. (vars1 (buffer-local-variables (get-buffer buffer1)))
  422. (vars2 (buffer-local-variables (get-buffer buffer2)))
  423. (all-keys (delete-dups (append (mapcar #'car vars1)
  424. (mapcar #'car vars2))))
  425. (out-buf (get-buffer-create "*pm-debug-output")))
  426. (with-current-buffer out-buf
  427. (erase-buffer)
  428. (pp (delq nil
  429. (mapcar (lambda (k)
  430. (let ((val1 (cdr (assoc k vars1)))
  431. (val2 (cdr (assoc k vars2))))
  432. (unless (equal val1 val2)
  433. (list k val1 val2))))
  434. all-keys))
  435. out-buf))
  436. (pop-to-buffer out-buf)))
  437. ;;; HIGHLIGHT
  438. (defun pm-debug-highlight-current-span ()
  439. (when polymode-mode
  440. (with-silent-modifications
  441. (unless (memq this-command '(pm-debug-info-on-current-span
  442. pm-debug-highlight-last-font-lock-error-region))
  443. (delete-overlay pm--highlight-overlay))
  444. (condition-case-unless-debug err
  445. (let ((span (pm-innermost-span)))
  446. (when pm-debug-display-info-message
  447. (message (pm--debug-info span)))
  448. (move-overlay pm--underline-overlay (nth 1 span) (nth 2 span) (current-buffer)))
  449. (error (message "%s" (error-message-string err)))))))
  450. (defun pm-debug-flick-region (start end &optional delay)
  451. (move-overlay pm--highlight-overlay start end (current-buffer))
  452. (run-with-timer (or delay 0.4) nil (lambda () (delete-overlay pm--highlight-overlay))))
  453. (defun pm-debug-map-over-spans-and-highlight ()
  454. "Map over all spans in the buffer and highlight briefly."
  455. (interactive)
  456. (pm-map-over-spans (lambda (span)
  457. (let ((start (nth 1 span))
  458. (end (nth 2 span)))
  459. (pm-debug-flick-region start end)
  460. (sit-for 1)))
  461. (point-min) (point-max) nil nil t))
  462. (defun pm-debug-map-over-modes-and-highlight (&optional beg end)
  463. "Map over all spans between BEG and END and highlight modes."
  464. (interactive)
  465. (let ((cbuf (current-buffer)))
  466. (pm-map-over-modes
  467. (lambda (beg end)
  468. (goto-char beg)
  469. ;; (dbg beg end (pm-format-span))
  470. (with-current-buffer cbuf
  471. (recenter-top-bottom)
  472. (pm-debug-flick-region (max beg (point-min))
  473. (min end (point-max))))
  474. (sit-for 1))
  475. (or beg (point-min))
  476. (or end (point-max)))))
  477. (defun pm-debug-run-over-check (no-cache)
  478. "Map over all spans and report the time taken.
  479. Switch to buffer is performed on every position in the buffer.
  480. On prefix NO-CACHE don't use cached spans."
  481. (interactive)
  482. (goto-char (point-min))
  483. (let ((start (current-time))
  484. (count 1)
  485. (pm-initialization-in-progress no-cache))
  486. (pm-switch-to-buffer)
  487. (while (< (point) (point-max))
  488. (setq count (1+ count))
  489. (forward-char)
  490. (pm-switch-to-buffer))
  491. (let ((elapsed (float-time (time-subtract (current-time) start))))
  492. (message "Elapsed: %s per-char: %s" elapsed (/ elapsed count)))))
  493. (defun pm-dbg (msg &rest args)
  494. (let ((cbuf (current-buffer))
  495. (cpos (point)))
  496. (with-current-buffer (get-buffer-create "*pm-dbg*")
  497. (save-excursion
  498. (goto-char (point-max))
  499. (insert "\n")
  500. (insert (apply 'format (concat "%f [%s at %d]: " msg)
  501. (float-time) cbuf cpos args))))))
  502. (provide 'polymode-debug)
  503. ;;; polymode-debug.el ends here