|
31 | 31 | ;;; Code: |
32 | 32 |
|
33 | 33 | (require 'cl-lib) |
| 34 | +(require 'lisp-mnt) |
| 35 | +(require 'ghostel) |
| 36 | + |
| 37 | +(declare-function ghostel--module-version "ghostel-module") |
| 38 | +(declare-function ghostel--mode-enabled "ghostel-module") |
34 | 39 |
|
35 | 40 | (defvar ghostel-debug--log-buffer nil |
36 | 41 | "Buffer used for ghostel debug logging.") |
37 | 42 |
|
| 43 | +;;;###autoload |
38 | 44 | (defun ghostel-debug-start () |
39 | | - "Start logging ghostel filter calls to *ghostel-debug* buffer." |
| 45 | + "Start logging ghostel events to *ghostel-debug* buffer. |
| 46 | +Logs filter calls, key sends, resize events, redraw decisions |
| 47 | +\(including DEC 2026 skip/force), and `window-start' anchoring." |
40 | 48 | (interactive) |
41 | 49 | (setq ghostel-debug--log-buffer (get-buffer-create "*ghostel-debug*")) |
42 | 50 | (with-current-buffer ghostel-debug--log-buffer |
43 | 51 | (erase-buffer) |
44 | 52 | (insert "=== Ghostel Debug Log ===\n\n")) |
| 53 | + ;; Data path |
45 | 54 | (advice-add 'ghostel--filter :before #'ghostel-debug--log-filter) |
46 | 55 | (advice-add 'ghostel--send-key :before #'ghostel-debug--log-send) |
47 | 56 | (advice-add 'ghostel--send-encoded :before #'ghostel-debug--log-encoded) |
| 57 | + ;; Render path |
| 58 | + (advice-add 'ghostel--delayed-redraw :around #'ghostel-debug--log-redraw) |
| 59 | + (advice-add 'ghostel--window-adjust-process-window-size |
| 60 | + :around #'ghostel-debug--log-resize) |
48 | 61 | (when (fboundp 'ghostel--enable-vt-log) |
49 | 62 | (ghostel--enable-vt-log)) |
50 | 63 | (message "ghostel-debug: logging started, check *ghostel-debug* buffer")) |
|
55 | 68 | (advice-remove 'ghostel--filter #'ghostel-debug--log-filter) |
56 | 69 | (advice-remove 'ghostel--send-key #'ghostel-debug--log-send) |
57 | 70 | (advice-remove 'ghostel--send-encoded #'ghostel-debug--log-encoded) |
| 71 | + (advice-remove 'ghostel--delayed-redraw #'ghostel-debug--log-redraw) |
| 72 | + (advice-remove 'ghostel--window-adjust-process-window-size |
| 73 | + #'ghostel-debug--log-resize) |
58 | 74 | (when (fboundp 'ghostel--disable-vt-log) |
59 | 75 | (ghostel--disable-vt-log)) |
60 | 76 | (message "ghostel-debug: logging stopped")) |
@@ -103,6 +119,63 @@ _PROC is ignored." |
103 | 119 | (format-time-string "%T.%3N") |
104 | 120 | key-name mods utf8))))) |
105 | 121 |
|
| 122 | +(defun ghostel-debug--log-redraw (orig-fn buffer) |
| 123 | + "Log redraw decisions: skip vs execute, DEC 2026 state, timing. |
| 124 | +ORIG-FN is `ghostel--delayed-redraw', BUFFER is the target buffer." |
| 125 | + (when ghostel-debug--log-buffer |
| 126 | + (let (sync force win-start pt buf-size) |
| 127 | + (when (buffer-live-p buffer) |
| 128 | + (with-current-buffer buffer |
| 129 | + (setq sync (and ghostel--term |
| 130 | + (ghostel--mode-enabled ghostel--term 2026))) |
| 131 | + (setq force ghostel--force-next-redraw) |
| 132 | + (setq buf-size (buffer-size)) |
| 133 | + (setq pt (point)) |
| 134 | + (let ((win (get-buffer-window buffer))) |
| 135 | + (when win |
| 136 | + (setq win-start (window-start win)))))) |
| 137 | + (let ((t0 (current-time))) |
| 138 | + (funcall orig-fn buffer) |
| 139 | + (let ((elapsed (* 1000 (float-time (time-subtract (current-time) t0)))) |
| 140 | + pt-after win-start-after buf-size-after) |
| 141 | + (when (buffer-live-p buffer) |
| 142 | + (with-current-buffer buffer |
| 143 | + (setq pt-after (point)) |
| 144 | + (setq buf-size-after (buffer-size)) |
| 145 | + (let ((win (get-buffer-window buffer))) |
| 146 | + (when win |
| 147 | + (setq win-start-after (window-start win)))))) |
| 148 | + (with-current-buffer ghostel-debug--log-buffer |
| 149 | + (goto-char (point-max)) |
| 150 | + (if (and sync (not force)) |
| 151 | + (insert (format "[%s] REDRAW: SKIPPED (DEC2026 active, force=nil)\n" |
| 152 | + (format-time-string "%T.%3N"))) |
| 153 | + (insert (format "[%s] REDRAW: %.1fms force=%s dec2026=%s buf=%d→%d pt=%d→%d wstart=%s→%s\n" |
| 154 | + (format-time-string "%T.%3N") |
| 155 | + elapsed force sync |
| 156 | + buf-size buf-size-after |
| 157 | + pt pt-after |
| 158 | + win-start win-start-after))))))))) |
| 159 | + |
| 160 | +(defun ghostel-debug--log-resize (orig-fn process windows) |
| 161 | + "Log resize events with old/new dimensions and timing. |
| 162 | +ORIG-FN is `ghostel--window-adjust-process-window-size'. |
| 163 | +PROCESS and WINDOWS are passed through." |
| 164 | + (let* ((old-rows (when (buffer-live-p (process-buffer process)) |
| 165 | + (buffer-local-value 'ghostel--term-rows (process-buffer process)))) |
| 166 | + (t0 (current-time)) |
| 167 | + (size (funcall orig-fn process windows)) |
| 168 | + (elapsed (* 1000 (float-time (time-subtract (current-time) t0))))) |
| 169 | + (when ghostel-debug--log-buffer |
| 170 | + (with-current-buffer ghostel-debug--log-buffer |
| 171 | + (goto-char (point-max)) |
| 172 | + (insert (format "[%s] RESIZE: %sx%s → %sx%s (%.1fms)\n" |
| 173 | + (format-time-string "%T.%3N") |
| 174 | + (and old-rows (cdr size)) old-rows |
| 175 | + (car size) (cdr size) |
| 176 | + elapsed)))) |
| 177 | + size)) |
| 178 | + |
106 | 179 |
|
107 | 180 | ;;; Typing latency measurement |
108 | 181 |
|
@@ -221,5 +294,129 @@ The latency breakdown shows: |
221 | 294 | (insert "\n"))) |
222 | 295 | (message "ghostel-debug: latency report ready in *ghostel-debug*"))) |
223 | 296 |
|
| 297 | + |
| 298 | +;;; Environment diagnostics |
| 299 | + |
| 300 | +;;;###autoload |
| 301 | +(defun ghostel-debug-info () |
| 302 | + "Display diagnostic info about the ghostel environment. |
| 303 | +Collects Emacs version, system info, native module state, terminal |
| 304 | +state, and settings into *ghostel-debug* for pasting into bug reports." |
| 305 | + (interactive) |
| 306 | + (let ((out (get-buffer-create "*ghostel-debug*")) |
| 307 | + (ghostel-buf (when (derived-mode-p 'ghostel-mode) (current-buffer)))) |
| 308 | + (with-current-buffer out |
| 309 | + (let ((inhibit-read-only t)) |
| 310 | + (erase-buffer) |
| 311 | + (insert "=== ghostel-debug-info ===\n\n") |
| 312 | + ;; System |
| 313 | + (insert "--- System ---\n") |
| 314 | + (insert (format "Emacs version: %s\n" emacs-version)) |
| 315 | + (insert (format "System type: %s\n" system-type)) |
| 316 | + (insert (format "System config: %s\n" system-configuration)) |
| 317 | + (insert (format "Window system: %s\n" (or window-system "terminal"))) |
| 318 | + (when (display-graphic-p) |
| 319 | + (insert (format "Display pixel size: %sx%s\n" |
| 320 | + (display-pixel-width) (display-pixel-height))) |
| 321 | + (insert (format "Char size: %dx%d px\n" |
| 322 | + (frame-char-width) (frame-char-height)))) |
| 323 | + (insert (format "Native comp: %s\n" |
| 324 | + (if (and (fboundp 'native-comp-available-p) |
| 325 | + (native-comp-available-p)) |
| 326 | + "yes" "no"))) |
| 327 | + ;; Ghostel versions |
| 328 | + (insert "\n--- Ghostel ---\n") |
| 329 | + (let* ((lib (locate-library "ghostel")) |
| 330 | + (dir (and lib (file-name-directory lib)))) |
| 331 | + (insert (format "Package version: %s\n" |
| 332 | + (condition-case nil |
| 333 | + (lm-version (locate-library "ghostel.el" t)) |
| 334 | + (error "Unknown")))) |
| 335 | + (insert (format "Min module version: %s\n" ghostel--minimum-module-version)) |
| 336 | + (insert (format "Library path: %s\n" (or lib "not found"))) |
| 337 | + ;; Native module |
| 338 | + (let ((mod-loaded (fboundp 'ghostel--module-version))) |
| 339 | + (insert (format "Module loaded: %s\n" (if mod-loaded "yes" "no"))) |
| 340 | + (when mod-loaded |
| 341 | + (let ((mod-ver (ghostel--module-version))) |
| 342 | + (insert (format "Module version: %s\n" mod-ver)) |
| 343 | + (unless (string= mod-ver ghostel--minimum-module-version) |
| 344 | + (insert (format " *** VERSION MISMATCH: elisp expects >= %s, module is %s ***\n" |
| 345 | + ghostel--minimum-module-version mod-ver))))) |
| 346 | + (when dir |
| 347 | + (let ((mod-file (expand-file-name |
| 348 | + (concat "ghostel-module" module-file-suffix) dir))) |
| 349 | + (if (file-exists-p mod-file) |
| 350 | + (let ((attrs (file-attributes mod-file))) |
| 351 | + (insert (format "Module file: %s\n" mod-file)) |
| 352 | + (insert (format "Module size: %s bytes\n" |
| 353 | + (file-attribute-size attrs))) |
| 354 | + (insert (format "Module modified: %s\n" |
| 355 | + (format-time-string |
| 356 | + "%Y-%m-%d %H:%M:%S" |
| 357 | + (file-attribute-modification-time attrs))))) |
| 358 | + (insert (format "Module file: NOT FOUND in %s\n" dir))))))) |
| 359 | + ;; Terminal state |
| 360 | + (insert "\n--- Terminal State ---\n") |
| 361 | + (if ghostel-buf |
| 362 | + (let (info) |
| 363 | + (with-current-buffer ghostel-buf |
| 364 | + (setq info |
| 365 | + (list :buffer (buffer-name) |
| 366 | + :rows ghostel--term-rows |
| 367 | + :buf-size (buffer-size) |
| 368 | + :buf-lines (count-lines (point-min) (point-max)) |
| 369 | + :point (point) |
| 370 | + :term ghostel--term |
| 371 | + :force ghostel--force-next-redraw |
| 372 | + :pending (length ghostel--pending-output) |
| 373 | + :timer (and ghostel--redraw-timer t) |
| 374 | + :copy ghostel--copy-mode-active |
| 375 | + :dec2026 (and ghostel--term |
| 376 | + (ghostel--mode-enabled ghostel--term 2026)))) |
| 377 | + (let ((win (get-buffer-window ghostel-buf))) |
| 378 | + (when win |
| 379 | + (setq info (plist-put info :win-w (window-body-width win))) |
| 380 | + (setq info (plist-put info :win-h (window-body-height win))) |
| 381 | + (setq info (plist-put info :win-start (window-start win))) |
| 382 | + (setq info (plist-put info :win-end (window-end win t)))))) |
| 383 | + (insert (format "Buffer: %s\n" (plist-get info :buffer))) |
| 384 | + (insert (format "Term rows: %s\n" (plist-get info :rows))) |
| 385 | + (insert (format "Buffer size: %d chars, %d lines\n" |
| 386 | + (plist-get info :buf-size) (plist-get info :buf-lines))) |
| 387 | + (insert (format "Point: %d\n" (plist-get info :point))) |
| 388 | + (if (plist-get info :win-w) |
| 389 | + (progn |
| 390 | + (insert (format "Window body: %dx%d (cols x rows)\n" |
| 391 | + (plist-get info :win-w) (plist-get info :win-h))) |
| 392 | + (insert (format "Window start: %d\n" (plist-get info :win-start))) |
| 393 | + (insert (format "Window end: %d\n" (plist-get info :win-end)))) |
| 394 | + (insert "Window: not displayed\n")) |
| 395 | + (if (plist-get info :term) |
| 396 | + (progn |
| 397 | + (insert (format "DEC 2026 (sync): %s\n" |
| 398 | + (if (plist-get info :dec2026) "ACTIVE" "off"))) |
| 399 | + (insert (format "Force next redraw: %s\n" (plist-get info :force))) |
| 400 | + (insert (format "Pending output: %s chunks\n" (plist-get info :pending))) |
| 401 | + (insert (format "Redraw timer: %s\n" |
| 402 | + (if (plist-get info :timer) "pending" "none"))) |
| 403 | + (insert (format "Copy mode: %s\n" |
| 404 | + (if (plist-get info :copy) "active" "off")))) |
| 405 | + (insert "Term handle: nil (no terminal)\n"))) |
| 406 | + (insert "(not in a ghostel buffer)\n")) |
| 407 | + ;; Settings |
| 408 | + (insert "\n--- Settings ---\n") |
| 409 | + (insert (format "timer-delay: %s\n" ghostel-timer-delay)) |
| 410 | + (insert (format "full-redraw: %s\n" ghostel-full-redraw)) |
| 411 | + (insert (format "adaptive-fps: %s\n" ghostel-adaptive-fps)) |
| 412 | + (insert (format "immediate-threshold: %s\n" ghostel-immediate-redraw-threshold)) |
| 413 | + (insert (format "immediate-interval: %s\n" ghostel-immediate-redraw-interval)) |
| 414 | + (insert (format "scroll-conservatively: %s\n" |
| 415 | + (if ghostel-buf |
| 416 | + (buffer-local-value 'scroll-conservatively ghostel-buf) |
| 417 | + scroll-conservatively))))) |
| 418 | + (display-buffer out) |
| 419 | + (message "Debug info written to *ghostel-debug*"))) |
| 420 | + |
224 | 421 | (provide 'ghostel-debug) |
225 | 422 | ;;; ghostel-debug.el ends here |
0 commit comments