Round 34: flycheck, flymake, eldoc, which-function, imenu, speedbar, neotree, treemacs, project-find-file, project-switch-to-buffer, project-find-regexp, project-compile, project-switch-project, xref-find-definitions, xref-find-references, xref-pop-marker-stack, xref-go-back, tags-search, tags-query-replace

ober

128200c407ce18d0f95a71e1d72c7dec41fd2c91

diff --git a/docs/jemacs-vs-emacs.md b/docs/jemacs-vs-emacs.md
index e85cd09..afbbb31 100644
--- a/docs/jemacs-vs-emacs.md
+++ b/docs/jemacs-vs-emacs.md
@@ -1786,6 +1786,26 @@ No remaining Tier 1 gaps. All core editing, completion, and navigation features 
 | Set mark command repeat | :orange_circle: | Set mark (repeatable) |
 | Mark defun | :orange_circle: | Select top-level form |
 | Narrow to defun | :orange_circle: | Narrow to top-level form |
+| Flycheck mode | :orange_circle: | On-the-fly syntax checking |
+| Flymake mode | :orange_circle: | Built-in syntax checking |
+| Eldoc mode | :orange_circle: | Show docs in echo area |
+| Which-function mode | :orange_circle: | Show current function name |
+| Imenu | :orange_circle: | Navigate definitions in buffer |
+| Imenu add to menubar | :orange_circle: | Add imenu to menu bar |
+| Speedbar | :orange_circle: | File/code browser (placeholder) |
+| Neotree | :orange_circle: | Tree file browser (placeholder) |
+| Treemacs | :orange_circle: | Project tree browser (placeholder) |
+| Project find file | :orange_circle: | Open file in project |
+| Project switch to buffer | :orange_circle: | Switch project buffer |
+| Project find regexp | :orange_circle: | Grep in project files |
+| Project compile | :orange_circle: | Compile project |
+| Project switch project | :orange_circle: | Switch to another project |
+| Xref find definitions | :orange_circle: | Jump to definition |
+| Xref find references | :orange_circle: | Find all references |
+| Xref pop marker stack | :orange_circle: | Go back in xref history |
+| Xref go back | :orange_circle: | Return from definition |
+| Tags search | :orange_circle: | Search TAGS (placeholder) |
+| Tags query replace | :orange_circle: | Replace in TAGS (placeholder) |
 
 ---
 
diff --git a/src/jerboa-emacs/editor-extra-final.ss b/src/jerboa-emacs/editor-extra-final.ss
index 2a99801..f172467 100644
--- a/src/jerboa-emacs/editor-extra-final.ss
+++ b/src/jerboa-emacs/editor-extra-final.ss
@@ -9383,3 +9383,121 @@
                                     (find-end (+ j 1) (- depth 1))))
                   (else (find-end (+ j 1) depth))))))
           (loop (- i 1)))))))
