Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 65 additions & 5 deletions pi-coding-agent-grammars.el
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,56 @@ Each entry is (LANG URL REVISION [SOURCE-DIR]).")
(seq-filter #'treesit-language-available-p
pi-coding-agent--optional-grammars))

(defconst pi-coding-agent--markdown-table-smoke-text
"| a | b |\n| - | - |\n| 1 | 2 |\n"
"Small table used to check Markdown grammar compatibility.")

(defconst pi-coding-agent--markdown-table-smoke-query
'((pipe_table) @table
(pipe_table_cell) @cell
(pipe_table_row) @row)
"Markdown grammar query required by chat table rendering.")

(defun pi-coding-agent--markdown-grammar-compatible-p ()
"Return non-nil when the loaded Markdown grammar supports chat rendering."
(condition-case nil
(with-temp-buffer
(insert pi-coding-agent--markdown-table-smoke-text)
(let* ((parser (treesit-parser-create 'markdown))
(root (treesit-parser-root-node parser))
(captures (treesit-query-capture
root pi-coding-agent--markdown-table-smoke-query)))
(and (assq 'table captures)
(assq 'cell captures)
(assq 'row captures)
t)))
(error nil)))

(defun pi-coding-agent--markdown-grammar-incompatible-p ()
"Return non-nil when an installed Markdown grammar is incompatible."
(and (treesit-language-available-p 'markdown)
(not (pi-coding-agent--markdown-grammar-compatible-p))))

(defun pi-coding-agent--incompatible-markdown-grammar-message ()
"Return the user-facing Markdown grammar compatibility warning."
(format "Incompatible Markdown tree-sitter grammar version loaded. Remove\
old/system libtree-sitter-markdown from `treesit-extra-load-path', %s, or\
system packages, then restart Emacs or run M-x pi-coding-agent-install-grammars."
(abbreviate-file-name (locate-user-emacs-file "tree-sitter"))))

(defvar pi-coding-agent--markdown-grammar-warning-done nil
"Non-nil once the incompatible Markdown grammar warning was shown.")

(defun pi-coding-agent--maybe-warn-incompatible-markdown-grammar ()
"Warn once when the loaded Markdown tree-sitter grammar is incompatible."
(unless (or noninteractive pi-coding-agent--markdown-grammar-warning-done)
(when (pi-coding-agent--markdown-grammar-incompatible-p)
(setq pi-coding-agent--markdown-grammar-warning-done t)
(display-warning
'pi-coding-agent
(pi-coding-agent--incompatible-markdown-grammar-message)
:warning))))

;;;; Installation

(defun pi-coding-agent--install-grammars (grammars)
Expand All @@ -146,6 +196,7 @@ A C compiler (gcc or cc) is required.\n\
(setq idx (1- idx))))
(when (> idx 0)
(message "[pi-coding-agent] Installed %d/%d grammars." idx total))
(setq pi-coding-agent--markdown-grammar-warning-done nil)
idx))

(defcustom pi-coding-agent-essential-grammar-action 'prompt
Expand Down Expand Up @@ -251,10 +302,19 @@ then offers to install the missing ones."
(interactive)
(let ((installed (pi-coding-agent--installed-optional-grammars))
(missing (pi-coding-agent--missing-optional-grammars))
(essential-missing (pi-coding-agent--missing-essential-grammars)))
(if (and (null missing) (null essential-missing))
(message "All %d tree-sitter grammars are installed. ✓"
(length installed))
(essential-missing (pi-coding-agent--missing-essential-grammars))
(markdown-incompatible
(pi-coding-agent--markdown-grammar-incompatible-p)))
(cond
(markdown-incompatible
(display-warning
'pi-coding-agent
(pi-coding-agent--incompatible-markdown-grammar-message)
:warning))
((and (null missing) (null essential-missing))
(message "All %d tree-sitter grammars are installed. ✓"
(length installed)))
(t
(let ((buf (get-buffer-create "*pi-coding-agent-grammars*")))
(with-current-buffer buf
(let ((inhibit-read-only t))
Expand Down Expand Up @@ -286,7 +346,7 @@ then offers to install the missing ones."
(pi-coding-agent-install-grammars)))))
(local-set-key "q" #'quit-window)
(goto-char (point-min)))
(pop-to-buffer buf)))))
(pop-to-buffer buf))))))

(provide 'pi-coding-agent-grammars)
;;; pi-coding-agent-grammars.el ends here
1 change: 1 addition & 0 deletions pi-coding-agent-ui.el
Original file line number Diff line number Diff line change
Expand Up @@ -1597,6 +1597,7 @@ Displays warnings for missing dependencies."
(pi-coding-agent--pi-install-command))
:error))
(pi-coding-agent--maybe-install-essential-grammars)
(pi-coding-agent--maybe-warn-incompatible-markdown-grammar)
(pi-coding-agent--maybe-install-optional-grammars))

