Round 29: Add 20 new Emacs features

ober

48124abd3b3af6506c3bf89a20df61fa42818573

diff --git a/docs/jemacs-vs-emacs.md b/docs/jemacs-vs-emacs.md
index f400f08..babd6d9 100644
--- a/docs/jemacs-vs-emacs.md
+++ b/docs/jemacs-vs-emacs.md
@@ -1686,6 +1686,26 @@ No remaining Tier 1 gaps. All core editing, completion, and navigation features 
 | Describe key briefly | :orange_circle: | Show key's command |
 | Describe bindings | :orange_circle: | Show all key bindings |
 | Describe mode | :orange_circle: | Show current modes |
+| Describe face | :orange_circle: | Show face/style at point |
+| Describe char | :orange_circle: | Show character details at point |
+| Describe syntax | :orange_circle: | Show syntax class of char at point |
+| Describe categories | :orange_circle: | Show character categories |
+| Apropos command | :orange_circle: | Search commands by pattern |
+| Info/Emacs manual | :orange_circle: | Show jemacs manual reference |
+| View echo area messages | :orange_circle: | Show message history |
+| Toggle debug on error | :orange_circle: | Toggle debug-on-error |
+| Toggle debug on quit | :orange_circle: | Toggle debug-on-quit |
+| Profiler start | :orange_circle: | Start CPU profiler |
+| Profiler stop | :orange_circle: | Stop CPU profiler |
+| Profiler report | :orange_circle: | Show profiler report |
+| Memory report | :orange_circle: | Show memory usage |
+| Emacs uptime | :orange_circle: | Show session uptime |
+| Emacs version | :orange_circle: | Show jemacs version |
+| Emacs init time | :orange_circle: | Show init time |
+| List packages | :orange_circle: | List available packages |
+| Package install | :orange_circle: | Install package (placeholder) |
+| Package delete | :orange_circle: | Delete package (placeholder) |
+| Package refresh contents | :orange_circle: | Refresh packages (placeholder) |
 
 ---
 
diff --git a/src/jerboa-emacs/editor-extra-final.ss b/src/jerboa-emacs/editor-extra-final.ss
index 7b8e92f..b962e77 100644
--- a/src/jerboa-emacs/editor-extra-final.ss
+++ b/src/jerboa-emacs/editor-extra-final.ss
@@ -8927,3 +8927,99 @@
                     "\n\nTotal: " (length sorted) " active minor modes\n")))
     (editor-set-text ed text)
     (echo-message! echo (str (length sorted) " active modes"))))