+
+;; Round 34 batch 2
+
+(def (cmd-project-switch-to-buffer app)
+  (let* ((echo (app-state-echo app))
+         (buffers (app-state-buffers app))
+         (buf-names (map buffer-name buffers))
+         (name (echo-read-string-with-completion echo "Project buffer: " buf-names)))
+    (if (or (not name) (string=? name ""))
+      (echo-message! echo "No buffer specified")
+      (let ((target (find (lambda (b) (string=? (buffer-name b) name)) buffers)))
+        (if target
+          (begin
+            (switch-to-buffer (app-state-frame app) target)
+            (echo-message! echo (str "Switched to: " name)))
+          (echo-message! echo (str "Buffer not found: " name)))))))
+
+(def (cmd-project-find-regexp app)
+  (let* ((echo (app-state-echo app))
+         (buf (app-state-current-buffer app))
+         (ed (buffer-editor buf))
+         (pattern (echo-read-string echo "Project grep: ")))
+    (if (or (not pattern) (string=? pattern ""))
+      (echo-message! echo "No pattern specified")
+      (with-catch
+        (lambda (e) (echo-message! echo (str "Grep error: " e)))
+        (lambda ()
+          (let-values (((si so se pid)
+                        (open-process-ports (str "grep -rn " (shell-quote pattern) " . --include='*.ss' --include='*.el' --include='*.py' --include='*.js' 2>/dev/null | head -50")
+                          'block (native-transcoder))))
+            (close-port si)
+            (let loop ((lines '()))
+              (let ((line (get-line so)))
+                (if (eof-object? line)
+                  (begin
+                    (close-port so) (close-port se)
+                    (let ((result (string-join (reverse lines) "\n")))
+                      (editor-set-text ed (str "=== Project Grep: \"" pattern "\" ===\n\n" result "\n"))
+                      (echo-message! echo (str (length lines) " matches"))))
+                  (loop (cons line lines)))))))))))
+
+(def (cmd-project-compile app)
+  (cmd-compile-command app))
+
+(def (cmd-project-switch-project app)
+  (let* ((echo (app-state-echo app))
+         (dir (echo-read-string echo "Switch to project: ")))
+    (if (or (not dir) (string=? dir ""))
+      (echo-message! echo "No project specified")
+      (echo-message! echo (str "Would switch to project: " dir)))))
+
+(def (cmd-xref-find-definitions 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))
+         (word-start (let loop ((i pos))
+                       (if (or (<= i 0) (not (or (char-alphabetic? (string-ref text (- i 1)))
+                                                   (char=? (string-ref text (- i 1)) #\-))))
+                         i (loop (- i 1)))))
+         (word-end (let loop ((i pos))
+                     (if (or (>= i len) (not (or (char-alphabetic? (string-ref text i))
+                                                   (char=? (string-ref text i) #\-))))
+                       i (loop (+ i 1)))))
+         (symbol (if (= word-start word-end) "" (substring text word-start word-end))))
+    (if (string=? symbol "")
+      (echo-message! echo "No symbol at point")
+      (let ((def-pattern (str "(def (" symbol " "))
+            (def-pattern2 (str "(def " symbol " ")))
+        (let ((idx (or (string-contains text def-pattern)
+                       (string-contains text def-pattern2))))
+          (if idx
+            (begin (editor-set-cursor ed idx) (echo-message! echo (str "Found: " symbol)))
+            (echo-message! echo (str "Definition not found: " symbol))))))))
+
+(def (cmd-xref-find-references 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))
+         (word-start (let loop ((i pos))
+                       (if (or (<= i 0) (not (or (char-alphabetic? (string-ref text (- i 1)))
+                                                   (char=? (string-ref text (- i 1)) #\-))))
+                         i (loop (- i 1)))))
+         (word-end (let loop ((i pos))
+                     (if (or (>= i len) (not (or (char-alphabetic? (string-ref text i))
+                                                   (char=? (string-ref text i) #\-))))
+                       i (loop (+ i 1)))))
+         (symbol (if (= word-start word-end) "" (substring text word-start word-end))))
+    (if (string=? symbol "")
+      (echo-message! echo "No symbol at point")
+      (let* ((count (let loop ((p 0) (n 0))
+                      (let ((idx (string-contains text symbol p)))
+                        (if (not idx) n
+                          (loop (+ idx (string-length symbol)) (+ n 1)))))))
+        (echo-message! echo (str symbol ": " count " references in buffer"))))))
+
+(def (cmd-xref-pop-marker-stack app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Marker stack empty")))
+
+(def (cmd-xref-go-back app)
+  (cmd-xref-pop-marker-stack app))
+
+(def (cmd-tags-search app)
+  (let* ((echo (app-state-echo app))
+         (pattern (echo-read-string echo "Tags search: ")))
+    (if (or (not pattern) (string=? pattern ""))
+      (echo-message! echo "No pattern specified")
+      (echo-message! echo (str "Tags search for \"" pattern "\" (TAGS file not available)")))))
+
+(def (cmd-tags-query-replace app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Tags query-replace (TAGS file not available)")))
diff --git a/src/jerboa-emacs/editor-extra-modes.ss b/src/jerboa-emacs/editor-extra-modes.ss
index 21b5aa3..cc5bf94 100644
--- a/src/jerboa-emacs/editor-extra-modes.ss
+++ b/src/jerboa-emacs/editor-extra-modes.ss
@@ -9916,4 +9916,97 @@
       (echo-message! echo "Transient-mark mode enabled")
       (echo-message! echo "Transient-mark mode disabled"))))
 
+;; Round 34 batch 1
+
+(def (cmd-flycheck-mode app)
+  (let* ((echo (app-state-echo app)))
+    (toggle-mode! app 'flycheck-mode)
+    (if (mode-enabled? app 'flycheck-mode)
+      (echo-message! echo "Flycheck mode enabled (on-the-fly syntax checking)")
+      (echo-message! echo "Flycheck mode disabled"))))
+
+(def (cmd-flymake-mode app)
+  (let* ((echo (app-state-echo app)))
+    (toggle-mode! app 'flymake-mode)
+    (if (mode-enabled? app 'flymake-mode)
+      (echo-message! echo "Flymake mode enabled")
+      (echo-message! echo "Flymake mode disabled"))))
+
+(def (cmd-eldoc-mode app)
+  (let* ((echo (app-state-echo app)))
+    (toggle-mode! app 'eldoc-mode)
+    (if (mode-enabled? app 'eldoc-mode)
+      (echo-message! echo "Eldoc mode enabled (show docs in echo area)")
+      (echo-message! echo "Eldoc mode disabled"))))
+
+(def (cmd-which-function-mode app)
+  (let* ((echo (app-state-echo app)))
+    (toggle-mode! app 'which-function-mode)
+    (if (mode-enabled? app 'which-function-mode)
+      (echo-message! echo "Which-function mode enabled")
+      (echo-message! echo "Which-function mode disabled"))))
+
+(def (cmd-imenu app)
+  (let* ((buf (app-state-current-buffer app))
+         (ed (buffer-editor buf))
+         (echo (app-state-echo app))
+         (text (editor-get-text ed))
+         (lines (string-split text #\newline))
+         (defs '()))
+    ;; Find def/defun/class etc lines
+    (let loop ((ls lines) (n 1))
+      (if (null? ls) #f
+        (begin
+          (let ((line (car ls)))
+            (when (or (string-contains line "(def ")
+                      (string-contains line "(defun ")
+                      (string-contains line "(defstruct ")
+                      (string-contains line "(defclass ")
+                      (string-contains line "function ")
+                      (string-contains line "class "))
+              (set! defs (cons (cons n (string-trim line)) defs))))
+          (loop (cdr ls) (+ n 1)))))
+    (let* ((entries (reverse defs))
+           (result (str "=== Imenu ===\n\n"
+                        (if (null? entries) "No definitions found.\n"
+                          (string-join
+                            (map (lambda (e) (str "  " (number->string (car e)) ": " (cdr e)))
+                                 entries)
+                            "\n"))
+                        "\n")))
+      (editor-set-text ed result)
+      (echo-message! echo (str (length entries) " definitions found")))))
+
+(def (cmd-imenu-add-to-menubar app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Imenu added to menu bar (conceptual)")))
+
+(def (cmd-speedbar app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Speedbar not available (use M-x dired or M-x treemacs)")))
+
+(def (cmd-neotree app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Neotree not available (use M-x dired for file browsing)")))
+
+(def (cmd-treemacs app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Treemacs not available (use M-x dired for file browsing)")))
+
+(def (cmd-project-find-file app)
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (file (echo-read-string echo "Project find file: ")))
+    (if (or (not file) (string=? file ""))
+      (echo-message! echo "No file specified")
+      (if (file-exists? file)
+        (let* ((content (read-file-string file))
+               (new-buf (create-buffer (path-strip-directory file))))
+          (buffer-file-set! new-buf file)
+          (switch-to-buffer frame new-buf)
+          (let ((ed (edit-window-editor (current-window frame))))
+            (editor-set-text ed content))
+          (echo-message! echo (str "Opened: " file)))
+        (echo-message! echo (str "File not found: " file))))))
+
 
diff --git a/src/jerboa-emacs/editor-extra-regs2.ss b/src/jerboa-emacs/editor-extra-regs2.ss
index 804a864..69706ac 100644
--- a/src/jerboa-emacs/editor-extra-regs2.ss
+++ b/src/jerboa-emacs/editor-extra-regs2.ss
@@ -2157,4 +2157,25 @@
   (register-command! 'set-mark-command-repeat cmd-set-mark-command-repeat)
   (register-command! 'mark-defun cmd-mark-defun)
   (register-command! 'narrow-to-defun cmd-narrow-to-defun)
+  ;; Round 34
+  (register-command! 'flycheck-mode cmd-flycheck-mode)
+  (register-command! 'flymake-mode cmd-flymake-mode)
+  (register-command! 'eldoc-mode cmd-eldoc-mode)
+  (register-command! 'which-function-mode cmd-which-function-mode)
+  (register-command! 'imenu cmd-imenu)
+  (register-command! 'imenu-add-to-menubar cmd-imenu-add-to-menubar)
+  (register-command! 'speedbar cmd-speedbar)
+  (register-command! 'neotree cmd-neotree)
+  (register-command! 'treemacs cmd-treemacs)
+  (register-command! 'project-find-file cmd-project-find-file)
+  (register-command! 'project-switch-to-buffer cmd-project-switch-to-buffer)
+  (register-command! 'project-find-regexp cmd-project-find-regexp)
+  (register-command! 'project-compile cmd-project-compile)
+  (register-command! 'project-switch-project cmd-project-switch-project)
+  (register-command! 'xref-find-definitions cmd-xref-find-definitions)
+  (register-command! 'xref-find-references cmd-xref-find-references)
+  (register-command! 'xref-pop-marker-stack cmd-xref-pop-marker-stack)
+  (register-command! 'xref-go-back cmd-xref-go-back)
+  (register-command! 'tags-search cmd-tags-search)
+  (register-command! 'tags-query-replace cmd-tags-query-replace)
 )