branch: elpa/racket-mode
commit 27140827c5ca7b167a56d21a3ae5bc5a829c3647
Author: Greg Hendershott <[email protected]>
Commit: Greg Hendershott <[email protected]>

    Add racket--cmd-ready-p and often use instead of racket--cmd-open-p
    
    Arising from working on #763. Although this doesn't fix that issue,
    this improvement is "adjacent".
---
 racket-cmd.el         | 82 ++++++++++++++++++++++++++++++++++++---------------
 racket-hash-lang.el   |  2 +-
 racket-repl.el        |  2 +-
 racket-xp-complete.el |  6 ++--
 racket-xp.el          |  4 +--
 5 files changed, 66 insertions(+), 30 deletions(-)

diff --git a/racket-cmd.el b/racket-cmd.el
index 00f215dc0c4..4ff46f57b7a 100644
--- a/racket-cmd.el
+++ b/racket-cmd.el
@@ -61,11 +61,43 @@ Before doing anything runs the hook 
`racket-stop-back-end-hook'."
   (racket--cmd-close))
 
 (defun racket--cmd-open-p ()
-  "Does a running process exist for `racket-back-end-name'?"
+  "Does a running process exist for `racket-back-end'?
+
+The process could be running, but the back end not yet fully
+initialized. See also `racket--cmd-ready-p'."
   (pcase (get-process (racket--back-end-process-name (racket-back-end)))
     ((and (pred (processp)) proc)
      (eq 'run (process-status proc)))))
 
+(defun racket--cmd-ready-p ()
+  "Is `racket-back-end' ready to accept commands?
+
+This is a \"superset\" of `racket--cmd-open-p': Not merely is the Racket
+process running, but also has our back end code fully loaded and issued
+a \"ready\" notification. In other words, will it respond to commands
+promptly.
+
+Despite the \"-p\" suffix, returns nil or a value more interesting than
+t: See `racket--put-start-and-load-timings'."
+  (pcase (get-process (racket--back-end-process-name (racket-back-end)))
+    ((and (pred (processp)) proc)
+     (and (eq 'run (process-status proc))
+          (process-get proc 'racket-back-end-ready)))))
+
+(defun racket--put-start-and-load-timings (proc)
+  "Set process prop \\='racket-back-end-ready for `racket--cmd-ready-p'.
+
+The value is a cons of the duration of the Racket process startup, and,
+the subsequent duration of our back end loading sufficiently to become
+ready.
+
+Intended to be called after a (ready) notification from back end."
+  (pcase (process-get proc 'racket-back-end-startup)
+    (`(,proc0 ,proc1) ;times before/after `make-process'
+     (let ((proc-dur (float-time (time-subtract proc1 proc0)))
+           (load-dur (float-time (time-subtract nil proc1))))
+       (process-put proc 'racket-back-end-ready (cons proc-dur load-dur))))))
+
 (make-obsolete-variable
  'racket-adjust-run-rkt
  "This is no longer supported."
@@ -109,6 +141,7 @@ Before doing anything runs the hook 
`racket-stop-back-end-hook'."
                        "--do-not-use-svg"))
            (args    (list main-dot-rkt svg-flag))
            (command (racket--back-end-args->command back-end args))
+           (t0      (current-time))
            (process
             (make-process
              :name            process-name
@@ -122,6 +155,7 @@ Before doing anything runs the hook 
`racket-stop-back-end-hook'."
              :sentinel        #'racket--cmd-process-sentinel))
            (status (process-status process)))
       (process-put process 'racket-back-end-name (racket-back-end-name 
back-end))
+      (process-put process 'racket-back-end-startup (list t0 (current-time)))
       (unless (eq status 'run)
         (error "%s process status is not \"run\", instead it is %s"
                process-name
@@ -160,9 +194,7 @@ sentinel is `ignore'."
       (with-current-buffer buffer
         (goto-char (point-max))
         (insert string)
-        (racket--cmd-read (apply-partially
-                           #'racket--cmd-dispatch
-                           (process-get proc 'racket-back-end-name)))))))
+        (racket--cmd-read (apply-partially #'racket--cmd-dispatch proc))))))
 
 ;; The process filter inserts text as it arrives in chunks. So the
 ;; challenge here is to read whenever the buffer accumulates one or
@@ -209,29 +241,33 @@ sentinel is `ignore'."
 (defvar racket--cmd-nonce 0
   "Number that increments for each command request we send.")
 
-(defun racket--cmd-dispatch (back-end response)
+(defun racket--cmd-dispatch (proc response)
   "Do something with a sexpr sent to us from the command server.
 Although mostly these are 1:1 responses to command requests, some
 like \"logger\", \"debug-break\", and \"hash-lang\" are
 notifications."
-  (pcase response
-    (`(startup-error ,kind ,data)
-     (run-at-time 0.001 nil #'racket--on-startup-error kind data))
-    (`(logger ,str)
-     (run-at-time 0.001 nil #'racket--logger-on-notify back-end str))
-    (`(debug-break . ,response)
-     (run-at-time 0.001 nil #'racket--debug-on-break response))
-    (`(hash-lang ,id . ,vs)
-     (run-at-time 0.001 nil #'racket--hash-lang-on-notify id vs))
-    (`(repl-output ,session-id ,kind ,v)
-     (run-at-time 0.001 nil #'racket--repl-on-output session-id kind v))
-    (`(pkg-op-notify . ,v)
-     (run-at-time 0.001 nil #'racket--package-on-notify v))
-    (`(,nonce . ,response)
-     (when-let (callback (gethash nonce racket--cmd-nonce->callback))
-       (remhash nonce racket--cmd-nonce->callback)
-       (run-at-time 0.001 nil callback response)))
-    (_ nil)))
+  (cl-flet ((soon (fun &rest args)
+              (apply #'run-at-time 0.001 nil fun args)))
+    (pcase response
+      (`(ready)
+       (racket--put-start-and-load-timings proc))
+      (`(startup-error ,kind ,data)
+       (soon #'racket--on-startup-error kind data))
+      (`(logger ,str)
+       (soon #'racket--logger-on-notify (process-get proc 
'racket-back-end-name) str))
+      (`(debug-break . ,response)
+       (soon #'racket--debug-on-break response))
+      (`(hash-lang ,id . ,vs)
+       (soon #'racket--hash-lang-on-notify id vs))
+      (`(repl-output ,session-id ,kind ,v)
+       (soon #'racket--repl-on-output session-id kind v))
+      (`(pkg-op-notify . ,v)
+       (soon #'racket--package-on-notify v))
+      (`(,nonce . ,response)
+       (when-let (callback (gethash nonce racket--cmd-nonce->callback))
+         (remhash nonce racket--cmd-nonce->callback)
+         (soon callback response)))
+      (_ nil))))
 
 (defun racket--assert-readable (sexp)
   "Sanity check that SEXP is readable by Racket.
diff --git a/racket-hash-lang.el b/racket-hash-lang.el
index 652576f9d7e..334a6acfe29 100644
--- a/racket-hash-lang.el
+++ b/racket-hash-lang.el
@@ -323,7 +323,7 @@ A discussion of the information provided by a Racket 
language:
   ;; plain `prog-mode'.
   (setq-local racket--hash-lang-id nil) ;until async command response
   (setq-local racket--hash-lang-generation 1)
-  (unless (racket--cmd-open-p)
+  (unless (racket--cmd-ready-p)
     (setq-local header-line-format "Waiting for back end to start..."))
   (setq-local buffer-read-only t)
   (racket--cmd/async
diff --git a/racket-repl.el b/racket-repl.el
index 28c45fde1b5..a64d72d30b6 100644
--- a/racket-repl.el
+++ b/racket-repl.el
@@ -1234,7 +1234,7 @@ to supply this quickly enough or at all."
 
 Obtains documentation for point, if any, else the head of the
 s-expression."
-(when (and (racket--cmd-open-p)
+(when (and (racket--cmd-ready-p)
              (racket--repl-in-input-p (point)))
     (let ((point-pos (if (eq 32 (char-before))
                          (point)
diff --git a/racket-xp-complete.el b/racket-xp-complete.el
index 6f99c7d945f..45773acc878 100644
--- a/racket-xp-complete.el
+++ b/racket-xp-complete.el
@@ -1,6 +1,6 @@
 ;;; racket-xp-complete.el -*- lexical-binding: t -*-
 
-;; Copyright (c) 2013-2024 by Greg Hendershott.
+;; Copyright (c) 2013-2025 by Greg Hendershott.
 ;; Portions Copyright (C) 1985-1986, 1999-2013 Free Software Foundation, Inc.
 
 ;; Author: Greg Hendershott
@@ -131,7 +131,7 @@ that are require transformers."
           :exclusive 'no)))
 
 (defun racket--xp-make-company-location-proc ()
-  (when (racket--cmd-open-p)
+  (when (racket--cmd-ready-p)
     (let ((how (racket-how-front-to-back (buffer-file-name))))
       (lambda (str)
         (let ((str (substring-no-properties str)))
@@ -140,7 +140,7 @@ that are require transformers."
              (cons (racket-file-name-back-to-front path) line))))))))
 
 (defun racket--xp-make-company-doc-buffer-proc ()
-  (when (racket--cmd-open-p)
+  (when (racket--cmd-ready-p)
     (let ((how (racket-how-front-to-back (buffer-file-name))))
       (lambda (str)
         (let ((str (substring-no-properties str)))
diff --git a/racket-xp.el b/racket-xp.el
index a311e0e44f3..40cd6040ff0 100644
--- a/racket-xp.el
+++ b/racket-xp.el
@@ -639,7 +639,7 @@ Obtains documentation for point, if any, else the head of 
the
 s-expression.
 
 See also the customization variable `racket-xp-eldoc-level'."
-  (when (racket--cmd-open-p)
+  (when (racket--cmd-ready-p)
     (let ((point-pos (point))
           (head-pos (when (> (point) (point-min))
                       (condition-case _
@@ -1340,7 +1340,7 @@ we've worked hard to help shr to convert these correctly."
 (defun racket--xp-mode-lighter ()
   (let ((prefix "Rkt"))
     (pcase-let*
-        ((status (and (racket--cmd-open-p)
+        ((status (and (racket--cmd-ready-p)
                       racket--xp-mode-status))
          (`(,suffix ,face ,help-echo)
           (cl-case status

Reply via email to