;;;; Startup Header
Expand Down
61 changes: 60 additions & 1 deletion test/pi-coding-agent-ui-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,39 @@ Buffer is read-only with `inhibit-read-only' used for insertion.
(pi-coding-agent--maybe-install-essential-grammars)
(should-not installed))))

;;; Markdown Grammar Compatibility

(ert-deftest pi-coding-agent-test-incompatible-markdown-grammar-detected ()
"Detect an installed Markdown grammar that lacks required table nodes."
(cl-letf (((symbol-function 'treesit-language-available-p)
(lambda (_lang &rest _) t))
((symbol-function 'pi-coding-agent--markdown-grammar-compatible-p)
(lambda () nil)))
(should (pi-coding-agent--markdown-grammar-incompatible-p))))

(ert-deftest pi-coding-agent-test-missing-markdown-grammar-not-incompatible ()
"Missing Markdown grammar is handled by the missing-essential path."
(cl-letf (((symbol-function 'treesit-language-available-p)
(lambda (_lang &rest _) nil))
((symbol-function 'pi-coding-agent--markdown-grammar-compatible-p)
(lambda () nil)))
(should-not (pi-coding-agent--markdown-grammar-incompatible-p))))

(ert-deftest pi-coding-agent-test-incompatible-markdown-grammar-warns-once ()
"Warn once when the loaded Markdown grammar is incompatible."
(let ((noninteractive nil)
(pi-coding-agent--markdown-grammar-warning-done nil)
(warnings nil))
(cl-letf (((symbol-function 'pi-coding-agent--markdown-grammar-incompatible-p)
(lambda () t))
((symbol-function 'display-warning)
(lambda (_type msg &rest _) (push msg warnings))))
(pi-coding-agent--maybe-warn-incompatible-markdown-grammar)
(pi-coding-agent--maybe-warn-incompatible-markdown-grammar)
(should (= 1 (length warnings)))
(should (string-match-p "Incompatible Markdown" (car warnings)))
(should (string-match-p "treesit-extra-load-path" (car warnings))))))

;;; Grammar Recipe Validation

(ert-deftest pi-coding-agent-test-grammar-recipes-all-registered ()
Expand Down Expand Up @@ -1504,6 +1537,8 @@ a new grammar (e.g., `zig') appears in the missing set."
(let ((msg nil))
(cl-letf (((symbol-function 'treesit-language-available-p)
(lambda (_lang &rest _) t))
((symbol-function 'pi-coding-agent--markdown-grammar-compatible-p)
(lambda () t))
((symbol-function 'message)
(lambda (fmt &rest args) (setq msg (apply #'format fmt args)))))
(pi-coding-agent-install-grammars)
Expand All @@ -1515,6 +1550,8 @@ a new grammar (e.g., `zig') appears in the missing set."
(cl-letf (((symbol-function 'treesit-language-available-p)
(lambda (lang &rest _)
(memq lang '(markdown markdown-inline python))))
((symbol-function 'pi-coding-agent--markdown-grammar-compatible-p)
(lambda () t))
((symbol-function 'pop-to-buffer)
#'ignore))
(unwind-protect
Expand All @@ -1540,6 +1577,8 @@ a new grammar (e.g., `zig') appears in the missing set."
"Interactive command highlights missing essential grammars prominently."
(cl-letf (((symbol-function 'treesit-language-available-p)
(lambda (_lang &rest _) nil))
((symbol-function 'pi-coding-agent--markdown-grammar-compatible-p)
(lambda () t))
((symbol-function 'pop-to-buffer)
#'ignore))
(unwind-protect
Expand All @@ -1553,6 +1592,22 @@ a new grammar (e.g., `zig') appears in the missing set."
(when-let* ((buf (get-buffer "*pi-coding-agent-grammars*")))
(kill-buffer buf)))))

(ert-deftest pi-coding-agent-test-install-grammars-command-warns-incompatible-markdown ()
"Interactive command warns when an installed Markdown grammar is incompatible."
(let ((warning-text nil)
(msg nil))
(cl-letf (((symbol-function 'treesit-language-available-p)
(lambda (_lang &rest _) t))
((symbol-function 'pi-coding-agent--markdown-grammar-compatible-p)
(lambda () nil))
((symbol-function 'display-warning)
(lambda (_type msg &rest _) (setq warning-text msg)))
((symbol-function 'message)
(lambda (fmt &rest args) (setq msg (apply #'format fmt args)))))
(pi-coding-agent-install-grammars)
(should (string-match-p "Incompatible Markdown" warning-text))
(should-not msg))))

;;; CI Install Script Smoke Test

(ert-deftest pi-coding-agent-test-ci-install-script-loads ()
Expand All @@ -1569,16 +1624,20 @@ Catches wiring bugs like requiring deleted modules."
;;; check-dependencies

(ert-deftest pi-coding-agent-test-check-dependencies-calls-grammar-checks ()
"check-dependencies invokes both grammar check functions."
"check-dependencies invokes grammar install and compatibility checks."
(let ((essential-called nil)
(compatibility-called nil)
(optional-called nil))
(cl-letf (((symbol-function 'pi-coding-agent--check-pi) (lambda () t))
((symbol-function 'pi-coding-agent--maybe-install-essential-grammars)
(lambda () (setq essential-called t)))
((symbol-function 'pi-coding-agent--maybe-warn-incompatible-markdown-grammar)
(lambda () (setq compatibility-called t)))
((symbol-function 'pi-coding-agent--maybe-install-optional-grammars)
(lambda () (setq optional-called t))))
(pi-coding-agent--check-dependencies)
(should essential-called)
(should compatibility-called)
(should optional-called))))

;;; State response
Expand Down
Loading