aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-02 00:08:33 -0400
committerCraig Jennings <c@cjennings.net>2026-07-02 00:08:33 -0400
commit821ec1e4d5f5e64ed54972960589ca5c8c41b9f9 (patch)
treedad5e9b67a8f059b962d7070f42244f8d095a42f
parent622a4d8c13a5037e064cf9caa3e917100f7f5cc9 (diff)
downloaddotemacs-821ec1e4d5f5e64ed54972960589ca5c8c41b9f9.tar.gz
dotemacs-821ec1e4d5f5e64ed54972960589ca5c8c41b9f9.zip
feat(markdown): start the preview server from F2 when it's down
cj/markdown-preview now brings up the simple-httpd listener itself instead of signaling a user-error pointing at cj/markdown-preview-server-start. The two-command flow guarded against a surprise network listener, but the preview is the only reason the listener exists, so the guard was just an extra step. I kept the start command for manual use.
-rw-r--r--modules/markdown-config.el18
-rw-r--r--tests/test-markdown-config-preview-autostart.el52
-rw-r--r--tests/test-markdown-config.el36
3 files changed, 93 insertions, 13 deletions
diff --git a/modules/markdown-config.el b/modules/markdown-config.el
index cb37556f..d2cd7f86 100644
--- a/modules/markdown-config.el
+++ b/modules/markdown-config.el
@@ -40,6 +40,8 @@
;;;; --------------------- WIP: Markdown-Preview ---------------------
(declare-function imp--notify-clients "impatient-mode")
+(declare-function httpd-running-p "simple-httpd")
+(declare-function httpd-start "simple-httpd")
(defun cj/markdown-preview-server-start ()
"Start the simple-httpd listener that serves the live markdown preview.
@@ -49,16 +51,20 @@ Idempotent: re-running while the server is already up is a no-op."
(httpd-start)
(message "markdown preview server running on http://localhost:8080/imp"))
+(defun cj/--markdown-preview-ensure-server ()
+ "Start the markdown preview server unless it's already running."
+ (require 'simple-httpd)
+ (unless (httpd-running-p)
+ (cj/markdown-preview-server-start)))
+
;; the filter to apply to markdown before impatient-mode pushes it to the server
(defun cj/markdown-preview ()
"Open the current buffer as a live HTML preview at http://localhost:8080/imp.
-The simple-httpd listener must already be running -- see
-`cj/markdown-preview-server-start'. Starting a network listener as a
-side effect of opening a preview is surprising, so the server start
-lives in a separate command."
+Starts the simple-httpd listener itself when it isn't already running
+\(per the 2026-07-01 decision; the earlier separate-start design
+signaled a `user-error' instead)."
(interactive)
- (unless (httpd-running-p)
- (user-error "markdown preview server not running; run `M-x cj/markdown-preview-server-start' first"))
+ (cj/--markdown-preview-ensure-server)
(impatient-mode 1)
(setq imp-user-filter #'cj/markdown-html)
(cl-incf imp-last-state)
diff --git a/tests/test-markdown-config-preview-autostart.el b/tests/test-markdown-config-preview-autostart.el
new file mode 100644
index 00000000..247c75d7
--- /dev/null
+++ b/tests/test-markdown-config-preview-autostart.el
@@ -0,0 +1,52 @@
+;;; test-markdown-config-preview-autostart.el --- preview server autostart -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for `cj/--markdown-preview-ensure-server': F2 preview starts the
+;; simple-httpd listener itself when it isn't running (per the 2026-07-01
+;; cj comment), and leaves a running server alone. simple-httpd isn't
+;; installed in batch, so the tests provide a stub feature with the two
+;; functions the helper touches.
+
+;;; Code:
+
+(require 'ert)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+
+(require 'markdown-config)
+
+;; Stand in for the elpa package: `require' in the helper becomes a
+;; no-op. Tests mock httpd-running-p explicitly (works whether the
+;; real simple-httpd or this stub is loaded), so the stub bodies never
+;; matter.
+(require 'cl-lib)
+(unless (featurep 'simple-httpd)
+ (defun httpd-running-p () nil)
+ (defun httpd-start () nil)
+ (provide 'simple-httpd))
+
+(ert-deftest test-markdown-config-ensure-server-starts-when-down ()
+ "Normal: a stopped server gets started."
+ (let ((started nil))
+ (cl-letf (((symbol-function 'httpd-running-p) (lambda () nil))
+ ((symbol-function 'cj/markdown-preview-server-start)
+ (lambda () (setq started t))))
+ (cj/--markdown-preview-ensure-server)
+ (should started))))
+
+(ert-deftest test-markdown-config-ensure-server-noop-when-running ()
+ "Boundary: a running server is left alone."
+ (let ((started nil))
+ (cl-letf (((symbol-function 'httpd-running-p) (lambda () t))
+ ((symbol-function 'cj/markdown-preview-server-start)
+ (lambda () (setq started t))))
+ (cj/--markdown-preview-ensure-server)
+ (should-not started))))
+
+(ert-deftest test-markdown-config-preview-no-longer-signals-user-error ()
+ "Error-path regression: the old user-error on a stopped server is gone."
+ (should-not (string-match-p "user-error"
+ (format "%S" (symbol-function 'cj/markdown-preview)))))
+
+(provide 'test-markdown-config-preview-autostart)
+;;; test-markdown-config-preview-autostart.el ends here
diff --git a/tests/test-markdown-config.el b/tests/test-markdown-config.el
index e079a8b4..ed3543bb 100644
--- a/tests/test-markdown-config.el
+++ b/tests/test-markdown-config.el
@@ -18,6 +18,14 @@
(require 'org)
(require 'markdown-config)
+;; simple-httpd (elpa) is absent under `make test'; stand in for it so
+;; the preview path's (require 'simple-httpd) stays a no-op. Tests mock
+;; httpd-running-p explicitly, so the stub bodies never matter.
+(unless (featurep 'simple-httpd)
+ (defun httpd-running-p () nil)
+ (defun httpd-start () nil)
+ (provide 'simple-httpd))
+
(ert-deftest test-markdown-config-registers-markdown-org-src-lang ()
"Normal: `markdown' shows up in `org-src-lang-modes' mapped to
`markdown' so org-lint stops warning on `#+begin_src markdown' and
@@ -70,14 +78,28 @@ plain HTTP."
(should (string-match-p "<xmp" (buffer-string))))
(kill-buffer src))))
-;;; cj/markdown-preview (guard: refuse when the httpd listener is down)
+;;; cj/markdown-preview (autostart: bring the httpd listener up when down)
+
+(defvar imp-user-filter)
+(defvar imp-last-state)
-(ert-deftest test-markdown-preview-errors-when-server-down ()
- "Error: `cj/markdown-preview' signals a user-error when the simple-httpd
-listener is not running, rather than opening a preview against a dead server.
-Also pins the rename off the bare `markdown-preview' that markdown-mode shadows."
- (cl-letf (((symbol-function 'httpd-running-p) (lambda () nil)))
- (should-error (cj/markdown-preview) :type 'user-error)))
+(ert-deftest test-markdown-preview-starts-server-when-down ()
+ "Normal: `cj/markdown-preview' starts the httpd listener when it's down
+\(the 2026-07-01 autostart decision replaced the old user-error guard).
+Also pins the rename off the bare `markdown-preview' that markdown-mode
+shadows. Everything downstream is mocked so no listener or browser runs."
+ (let ((started nil)
+ (imp-user-filter nil)
+ (imp-last-state 0))
+ (cl-letf (((symbol-function 'httpd-running-p) (lambda () nil))
+ ((symbol-function 'cj/markdown-preview-server-start)
+ (lambda () (setq started t)))
+ ((symbol-function 'impatient-mode) (lambda (&rest _) nil))
+ ((symbol-function 'imp--notify-clients) (lambda (&rest _) nil))
+ ((symbol-function 'browse-url) (lambda (&rest _) nil)))
+ (with-temp-buffer
+ (cj/markdown-preview))
+ (should started))))
(provide 'test-markdown-config)
;;; test-markdown-config.el ends here