summaryrefslogtreecommitdiff
path: root/.config/emacs
diff options
context:
space:
mode:
authorThomas Voss <mail@thomasvoss.com> 2025-01-04 23:48:10 +0100
committerThomas Voss <mail@thomasvoss.com> 2025-01-04 23:48:10 +0100
commitf93579ccddf3f3f8a5a50280dd2722fee8f71f3d (patch)
treea43c15fcae3448e94acd00a597cc216645e2fa6e /.config/emacs
parent9cc8020ab02c50f383fdb53fe1915199546f2aba (diff)
emacs: Introduce number-format-mode.el
Diffstat (limited to '.config/emacs')
-rw-r--r--.config/emacs/modules/mm-editing.el8
-rw-r--r--.config/emacs/site-lisp/number-format-mode.el129
2 files changed, 137 insertions, 0 deletions
diff --git a/.config/emacs/modules/mm-editing.el b/.config/emacs/modules/mm-editing.el
index 2bc71e5..c6c8532 100644
--- a/.config/emacs/modules/mm-editing.el
+++ b/.config/emacs/modules/mm-editing.el
@@ -310,6 +310,14 @@ is as described by `emmet-expand-line'."
(emmet-self-closing-tag-style ""))
+;;; Number Formatting
+
+(use-package number-format-mode
+ :commands ( number-format-buffer number-format-region
+ number-unformat-buffer number-unformat-region
+ number-format-mode))
+
+
;;; Additional Major Modes
(use-package awk-ts-mode :ensure t)
diff --git a/.config/emacs/site-lisp/number-format-mode.el b/.config/emacs/site-lisp/number-format-mode.el
new file mode 100644
index 0000000..cbc5937
--- /dev/null
+++ b/.config/emacs/site-lisp/number-format-mode.el
@@ -0,0 +1,129 @@
+;;; number-format-mode.el --- Format numbers in the current buffer -*- lexical-binding: t; -*-
+
+(eval-when-compile
+ (require 'cl-macs)
+ (require 'seq))
+
+(defgroup number-format nil
+ "Customization group for `number-format'."
+ :group 'convenience) ; TODO: Is this the right group?
+
+(defcustom number-format-separator "."
+ "Thousands separator to use in numeric literals."
+ :type 'string
+ :package-version '(number-format-mode . "1.0.0")
+ :group 'number-format)
+
+(defcustom number-format-predicate nil
+ "Function determining if a number should be formatted.
+When formatting a number, this function is called with the START and END
+range of the number in the buffer. If this function returns non-nil the
+number is formatted.
+
+If this function is nil then all numbers are formatted."
+ :type 'function
+ :package-version '(number-format-mode . "1.0.0")
+ :group 'number-format)
+
+(defvar-local number-format--overlays (make-hash-table :test 'eq))
+(defconst number-format--regexp "\\b[0-9]\\{4,\\}\\b")
+
+(defun number-format--add-separators (s)
+ (while (string-match "\\(.*[0-9]\\)\\([0-9][0-9][0-9].*\\)" s)
+ (setq s (concat (match-string 1 s)
+ number-format-separator
+ (match-string 2 s))))
+ s)
+
+(defun number-format--adjust-overlays (ov _1 beg end &optional _2)
+ (let* ((ov-beg (overlay-start ov))
+ (ov-end (overlay-end ov))
+ (overlays (overlays-in ov-beg ov-end)))
+ (mapcar #'delete-overlay (gethash ov number-format--overlays))
+ (save-excursion
+ (goto-char ov-beg)
+ (if (looking-at number-format--regexp :inhibit-modify)
+ (puthash ov (number-format--at-range ov-beg ov-end)
+ number-format--overlays)
+ (delete-overlay ov)
+ (remhash ov number-format--overlays)))))
+
+(defun number-format--at-range (beg end)
+ (when (or (null number-format-predicate)
+ (funcall number-format-predicate beg end))
+ (let* ((offsets [3 1 2])
+ (len (- end beg))
+ (off (aref offsets (mod len 3))))
+ (goto-char (+ beg off)))
+ (let (overlays)
+ (while (< (point) end)
+ (let* ((group-end (+ (point) 3))
+ (ov (make-overlay (point) group-end)))
+ (overlay-put ov 'before-string ".")
+ (overlay-put ov 'evaporate t)
+ (push ov overlays)
+ (goto-char group-end)))
+ overlays)))
+
+(defun number-format--jit-lock (beg end)
+ (let ((line-beg (save-excursion (goto-char beg) (line-beginning-position)))
+ (line-end (save-excursion (goto-char end) (line-end-position))))
+ (number-unformat-region line-beg line-end)
+ (number-format-region line-beg line-end)))
+
+;;;###autoload
+(defun number-format-region (beg end)
+ "Format numbers between BEG and END.
+When called interactively, format numbers in the active region."
+ (interactive "r")
+ (save-excursion
+ (goto-char beg)
+ (save-restriction
+ (narrow-to-region beg end)
+ (number-unformat-region beg end)
+ (while (re-search-forward number-format--regexp nil :noerror)
+ (save-excursion
+ (cl-destructuring-bind (beg end) (match-data)
+ (let ((ov (make-overlay beg end nil nil :rear-advance)))
+ (overlay-put ov 'evaporate t)
+ (dolist (sym '(insert-behind-hooks
+ insert-in-front-hooks
+ modification-hooks))
+ (overlay-put ov sym '(number-format--adjust-overlays)))
+ (puthash ov (number-format--at-range beg end)
+ number-format--overlays))))))))
+
+;;;###autoload
+(defun number-unformat-region (beg end)
+ "Unformat numbers between BEG and END.
+When called interactively, unformat numbers in the active region."
+ (interactive "r")
+ (dolist (ov (overlays-in beg end))
+ (when-let ((overlays (gethash ov number-format--overlays)))
+ (mapcar #'delete-overlay overlays)
+ (remhash ov number-format--overlays)
+ (delete-overlay ov))))
+
+;;;###autoload
+(defun number-format-buffer ()
+ "Format numbers in the current buffer."
+ (interactive)
+ (number-format-region (point-min) (point-max)))
+
+;;;###autoload
+(defun number-unformat-buffer ()
+ "Unformat numbers in the current buffer."
+ (interactive)
+ (number-unformat-region (point-min) (point-max)))
+
+;;;###autoload
+(define-minor-mode number-format-mode
+ "TODO"
+ :lighter " Number-Format"
+ :group 'number-format
+ (number-unformat-buffer)
+ (if number-format-mode
+ (jit-lock-register #'number-format--jit-lock)
+ (jit-lock-unregister #'number-format--jit-lock)))
+
+(provide 'number-format) \ No newline at end of file