;;; auto-version.el --- Automatic x.y.z version -*- lexical-binding: t -*- ;; ;; Author: Emanuel Berg ;; Created: 2024-08-01 ;; Keywords: convenience, lisp, tools ;; License: GPL3+ ;; Package-Requires: ((emacs "25.1")) ;; URL: https://dataswamp.org/~incal/elpa/auto-version.el ;; Version: 1.16.3 ;; ;;; Commentary: ;; ;; __________________________________________________________ ;; ^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^` ;; ;; auto-version.el -- automatic x.y.z version ;; ;; __________________________________________________________ ;; `^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^`^ ;; ;; Automatic version number uptick. ;; ;; The version number looks like this: ;; ;; x.y.z ;; ;; Uptick is by default 20 for x and y. Increments to z are ;; by 1 every update. z resets and increments y; y resets ;; and increments x. x doesn't reset or increment: ;; ;; 0 <= z < 20 ;; 0 <= y < 20 ;; 0 <= x ;; ;; ---------------------------------------------------------- ;; ;; To enable in Emacs Lisp buffers: ;; ;; (keymap-set emacs-lisp-mode-map "C-x C-s" #'auto-version-save) ;; ;; ---------------------------------------------------------- ;; ;; The most important function, see its docstring: ;; `auto-version' ;; ;; Automates version update for every save, all transparent: ;; `auto-version-save' ;; ;; Extra material. How many updates is there in a version? ;; Plus version compare, and more. ;; `auto-version--updates' ;; `auto-version<' ;; ;; ---------------------------------------------------------- ;; ;; If you want to update the version at most once a day set: ;; `auto-version--max-once-a-day' ;; ;; ---------------------------------------------------------- ;; ;;; Code: (require 'cl-lib) (defvar auto-version--max-once-a-day nil "Non-nil and `auto-version' increments version(s) at most once a day.") (defun auto-version-save (&rest arg) "Uptick version and save, see `auto-version'. Also see `auto-version--max-once-a-day'. ARG are passed on to `save-buffer', you can use this just as you did `save-buffer'." (interactive "p") (let ((buffer-undo-list t) (today (format-time-string "%F"))) (if (not auto-version--max-once-a-day) (auto-version) (when (and auto-version--max-once-a-day (not (and (stringp auto-version--max-once-a-day) (string= today auto-version--max-once-a-day)))) (auto-version) (setq auto-version--max-once-a-day today)) )) (save-buffer arg)) (defun auto-version (&optional yz-max label) "Uptick the x.y.z version number if present. y and z overtick at YZ-MAX, default 20. LABEL is prepended to the version search string, it defaults to \"^;; Version: \" in Elisp buffers and otherwise to \"\\\\W\". Also see: `auto-version-save'" (interactive) (or yz-max (setq yz-max 20)) (or label (setq label (if (eq major-mode 'emacs-lisp-mode) "^;; Version: " "\\W"))) (when (buffer-modified-p) (save-mark-and-excursion (goto-char (point-min)) (when (re-search-forward (format "%s\\([0-9]+.[0-9]+.[0-9]+\\)\\b" label) nil t) (pcase-let ((`(,x ,y ,z) (version-to-list (match-string 1)))) (when (zerop (setq z (mod (cl-incf z) yz-max))) (when (zerop (setq y (mod (cl-incf y) yz-max))) (cl-incf x))) (let ((ver (format "%d.%d.%d" x y z))) (replace-match ver nil nil nil 1) ver)))))) (defun auto-version< (ver1 ver2 &optional otick1 otick2) "Do VER1 has less updates than VER2? OTICK1 and OTICK2 defaults to 20 for both." (interactive (list (read-string "version 1 [x.y.z]: ") (read-string "version 2 [a.b.c]: ") (read-number "version 1 overtick" 20) (read-number "version 2 overtick" 20))) (or otick1 (setq otick1 20)) (or otick2 (setq otick2 20)) (let* ((up1 (auto-version--updates ver1 otick1)) (up2 (auto-version--updates ver2 otick2)) (res (< up1 up2))) (prog1 res (message "updates %s: %d [%s]\nupdates %s: %d" ver1 up1 res ver2 up2)))) (defun auto-version--updates (ver &optional overtick) "Translate VER into the number of updates it represents. Overtick is OVERTICK for y and z, default 20. For example, version 0.0.1 is the first update or update number 1; and version 7.4.19 is already at update number 2899. \(+ (* 7 (expt 20 2)) (* 4 (expt 20 1)) (* 19 (expt 20 0)))" (interactive (list (read-string "Version: ") (read-number "Overtick: " 20))) (or overtick (setq overtick 20)) (let* ((vel (version-to-list ver))) (cl-loop with ups = 0 for i downfrom (1- (length vel)) for d in vel do (cl-incf ups (* d (expt overtick i))) finally return ups))) (provide 'auto-version) ;;; auto-version.el ends here