+
+;; Round 29 batch 2: profiler-stop, profiler-report, memory-report, emacs-uptime,
+;; emacs-version, emacs-init-time, list-packages, package-install, package-delete,
+;; package-refresh-contents
+
+;; cmd-profiler-stop: Stop the CPU profiler
+(def (cmd-profiler-stop app)
+  (let* ((echo (app-state-echo app))
+         (start (hash-get (app-state-modes app) 'profiler-start-time)))
+    (if (not start)
+      (echo-message! echo "Profiler not running")
+      (let ((elapsed (- (time-second (current-time)) start)))
+        (hash-put! (app-state-modes app) 'profiler-elapsed elapsed)
+        (hash-remove! (app-state-modes app) 'profiler-start-time)
+        (echo-message! echo (str "Profiler stopped after " elapsed " seconds"))))))
+
+;; cmd-profiler-report: Show profiler report
+(def (cmd-profiler-report app)
+  (let* ((buf (app-state-current-buffer app))
+         (ed (buffer-editor buf))
+         (echo (app-state-echo app))
+         (elapsed (or (hash-get (app-state-modes app) 'profiler-elapsed) 0))
+         (text (str "=== Profiler Report ===\n\n"
+                    "Profile duration: " elapsed " seconds\n\n"
+                    "(Detailed CPU profiling requires Chez Scheme's\n"
+                    " profile-dump-data and related forms.\n"
+                    " Use M-x profiler-start/stop to time sections.)\n")))
+    (editor-set-text ed text)
+    (echo-message! echo "Profiler report")))
+
+;; cmd-memory-report: Show memory usage information
+(def (cmd-memory-report app)
+  (let* ((buf (app-state-current-buffer app))
+         (ed (buffer-editor buf))
+         (echo (app-state-echo app))
+         (buffers (app-state-buffers app))
+         (buf-count (length buffers))
+         (total-chars (apply + (map (lambda (b) (editor-get-length (buffer-editor b))) buffers)))
+         (text (str "=== Memory Report ===\n\n"
+                    "Buffers: " buf-count "\n"
+                    "Total buffer text: " total-chars " characters\n"
+                    "Estimated buffer memory: ~" (quotient (* total-chars 4) 1024) " KB\n\n"
+                    "Chez Scheme heap:\n"
+                    "  (Use scheme's (statistics) for detailed GC info)\n")))
+    (editor-set-text ed text)
+    (echo-message! echo "Memory report")))
+
+;; cmd-emacs-uptime: Show how long jemacs has been running
+(def (cmd-emacs-uptime app)
+  (let* ((echo (app-state-echo app))
+         (start (or (hash-get (app-state-modes app) 'start-time) (time-second (current-time))))
+         (now (time-second (current-time)))
+         (elapsed (- now start))
+         (hours (quotient elapsed 3600))
+         (minutes (quotient (remainder elapsed 3600) 60))
+         (seconds (remainder elapsed 60)))
+    (echo-message! echo (str "Uptime: " hours "h " minutes "m " seconds "s"))))
+
+;; cmd-emacs-version: Show jemacs version
+(def (cmd-emacs-version app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "jemacs (jerboa-emacs) on Chez Scheme 10.x")))
+
+;; cmd-emacs-init-time: Show initialization time
+(def (cmd-emacs-init-time app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Init time: <not measured> (see startup log)")))
+
+;; cmd-list-packages: List available packages/extensions
+(def (cmd-list-packages app)
+  (let* ((buf (app-state-current-buffer app))
+         (ed (buffer-editor buf))
+         (echo (app-state-echo app))
+         (text (str "=== Package List ===\n\n"
+                    "jemacs does not yet have a package manager.\n\n"
+                    "Built-in features are available via M-x.\n"
+                    "Custom extensions can be added as .ss files\n"
+                    "in the src/jerboa-emacs/ directory.\n\n"
+                    "For Emacs packages, see: https://melpa.org\n")))
+    (editor-set-text ed text)
+    (echo-message! echo "Package list (no package manager yet)")))
+
+;; cmd-package-install: Install a package
+(def (cmd-package-install app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Package manager not available. Add .ss files to src/ instead.")))
+
+;; cmd-package-delete: Delete a package
+(def (cmd-package-delete app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Package manager not available.")))
+
+;; cmd-package-refresh-contents: Refresh package list
+(def (cmd-package-refresh-contents app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Package manager not available.")))
diff --git a/src/jerboa-emacs/editor-extra-modes.ss b/src/jerboa-emacs/editor-extra-modes.ss
index debef41..c233361 100644
--- a/src/jerboa-emacs/editor-extra-modes.ss
+++ b/src/jerboa-emacs/editor-extra-modes.ss
@@ -9356,3 +9356,150 @@
     (send-message ed SCI_SETREADONLY 1 0)
     (echo-message! echo "Compilation mode")))
 
+;; Round 29 batch 1: describe-face, describe-char, describe-syntax, describe-categories,
+;; apropos-command, info-emacs-manual, view-echo-area-messages, toggle-debug-on-error,
+;; toggle-debug-on-quit, profiler-start
+
+;; cmd-describe-face: Show information about face/style at point
+(def (cmd-describe-face app)
+  (let* ((buf (app-state-current-buffer app))
+         (ed (buffer-editor buf))
+         (echo (app-state-echo app))
+         (pos (editor-cursor-position ed))
+         (style (send-message ed SCI_GETSTYLEAT pos 0)))
+    (echo-message! echo (str "Face at point: style " style))))
+
+;; cmd-describe-char: Show detailed information about character at point
+(def (cmd-describe-char app)
+  (let* ((buf (app-state-current-buffer app))
+         (ed (buffer-editor buf))
+         (echo (app-state-echo app))
+         (pos (editor-cursor-position ed))
+         (text (editor-get-text ed))
+         (len (string-length text)))
+    (if (>= pos len)
+      (echo-message! echo "End of buffer")
+      (let* ((c (string-ref text pos))
+             (code (char->integer c))
+             (info (str "Character: " (if (char=? c #\space) "SPACE" (string c))
+                        "\n  Unicode: U+" (let ((h (number->string code 16)))
+                                            (if (< (string-length h) 4)
+                                              (str (make-string (- 4 (string-length h)) #\0) h) h))
+                        "\n  Decimal: " code
+                        "\n  Octal: " (number->string code 8)
+                        "\n  Category: " (cond
+                                          ((char-alphabetic? c) "letter")
+                                          ((char-numeric? c) "digit")
+                                          ((char-whitespace? c) "whitespace")
+                                          (else "other")))))
+        (echo-message! echo info)))))
+
+;; cmd-describe-syntax: Show syntax table info for character at point
+(def (cmd-describe-syntax app)
+  (let* ((buf (app-state-current-buffer app))
+         (ed (buffer-editor buf))
+         (echo (app-state-echo app))
+         (pos (editor-cursor-position ed))
+         (text (editor-get-text ed))
+         (len (string-length text)))
+    (if (>= pos len)
+      (echo-message! echo "End of buffer")
+      (let* ((c (string-ref text pos))
+             (syntax (cond
+                       ((char-alphabetic? c) "word constituent")
+                       ((char-numeric? c) "word constituent")
+                       ((char-whitespace? c) "whitespace")
+                       ((memv c '(#\( #\[ #\{)) "open paren")
+                       ((memv c '(#\) #\] #\})) "close paren")
+                       ((memv c '(#\" #\')) "string quote")
+                       ((char=? c #\;) "comment start")
+                       ((char=? c #\\) "escape")
+                       (else "punctuation"))))
+        (echo-message! echo (str "Syntax of '" (string c) "': " syntax))))))
+
+;; cmd-describe-categories: Show character categories
+(def (cmd-describe-categories app)
+  (let* ((echo (app-state-echo app))
+         (buf (app-state-current-buffer app))
+         (ed (buffer-editor buf))
+         (text (str "=== Character Categories ===\n\n"
+                    "a - ASCII\n"
+                    "l - Latin\n"
+                    "g - Greek\n"
+                    "c - Chinese/CJK\n"
+                    "j - Japanese\n"
+                    "k - Korean\n"
+                    "h - Hebrew\n"
+                    "r - Arabic\n"
+                    ". - Base (all others)\n")))
+    (editor-set-text ed text)
+    (echo-message! echo "Character categories listed")))
+
+;; cmd-apropos-command: Search for commands matching pattern
+(def (cmd-apropos-command app)
+  (let* ((echo (app-state-echo app))
+         (buf (app-state-current-buffer app))
+         (ed (buffer-editor buf))
+         (pattern (echo-read-string echo "Apropos command: ")))
+    (if (or (not pattern) (string=? pattern ""))
+      (echo-message! echo "No pattern specified")
+      (let* ((cmds (hash-keys (app-state-commands app)))
+             (matches (filter (lambda (sym) (string-contains (symbol->string sym) pattern)) cmds))
+             (sorted (sort string<? (map symbol->string matches)))
+             (text (str "=== Apropos Command: \"" pattern "\" ===\n\n"
+                        (if (null? sorted) "No matches found.\n"
+                          (str (string-join (map (lambda (s) (str "  M-x " s)) sorted) "\n")
+                               "\n\n" (length sorted) " commands\n")))))
+        (editor-set-text ed text)
+        (echo-message! echo (str (length sorted) " commands matching \"" pattern "\""))))))
+
+;; cmd-info-emacs-manual: Open the Emacs manual reference
+(def (cmd-info-emacs-manual app)
+  (let* ((buf (app-state-current-buffer app))
+         (ed (buffer-editor buf))
+         (echo (app-state-echo app))
+         (text (str "=== jemacs Manual ===\n\n"
+                    "jemacs is a Chez Scheme Emacs-like editor.\n\n"
+                    "For help:\n"
+                    "  C-h k     Describe a key binding\n"
+                    "  C-h f     Describe a function\n"
+                    "  M-x cheat-sheet    Quick reference\n"
+                    "  M-x describe-bindings   All key bindings\n"
+                    "  M-x apropos-command    Search commands\n"
+                    "  M-x tutorial-mode      Interactive tutorial\n\n"
+                    "For GNU Emacs manual: https://www.gnu.org/software/emacs/manual/\n")))
+    (editor-set-text ed text)
+    (echo-message! echo "jemacs manual")))
+
+;; cmd-view-echo-area-messages: Show recent echo area messages
+(def (cmd-view-echo-area-messages app)
+  (let* ((echo (app-state-echo app))
+         (buf (app-state-current-buffer app))
+         (ed (buffer-editor buf)))
+    (editor-set-text ed (str "=== *Messages* ===\n\n"
+                             "(Echo area message history is not persisted yet.\n"
+                             " Recent messages appear in the echo area at bottom.)\n"))
+    (echo-message! echo "Messages buffer")))
+
+;; cmd-toggle-debug-on-error: Toggle debug-on-error mode
+(def (cmd-toggle-debug-on-error app)
+  (let* ((echo (app-state-echo app)))
+    (toggle-mode! app 'debug-on-error)
+    (if (mode-enabled? app 'debug-on-error)
+      (echo-message! echo "Debug on error enabled")
+      (echo-message! echo "Debug on error disabled"))))
+
+;; cmd-toggle-debug-on-quit: Toggle debug-on-quit mode
+(def (cmd-toggle-debug-on-quit app)
+  (let* ((echo (app-state-echo app)))
+    (toggle-mode! app 'debug-on-quit)
+    (if (mode-enabled? app 'debug-on-quit)
+      (echo-message! echo "Debug on quit enabled")
+      (echo-message! echo "Debug on quit disabled"))))
+
+;; cmd-profiler-start: Start the CPU profiler
+(def (cmd-profiler-start app)
+  (let* ((echo (app-state-echo app)))
+    (hash-put! (app-state-modes app) 'profiler-start-time (time-second (current-time)))
+    (echo-message! echo "Profiler started")))
+
diff --git a/src/jerboa-emacs/editor-extra-regs2.ss b/src/jerboa-emacs/editor-extra-regs2.ss
index a04d808..8c62e36 100644
--- a/src/jerboa-emacs/editor-extra-regs2.ss
+++ b/src/jerboa-emacs/editor-extra-regs2.ss
@@ -2052,4 +2052,25 @@
   (register-command! 'describe-key-briefly cmd-describe-key-briefly)
   (register-command! 'describe-bindings cmd-describe-bindings)
   (register-command! 'describe-mode cmd-describe-mode)
+  ;; Round 29
+  (register-command! 'describe-face cmd-describe-face)
+  (register-command! 'describe-char cmd-describe-char)
+  (register-command! 'describe-syntax cmd-describe-syntax)
+  (register-command! 'describe-categories cmd-describe-categories)
+  (register-command! 'apropos-command cmd-apropos-command)
+  (register-command! 'info-emacs-manual cmd-info-emacs-manual)
+  (register-command! 'view-echo-area-messages cmd-view-echo-area-messages)
+  (register-command! 'toggle-debug-on-error cmd-toggle-debug-on-error)
+  (register-command! 'toggle-debug-on-quit cmd-toggle-debug-on-quit)
+  (register-command! 'profiler-start cmd-profiler-start)
+  (register-command! 'profiler-stop cmd-profiler-stop)
+  (register-command! 'profiler-report cmd-profiler-report)
+  (register-command! 'memory-report cmd-memory-report)
+  (register-command! 'emacs-uptime cmd-emacs-uptime)
+  (register-command! 'emacs-version cmd-emacs-version)
+  (register-command! 'emacs-init-time cmd-emacs-init-time)
+  (register-command! 'list-packages cmd-list-packages)
+  (register-command! 'package-install cmd-package-install)
+  (register-command! 'package-delete cmd-package-delete)
+  (register-command! 'package-refresh-contents cmd-package-refresh-contents)
 )