[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/evil-numbers 407d62222e 126/145: Add separator character s
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/evil-numbers 407d62222e 126/145: Add separator character support (issue #23) |
Date: |
Thu, 6 Jan 2022 03:00:24 -0500 (EST) |
branch: elpa/evil-numbers
commit 407d62222ea004cd5812237b240d671115db61f9
Author: Campbell Barton <ideasman42@gmail.com>
Commit: Campbell Barton <ideasman42@gmail.com>
Add separator character support (issue #23)
Optionally support numbers with single separators such as:
- `16_777_216`
- `0x10_000`
- `4,294,967,296`
---
CHANGELOG.org | 2 +
README.org | 7 +++
evil-numbers.el | 189 ++++++++++++++++++++++++++++++++++++++++----------------
3 files changed, 144 insertions(+), 54 deletions(-)
diff --git a/CHANGELOG.org b/CHANGELOG.org
index 247c03b7ca..8d4e7c8fd7 100644
--- a/CHANGELOG.org
+++ b/CHANGELOG.org
@@ -3,6 +3,8 @@
* In Development
** Additions
+ + Add =evil-numbers-separator-chars= option to support separator characters,
+ such as =16_777_216= or =4,294,967,296=.
+ Add =evil-numbers-case= option for the case to use for hexadecimal values
(defaults to the current case).
** Fixes
diff --git a/README.org b/README.org
index 86fe5641c0..f20d9bc716 100644
--- a/README.org
+++ b/README.org
@@ -35,6 +35,13 @@
Set to =t= if you want numbers to be padded with zeros (numbers with a
leading zero are always padded).
If you want both behaviors, all commands take an optional argument
=padded=.
+ - =evil-numbers-separator-chars=
+
+ This option to support separator characters, set to "_" or "," to support
numeric literals such as:
+ =16_777_216= or =4,294,967,296=.
+
+ You may wish to set this as a buffer local variable to enable this only
for languages that support separators.
+
- =evil-numbers-case=
The case to use for hexadecimal numbers.
diff --git a/evil-numbers.el b/evil-numbers.el
index 805013d5b9..e32a644bd8 100644
--- a/evil-numbers.el
+++ b/evil-numbers.el
@@ -80,6 +80,16 @@
:type 'boolean
:options '(nil t))
+(defcustom evil-numbers-separator-chars nil
+ "Support separator characters in numeric literals for visual grouping.
+
+This value is a string containing separator characters,
+typically \"_\" or \",\" which are allowed in numeric literals in some systems.
+
+Otherwise nil will disable this functionality."
+ :group 'evil-numbers
+ :type '(choice (const nil) string))
+
(defcustom evil-numbers-case nil
"Case to use for hexadecimal numbers."
:group 'evil-numbers
@@ -121,6 +131,32 @@
(number-sequence 0 9)))))
+;; ---------------------------------------------------------------------------
+;; Internal String Separator Utilities
+;;
+;; To remove, and restore separators.
+
+(defun evil-numbers--strip-chars (str sep-chars)
+ "Remove SEP-CHARS from STR."
+ (dotimes (i (length sep-chars))
+ (let ((ch (char-to-string (aref sep-chars i))))
+ (setq str (replace-regexp-in-string (regexp-quote ch) "" str t t))))
+ str)
+
+(defun evil-numbers--strip-chars-apply (str-src str-dst sep-chars)
+ "Add SEP-CHARS into STR-DST from STR-SRC."
+ (let ((sep-chars-list (append sep-chars nil))
+ ;; Strings to list.
+ (str-src-rev (nreverse (append str-src nil)))
+ (str-dst-rev (nreverse (append str-dst nil)))
+ (result (list)))
+ (while str-dst-rev
+ (let ((ch-src (pop str-src-rev)))
+ (if (and ch-src (memq ch-src sep-chars-list))
+ (push ch-src result)
+ (push (pop str-dst-rev) result))))
+ (apply #'string result)))
+
;; ---------------------------------------------------------------------------
;; Internal Utilities
;;
@@ -168,6 +204,59 @@ representation of NUMBER is smaller."
((= base 10) (format (format "%%0%dd" width) num))
(t "")))
+(defun evil-numbers--skip-chars-impl
+ (ch-skip ch-sep-optional dir ch-num limit)
+ "Wrapper for `skip-chars-forward' and `skip-chars-backward'.
+
+CH-SKIP: Characters to skip.
+CH-SEP-OPTIONAL: Separator characters (single instances are stepped over).
+DIR: Direction to step in (1 -1).
+CH-NUM: Number of characters to step.
+LIMIT: Point which will not be stepped past."
+ (let* ((is-forward (< 0 dir))
+ (skip-chars-fn (if is-forward
+ #'skip-chars-forward
+ #'skip-chars-backward))
+ (clamp-fn (if is-forward
+ #'min
+ #'max))
+ (skipped
+ (abs (funcall
+ skip-chars-fn ch-skip
+ ;; Limit.
+ (funcall clamp-fn (+ (point) (* ch-num dir)) limit)))))
+
+ ;; Step over single separators, as long as there is a number after them.
+ ;; Allow '100,123' and '16_777_216' to be handled as single numbers.
+ (when ch-sep-optional
+ (let ((point-next nil)
+ (skipped-next 0))
+ (setq ch-num (- ch-num skipped))
+ (while (and (not (zerop ch-num))
+ (save-excursion
+ (and (eq 1 (evil-numbers--skip-chars-impl
+ ch-sep-optional nil
+ dir 1 limit))
+ (progn
+ ;; Note counted towards 'skipped'
+ ;; as this character is to be ignored entirely.
+ (setq skipped-next
+ (evil-numbers--skip-chars-impl
+ ch-skip nil
+ dir ch-num limit))
+ (unless (zerop skipped-next)
+ (setq point-next (point))
+ ;; Found (apply `point-new').
+ t)))))
+ ;; Step over the separator and contents found afterwards.
+ (when point-next
+ (goto-char point-next)
+ (setq skipped (+ skipped skipped-next))
+ (setq ch-num (- ch-num skipped-next))
+ t))))
+
+ skipped))
+
(defun evil-numbers--match-from-skip-chars
(match-chars dir limit do-check do-match)
"Match MATCH-CHARS in DIR (-1 or 1), until LIMIT.
@@ -185,12 +274,6 @@ Each item in MATCH-CHARS is a cons pair.
- `integerp' this number exactly."
(catch 'result
(let* ((is-forward (< 0 dir))
- (skip-chars-fn (if is-forward
- #'skip-chars-forward
- #'skip-chars-backward))
- (clamp-fn (if is-forward
- #'min
- #'max))
(point-init (point))
;; Fill when `do-match' is set.
(match-list (list)))
@@ -199,10 +282,11 @@ Each item in MATCH-CHARS is a cons pair.
(when (if is-forward (> (point) limit) (< (point) limit))
(error "Limit is on wrong side of point (internal error)"))
- (dolist (ch-pair (if is-forward
- match-chars
- (reverse match-chars)))
- (pcase-let ((`(,ch-skip . ,ch-num) ch-pair))
+ (unless is-forward
+ (setq match-chars (reverse match-chars)))
+
+ (while match-chars
+ (pcase-let ((`(,ch-skip ,ch-num ,ch-sep-optional) (pop match-chars)))
;; Beginning of the match.
(when do-match
@@ -211,18 +295,15 @@ Each item in MATCH-CHARS is a cons pair.
(cond
((integerp ch-num)
(let ((skipped
- (funcall
- skip-chars-fn
- ch-skip
- (funcall clamp-fn (+ (point) (* ch-num dir)) limit))))
+ (evil-numbers--skip-chars-impl
+ ch-skip ch-sep-optional dir ch-num limit)))
(when do-check
(unless (eq skipped ch-num)
(throw 'result nil)))))
((eq ch-num '+)
(let ((skipped
- (funcall
- skip-chars-fn
- ch-skip limit)))
+ (evil-numbers--skip-chars-impl
+ ch-skip ch-sep-optional dir most-positive-fixnum limit)))
(when do-check
(unless (>= skipped 1)
(throw 'result nil)))))
@@ -230,15 +311,11 @@ Each item in MATCH-CHARS is a cons pair.
;; No length checking needed as zero is acceptable.
;; Skip these characters if they exist.
((eq ch-num '*)
- (funcall
- skip-chars-fn
- ch-skip
- limit))
+ (evil-numbers--skip-chars-impl
+ ch-skip ch-sep-optional dir most-positive-fixnum limit))
((eq ch-num '\?)
- (funcall
- skip-chars-fn
- ch-skip
- (funcall clamp-fn (+ (point) dir) limit)))
+ (evil-numbers--skip-chars-impl
+ ch-skip ch-sep-optional dir 1 limit))
(t
(error (format "Unknown type %S" ch-skip))))
@@ -320,11 +397,19 @@ replace number incremented by AMOUNT in BASE and return
non-nil."
(evil-numbers--match-from-skip-chars match-chars 1 end t t))
(goto-char (match-end num-group))
- (let* ((str-prev
+ (let* ((sep-char
+ (nth 2 (nth (1- num-group) match-chars)))
+ (str-prev
(funcall decode-fn
(concat (match-string sign-group)
(match-string num-group))))
- (num-prev (string-to-number str-prev base))
+
+ (str-prev-strip
+ (if sep-char
+ (evil-numbers--strip-chars str-prev sep-char)
+ str-prev))
+
+ (num-prev (string-to-number str-prev-strip base))
(num-next (+ amount num-prev))
(str-next
(evil-numbers--format
@@ -346,6 +431,12 @@ replace number incremented by AMOUNT in BASE and return
non-nil."
((eq evil-numbers-case 'downcase)
(setq str-next (downcase str-next)))))
+ (when sep-char
+ ;; This is a relatively expensive operation,
+ ;; only apply separators back if any were found to begin with.
+ (unless (string-equal str-prev str-prev-strip)
+ (setq str-next (evil-numbers--strip-chars-apply str-prev str-next
sep-char))))
+
;; Replace the sign (as needed).
(cond
;; From negative to positive.
@@ -370,10 +461,7 @@ Return non-nil on success, leaving the point at the end of
the number."
;; Find binary literals:
;; 0[bB][01]+, e.g. 0b101 or 0B0
(evil-numbers--inc-at-pt-impl-with-match-chars
- '(("+-" . \?)
- ("0" . 1)
- ("bB" . 1)
- ("01" . +))
+ `(("+-" \?) ("0" 1) ("bB" 1) ("01" + ,evil-numbers-separator-chars))
1 4 ;; Sign & number groups.
amount 2 beg end padded nil
#'identity #'identity)
@@ -381,10 +469,7 @@ Return non-nil on success, leaving the point at the end of
the number."
;; Find octal literals:
;; 0[oO][0-7]+, e.g. 0o42 or 0O5
(evil-numbers--inc-at-pt-impl-with-match-chars
- '(("+-" . \?)
- ("0" . 1)
- ("oO" . 1)
- ("0-7" . +))
+ `(("+-" \?) ("0" 1) ("oO" 1) ("0-7" + ,evil-numbers-separator-chars))
1 4 ;; Sign & number groups.
amount 8 beg end padded nil
#'identity #'identity)
@@ -392,10 +477,7 @@ Return non-nil on success, leaving the point at the end of
the number."
;; Find hex literals:
;; 0[xX][0-9a-fA-F]+, e.g. 0xBEEF or 0Xcafe
(evil-numbers--inc-at-pt-impl-with-match-chars
- '(("+-" . \?)
- ("0" . 1)
- ("xX" . 1)
- ("[:xdigit:]" . +))
+ `(("+-" \?) ("0" 1) ("xX" 1) ("[:xdigit:]" +
,evil-numbers-separator-chars))
1 4 ;; Sign & number groups.
amount 16 beg end padded t
#'identity #'identity)
@@ -403,24 +485,21 @@ Return non-nil on success, leaving the point at the end
of the number."
;; Find decimal literals:
;; [0-9]+, e.g. 42 or 23.
(evil-numbers--inc-at-pt-impl-with-match-chars
- '(("+-" . \?)
- ("0123456789" . +))
+ `(("+-" \?) ("0123456789" + ,evil-numbers-separator-chars))
1 2 ;; Sign & number groups.
amount 10 beg end padded nil
#'identity #'identity)
;; Find decimal literals (super-script).
(evil-numbers--inc-at-pt-impl-with-match-chars
- `(("⁺⁻" . \?)
- (,evil-numbers--chars-superscript . +))
+ `(("⁺⁻" \?) (,evil-numbers--chars-superscript + nil))
1 2 ;; Sign & number groups.
amount 10 beg end padded nil
#'evil-numbers--decode-super #'evil-numbers--encode-super)
;; Find decimal literals (sub-script).
(evil-numbers--inc-at-pt-impl-with-match-chars
- `(("₊₋" . \?)
- (,evil-numbers--chars-subscript . +))
+ `(("₊₋" \?) (,evil-numbers--chars-subscript + nil))
1 2 ;; Sign & number groups.
amount 10 beg end padded nil
#'evil-numbers--decode-sub #'evil-numbers--encode-sub)))
@@ -472,21 +551,23 @@ Return non-nil on success, leaving the point at the end
of the number."
(amount beg end type &optional incremental padded)
"Increment the number at point or after point before `end-of-line' by AMOUNT.
-When region is selected, increment all numbers in the region by AMOUNT
+When region is selected, increment all numbers in the region by AMOUNT.
NO-REGION is internal flag that allows
`evil-numbers/inc-at-point' to be called recursively when
applying the regional features of `evil-numbers/inc-at-point'.
-INCREMENTAL causes the first number to be increased by 1*AMOUNT, the second by
-2*AMOUNT and so on.
+INCREMENTAL causes the first number to be increased by 1*AMOUNT,
+the second by 2*AMOUNT and so on.
+
+PADDED is whether numbers should be padded (e.g. 10 -> 09).
+- nil: is default behavior set by `evil-numbers-pad-default',
+- t: is the opposite of `evil-numbers-pad-default',
+- '(t): enables padding and '(nil) disables padding.
-PADDED is whether numbers should be padded (e.g. 10 -> 09). nil is default
-behavior set by `evil-numbers-pad-default', t is the opposite of
`evil-numbers-pad-default',
-'(t) enables padding and '(nil) disables padding.
-Numbers with a leading zero are always padded. Signs are preserved when padding
-is enabled, i.e. increasing a negative number to a positive will result in a
-number with a + sign."
+Numbers with a leading zero are always padded. Signs are preserved when
+padding is enabled, i.e. increasing a negative number to a positive will
+result in a number with a + sign."
:motion nil
(interactive "*<c><R>")
- [nongnu] elpa/evil-numbers cdfb3cb83c 017/145: Cursor position adjustment after inc/dec, (continued)
- [nongnu] elpa/evil-numbers cdfb3cb83c 017/145: Cursor position adjustment after inc/dec, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers 84bb03cb51 037/145: Add incremental increasing like `g C-a' in vim, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers 23c0a48b62 033/145: Add contributors section., ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers 8834eb2e8b 034/145: Bump to 0.4, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers 5ec72e6cb5 032/145: Add visual state key documentation., ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers b1ba250f98 078/145: Cleanup: reduce right shift, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers 027f8d586c 080/145: Fix #18 operating on bin/hex/octal failed w/ the cursor at the start, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers 7ca411f4ac 100/145: CHANGELOG: use fullstops, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers 8403eeb125 097/145: Correct oversight in 299e0418caa43cb7c15dd21d2dd40b75b147990f, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers af088b7107 107/145: Cleanup: < 80 chars, reduce right shift, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers 407d62222e 126/145: Add separator character support (issue #23),
ELPA Syncer <=
- [nongnu] elpa/evil-numbers d5c40d2bd1 118/145: Cleanup: docstring line wrapping, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers 0ddb39d98e 027/145: Stray paren fix, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers 780db06056 018/145: Tag version 0.2., ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers b754f53c82 039/145: support for increasing superscripts, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers fb16206158 054/145: Fix autoloading, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers 4308935a03 061/145: Resolve looking-back warnings, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers aa94cd1b26 063/145: Merge pull request #11 from ideasman42/patch-1, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers 5356111930 077/145: Cleanup: avoid let binding to set default values for arguments, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers b54cc5ba35 076/145: Fix spelling, ELPA Syncer, 2022/01/06
- [nongnu] elpa/evil-numbers c37a4cf92a 092/145: Fix padding being ignored with block selection, ELPA Syncer, 2022/01/06