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.

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