diff options
Diffstat (limited to 'lib/prog')
| -rw-r--r-- | lib/prog/jao-clojure.el | 192 | ||||
| -rw-r--r-- | lib/prog/jao-compilation.el | 10 | ||||
| -rw-r--r-- | lib/prog/jao-rust.el | 142 | ||||
| -rw-r--r-- | lib/prog/jao-sloc.el | 2 | ||||
| -rw-r--r-- | lib/prog/jao-vterm-repl.el | 130 |
5 files changed, 340 insertions, 136 deletions
diff --git a/lib/prog/jao-clojure.el b/lib/prog/jao-clojure.el new file mode 100644 index 0000000..7de62ba --- /dev/null +++ b/lib/prog/jao-clojure.el @@ -0,0 +1,192 @@ +;;; jao-clojure.el --- Clojure utilities -*- lexical-binding: t; -*- + +;; Copyright (C) 2025, 2026 Jose Antonio Ortega Ruiz + +;; Author: Jose Antonio Ortega Ruiz <mail@jao.io> +;; Keywords: languages + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Helpers for clojure coding + +;;; Code: + +(require 'clojure-mode) +(require 'project) +(require 'cider-test) +(require 'jao-skel) +(require 'jao-compilation) + +;;;; Jumping between implementation and test files +(defun jao-clojure--ext-dir (prefix) + (let* ((ext (file-name-extension buffer-file-name)) + (ext-rx (format "/%s/" ext))) + (if (string-match-p ext-rx buffer-file-name) + (format "%s/%s" prefix ext) + prefix))) + +(defun jao-clojure-find-current-test () + (save-excursion + (and (re-search-backward + "(deftest\\(?:\\W+^:\\w+\\)*\\W+\\b\\(.+\\)\\b" nil t) + (match-string-no-properties 1)))) + +(defvar jao-clojure--src-candidates '("lib" "src" "srv" "app")) + +(defun jao-clojure--test-namespace-p (ns) + (or (string-suffix-p "-test" ns) + (string-match "\\(.+\\)\\.\\(test\\)\\(\\..+\\)" ns))) + +(defun jao-clojure-test-buffer-p () + (jao-clojure--test-namespace-p (clojure-find-ns))) + +(defun jao-clojure--test-for (namespace sep) + (replace-regexp-in-string "\\." sep + (cider-test-default-test-ns-fn namespace))) + +(defun jao-clojure--infer-test-ns (ns) + (if (jao-clojure--test-namespace-p ns) + ns + (jao-clojure--test-for ns "."))) + +(defun jao-clojure--root () (project-root (project-current))) + +(defun jao-clojure--to-fname (x) + (replace-regexp-in-string "-" "_" x)) + +(defun jao-clojure-jump-to-test () + "Jump from implementation to test file." + (interactive) + (let* ((tn (jao-clojure--test-for (clojure-find-ns) "/")) + (bn (file-name-extension buffer-file-name)) + (f (format "%s%s/%s.%s" + (jao-clojure--root) + "test" + (jao-clojure--to-fname tn) + (jao-clojure--to-fname bn)))) + (find-file f))) + +(defun jao-clojure--implementation-for (namespace) + (thread-last (replace-regexp-in-string "-test$" "" namespace) + (replace-regexp-in-string "\\.test\\." ".") + (replace-regexp-in-string "-" "_") + (replace-regexp-in-string "\\." "/") + (substring-no-properties))) + +(defun jao-clojure--find-implementation (src) + (let ((f (format "%s%s/%s.%s" + (jao-clojure--root) + src + (jao-clojure--implementation-for (clojure-find-ns)) + (file-name-extension buffer-file-name)))) + (and (file-exists-p f) f))) + +(defun jao-clojure-jump-to-implementation () + "Jump from test file to implementation." + (interactive) + (let ((impl (car (seq-keep #'jao-clojure--find-implementation + jao-clojure--src-candidates)))) + (if impl (find-file impl) (message "No implementation file found")))) + +(defun jao-clojure-other-file () + "Toggle between implementation and test file" + (interactive) + (if (jao-clojure-test-buffer-p) + (jao-clojure-jump-to-implementation) + (jao-clojure-jump-to-test))) + +(defun jao-clojure--setup-compilation (&optional _ns) + ;; (set (make-local-variable 'compile-command) (jao-clojure--test-str ns)) + ) + +;;;; Skeletons +(defconst jao-clojure--ns-destruct-rx + (format "\\(?:%s\\|tests?\\)\\.\\(?:clj[cs]?\\.\\)?\\(.+\\)" + (regexp-opt jao-clojure--src-candidates))) + +(defun jao-clojure-buffer-namespace () + (let* ((ddir (jao-compilation-root)) + (mbase (and ddir + (concat (replace-regexp-in-string "/" "." ddir) "."))) + (mbase (and mbase + (string-match jao-clojure--ns-destruct-rx mbase) + (match-string 1 mbase)))) + (concat (or mbase "") + (replace-regexp-in-string "_" "-" (jao-skel-basename))))) + +(defvar jao-clojure--test-check-lines + (concat "[clojure.test.check :as tc]\n " + "[clojure.test.check.generators :as gen]\n " + "[clojure.test.check.properties :as prop :include-macros true]\n")) + +(defun jao-clojure--cljs-test-reqs (prefix-cmp last-cmp _test-check) + (concat " (:require [cljs.test :as t :refer-macros [is deftest async]]" + "\n [" prefix-cmp "." last-cmp " :as " last-cmp "])")) + +(defun jao-clojure--clj-test-reqs (prefix-cmp last-cmp test-check) + (format "(:use clojure.test)\n (:require %s(%s [%s :as %s]))" + (if test-check + (concat "(clojure.test.check [clojure-test :refer [defspec]])\n" + jao-clojure--test-check-lines) + "") + prefix-cmp last-cmp last-cmp)) + +(defun jao-clojure--cljc-test-reqs (prefix-cmp last-cmp test-check) + (concat "(:require #?(:clj [clojure.test :as t :refer [is deftest]]\n" + " :cljs [cljs.test :as t :refer-macros [is deftest]])\n" + (when test-check + (concat " [clojure.test.check-clojure-test #?@(" + ":cljs [:refer-macros [defspec]]\n" + ":clj [:refer [defspec]))]\n" + jao-clojure--test-check-lines)) + " [" prefix-cmp "." last-cmp " :as " last-cmp "])")) + +(defun jao-clojure--skel-ns-contents (ns) + (if (jao-clojure--test-namespace-p ns) + (let ((test-check (y-or-n-p "Include test.check requires? ")) + (ns (concat (match-string 1 ns) (match-string 3 ns)))) + (let* ((cmps (split-string ns "\\.")) + (last-cmp (car (last cmps))) + (prefix-cmp (mapconcat 'identity (butlast cmps) ".")) + (ext (file-name-extension buffer-file-name))) + (cond ((string= "cljs" ext) + (jao-clojure--cljs-test-reqs prefix-cmp last-cmp test-check)) + ((string= "cljc" ext) + (jao-clojure--cljc-test-reqs prefix-cmp last-cmp test-check)) + ((string= "clj" ext) + (jao-clojure--clj-test-reqs prefix-cmp last-cmp test-check))))) + (format "%S" (read-string "Brief module description: ")))) + +(define-skeleton jao-clojure-skeleton + "Standard Clojure module file skeleton" + "" + (jao-skel-copyright-line ";; ") + \n + (jao-skel-author-line ";; Author: ") + (jao-skel-date-line ";; Start date: ") + \n '(setq v1 (jao-clojure-buffer-namespace)) + _ "(ns " v1 + '(jao-clojure--setup-compilation v1) + \n (jao-clojure--skel-ns-contents v1) ")" + \n + '(ignore-errors (indent-region (region-beginning) (region-end))) + > -) + +(jao-skel-install "\\.clj[sc]?$" 'jao-clojure-skeleton) + + +(provide 'jao-clojure) +;;; jao-clojure.el ends here diff --git a/lib/prog/jao-compilation.el b/lib/prog/jao-compilation.el index 5bea68e..42332cd 100644 --- a/lib/prog/jao-compilation.el +++ b/lib/prog/jao-compilation.el @@ -1,6 +1,6 @@ ;;; jao-compilation.el --- utilities to lauch compilations -*- lexical-binding: t; -*- -;; Copyright (C) 2020, 2021, 2022 jao +;; Copyright (C) 2020, 2021, 2022, 2025, 2026 jao ;; Author: jao <mail@jao.io> ;; Keywords: convenience @@ -33,7 +33,7 @@ (let (result) (dolist (v jao-compilation-environment result) (let ((vv (getenv v))) - (when vv (add-to-list 'result (format "%s=%s" v vv))))))) + (when vv (push (format "%s=%s" v vv) result)))))) ;;;###autoload (defun jao-compilation-add-dominating (&rest fs) @@ -68,7 +68,7 @@ ;;;###autoload (defun jao-compilation-root (&optional dir) (when-let* ((rfn (jao-compilation-find-root (or dir (buffer-file-name)) - jao-compilation-dominating-rx))) + jao-compilation-dominating-rx))) (let* ((default-directory (expand-file-name rfn)) (dir (file-name-directory rfn)) (rel-path (jao-path-relative-to dir default-directory))) @@ -108,8 +108,8 @@ ;;;###autoload (defun jao-compilation-setup () (jao-compilation-add-dominating - "Makefile" "makefile" "configure.ac" "bootstrap.sh" "aclocal.m4" - "project.clj" "build.xml" "pom.xml" "setup.py" "stack.yaml") + "Makefile" "makefile" "configure.ac" "Cargo.toml" + "deps.edn" "project.clj" "build.xml" "pom.xml" "setup.py" "stack.yaml") (with-eval-after-load "project" (add-to-list 'project-find-functions #'jao-find-compilation-root t))) diff --git a/lib/prog/jao-rust.el b/lib/prog/jao-rust.el new file mode 100644 index 0000000..53b0058 --- /dev/null +++ b/lib/prog/jao-rust.el @@ -0,0 +1,142 @@ +;;; jao-rust.el --- Utilities for rust programming -*- lexical-binding: t; -*- + +;; Copyright (C) 2026 Jose Ruiz + +;; Author: Jose Ruiz <mail@jao.io> +;; Keywords: languages + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Code: + +(declare-function rustic-cargo-bin "rustic") +(declare-function rustic-run-cargo-command "rustic") +(defvar rustic-cargo-test-runner) +(defvar rustic-cargo-use-last-stored-arguments) +(defvar rustic-default-test-arguments) +(defvar rustic-test-arguments) + +(defun jao-rust--root () + (locate-dominating-file default-directory "Cargo.toml")) + +(defun jao-rust-open-cargo () + (interactive) + (let ((f (expand-file-name "Cargo.toml" (jao-rust--root)))) + (when (or (file-exists-p f) + (y-or-n-p "No Cargo.toml found. Create? ")) + (find-file f)))) + +(defun jao-rust-current-module-name () + (let* ((root (expand-file-name (jao-rust--root))) + (current (file-name-sans-extension (buffer-file-name))) + (rel (string-replace root "" current))) + (and (string-prefix-p "src/" rel) + (thread-last + rel + (replace-regexp-in-string "^src\\(/bin\\)?/" "") + (string-replace "-" "_") + (replace-regexp-in-string "\\(^\\|/\\)\\(mod\\|lib\\|main\\)$" "") + (string-replace "/" "::"))))) + +(defun jao-rust--tests-prefix () + (let ((m (jao-rust-current-module-name))) + (cond ((not (stringp m)) "") + ((string-empty-p m) "tests::") + (t (concat m "::tests::"))))) + +(defconst jao-rust--test-header-rx + (concat "#\\[" (regexp-opt '("test" "quickcheck" "tokio::test") t))) + +(defconst jao-rust--test-name-rx "^ *\\(pub \\)?\\( async \\)?fn \\([^(]+\\)(") + +(defun jao-rust-test-at-point () + (when (looking-at jao-rust--test-name-rx) (match-string-no-properties 3))) + +(defun jao-rust-test-before-point () + (save-excursion + (when (re-search-backward jao-rust--test-header-rx nil t) + (forward-line) + (jao-rust-test-at-point)))) + + +(defconst jao-rust--tests-mod-rx "^\\(pub\\)? *mod tests {") + +(defun jao-rust-goto-tests () + (interactive) + (or (re-search-forward jao-rust--tests-mod-rx nil t) + (re-search-backward jao-rust--tests-mod-rx nil t) + (progn + (goto-char (point-min)) + (re-search-forward jao-rust--test-header-rx nil t)) + (when (y-or-n-p "No tests module. Add it?") + (goto-char (point-max)) + (insert "\n#[cfg(test)]\nmod tests {\n\n}") + (forward-line -1)))) + +(defun jao-rust-test-current () + (interactive) + (if-let* ((m (jao-rust--tests-prefix)) + (n (jao-rust-test-before-point))) + (let ((rustic-test-arguments (format "-- --exact %s%s" m n)) + (rustic-cargo-use-last-stored-arguments t)) + (rustic-cargo-test)) + (message "No test before point"))) + +(defun jao-rust-buffer-tests () + (save-excursion + (goto-char (point-min)) + (let ((res '())) + (while (re-search-forward jao-rust--test-header-rx nil t) + (forward-line) + (when-let* ((name (jao-rust-test-at-point))) (push name res))) + res))) + +(defun jao-rust-test-module () + (interactive) + (let ((tests (jao-rust-buffer-tests))) + (if (seq-empty-p tests) + (when (y-or-n-p "No tests in this module. Run doc tests?") + (jao-rust-test-doctests)) + (let* ((prefix (jao-rust--tests-prefix)) + (tests (mapconcat (lambda (x) (format "%s%s" prefix x)) tests " ")) + (rustic-test-arguments (format "-- --exact %s" tests)) + (rustic-cargo-use-last-stored-arguments t)) + (rustic-cargo-test))))) + +(defun jao-rust-test-doctests (&optional all) + (interactive "P") + (let* ((m (if all "" (jao-rust-current-module-name))) + (rustic-cargo-test-runner 'cargo) + (rustic-test-arguments (format "--doc %s" m)) + (rustic-cargo-use-last-stored-arguments t)) + (rustic-cargo-test))) + +(defun jao-rust-retest () + (interactive) + (let* ((rustic-test-arguments "-R latest") + (rustic-cargo-use-last-stored-arguments t)) + (rustic-cargo-test))) + +(defun jao-rust-test-workspace () + (interactive) + (let ((rustic-test-arguments rustic-default-test-arguments)) + (rustic-cargo-test))) + + +(defun jao-rust-build-doc (deps) + (interactive "P") + (rustic-run-cargo-command `(,(rustic-cargo-bin) "doc" ,@(unless deps '("--no-deps"))))) + +(provide 'jao-rust) +;;; jao-rust.el ends here diff --git a/lib/prog/jao-sloc.el b/lib/prog/jao-sloc.el index 1f0e9ab..a325871 100644 --- a/lib/prog/jao-sloc.el +++ b/lib/prog/jao-sloc.el @@ -1,4 +1,4 @@ -;; sloc.el -- LOC utilities +;; sloc.el -- LOC utilities -*- lexical-binding: t; -*- ;;;###autoload (defun count-sloc-region (beg end kind) diff --git a/lib/prog/jao-vterm-repl.el b/lib/prog/jao-vterm-repl.el deleted file mode 100644 index 699ff39..0000000 --- a/lib/prog/jao-vterm-repl.el +++ /dev/null @@ -1,130 +0,0 @@ -;;; jao-vterm-repl.el --- vterm-based repls -*- lexical-binding: t; -*- - -;; Copyright (C) 2020, 2021 jao - -;; Author: jao <mail@jao.io> -;; Keywords: terminals - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <https://www.gnu.org/licenses/>. - -;;; Commentary: - -;; Helpers to launch reply things such as erlang shells inside a vterm. -;; For instance, to declare an erl repl for rebar projects, one would call: -;; -;; (jao-vterm-repl-register "rebar.config" "rebar3 shell" "^[0-9]+> ") - -;;; Code: - -(require 'jao-compilation) - -(declare-function 'vterm-copy-mode "vterm") -(declare-function 'vterm-send-string "vterm") -(declare-function 'vterm-send-return "vterm") - -(defun jao-vterm-repl--buffer-name (&optional dir) - (format "*vterm -- repl - %s*" (or dir (jao-compilation-root)))) - -(defvar jao-vterm-repl-repls nil) -(defvar jao-vterm-repl-prompts nil) -(defvar-local jao-vterm-repl--name nil) -(defvar-local jao-vterm-repl--last-buffer nil) -(defvar-local jao-vterm-repl--prompt-rx "^[0-9]+> ") - -(setq vterm-buffer-name-string nil) - -(defun jao-vterm-repl--exec (cmd &optional name) - (vterm name) - (when name - (vterm-send-string "unset PROMPT_COMMAND\n\n")) - (vterm-send-string cmd) - (vterm-send-return) - (when name (rename-buffer name t))) - -;;;###autoload -(defun jao-vterm-repl-previous-prompt () - (interactive) - (when (derived-mode-p 'vterm-mode) - (vterm-copy-mode 1) - (forward-line 0) - (when (re-search-backward jao-vterm-repl--prompt-rx nil t) - (goto-char (match-end 0))))) - -;;;###autoload -(defun jao-vterm-repl-next-prompt () - (interactive) - (when (derived-mode-p 'vterm-mode) - (vterm-copy-mode 1) - (or (re-search-forward jao-vterm-repl--prompt-rx nil t) - (vterm-copy-mode -1)) - (unless (save-excursion - (re-search-forward jao-vterm-repl--prompt-rx nil t)) - (vterm-copy-mode -1)))) - -;;;###autoload -(define-minor-mode jao-vterm-repl-mode "repl-aware vterm" nil nil - '(("\C-c\C-p" . jao-vterm-repl-previous-prompt) - ("\C-c\C-n" . jao-vterm-repl-next-prompt) - ("\C-c\C-z" . jao-vterm-repl-pop-to-src))) - -;;;###autoload -(defun jao-vterm-repl () - (let* ((dir (jao-compilation-root)) - (vname (jao-vterm-repl--buffer-name dir)) - (root-name (jao-compilation-root-file)) - (buffer (seq-find `(lambda (b) - (string= - (buffer-local-value 'jao-vterm-repl--name - b) - ,vname)) - (buffer-list)))) - (or buffer - (let ((default-directory dir) - (prompt (cdr (assoc root-name jao-vterm-repl-prompts))) - (cmd (or (cdr (assoc root-name jao-vterm-repl-repls)) - (read-string "REPL command: "))) - (bname (format "* vrepl - %s/%s *" - (file-name-base (string-remove-suffix "/" dir)) - root-name))) - (jao-vterm-repl--exec cmd bname) - (jao-vterm-repl-mode) - (setq-local jao-vterm-repl--name vname) - (when prompt (setq-local jao-vterm-repl--prompt-rx prompt)) - (current-buffer))))) - -;;;###autoload -(defun jao-vterm-repl-register (build-file repl-cmd prompt-rx) - (jao-compilation-add-dominating build-file) - (add-to-list 'jao-vterm-repl-repls (cons build-file repl-cmd)) - (add-to-list 'jao-vterm-repl-prompts (cons build-file prompt-rx))) - -;;;###autoload -(defun jao-vterm-repl-pop-to-repl () - (interactive) - (let ((bn (current-buffer))) - (pop-to-buffer (jao-vterm-repl)) - (setq-local jao-vterm-repl--last-buffer bn))) - -;;;###autoload -(defun jao-vterm-repl-pop-to-src () - (interactive) - (when (buffer-live-p jao-vterm-repl--last-buffer) - (pop-to-buffer jao-vterm-repl--last-buffer))) - -;;;###autoload -(defun jao-vterm-repl-send (cmd) - (with-current-buffer (jao-vterm-repl) (vterm-send-string cmd))) - -(provide 'jao-vterm-repl) -;;; jao-vterm-repl.el ends here |
