forked from cofi/evil-numbers
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathevil-numbers.el
755 lines (648 loc) · 25.8 KB
/
evil-numbers.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
;;; evil-numbers.el --- Increment/decrement numbers like in VIM -*- lexical-binding: t -*-
;; SPDX-License-Identifier: GPL-3.0-or-later
;; Copyright (C) 2011 by Michael Markert
;; 2020 by Julia Path
;; Author: Michael Markert <[email protected]>
;; Maintainer: Julia Path <[email protected]>
;; Contributors: Matthew Fidler <[email protected]>
;; Michael Markert <[email protected]>
;; Julia Path <[email protected]>
;; Campbell Barton <[email protected]>
;; URL: http://github.com/juliapath/evil-numbers
;; Git-Repository: git://github.com/juliapath/evil-numbers.git
;; Created: 2011-09-02
;; Version: 0.7
;; Package-Requires: ((emacs "24.1") (evil "1.2.0"))
;; Keywords: convenience tools
;;; Commentary:
;; Increment / Decrement binary, octal, decimal and hex literals.
;;
;; Works like C-a/C-x in VIM, i.e. searches for number up to EOL and
;; then increments or decrements and keep zero padding up.
;;
;; Known Bugs:
;; See http://github.com/juliapath/evil-numbers/issues
;;
;; Install:
;;
;; (require 'evil-numbers)
;;
;; and bind, for example:
;;
;; (global-set-key (kbd "C-c +") 'evil-numbers/inc-at-pt)
;; (global-set-key (kbd "C-c -") 'evil-numbers/dec-at-pt)
;; (global-set-key (kbd "C-c C-+") 'evil-numbers/inc-at-pt-incremental)
;; (global-set-key (kbd "C-c C--") 'evil-numbers/dec-at-pt-incremental)
;;
;; or only in evil's normal and visual state:
;;
;; (evil-define-key '(normal visual) 'global (kbd "C-c +")
;; 'evil-numbers/inc-at-pt)
;; (evil-define-key '(normal visual) 'global (kbd "C-c -")
;; 'evil-numbers/dec-at-pt)
;; (evil-define-key '(normal visual) 'global (kbd "C-c C-+")
;; 'evil-numbers/inc-at-pt-incremental)
;; (evil-define-key '(normal visual) 'global (kbd "C-c C--")
;; 'evil-numbers/dec-at-pt-incremental)
;;
;; Usage:
;; Go and play with your numbers!
;;; Code:
(require 'evil)
(eval-when-compile
;; For `pcase-dolist'.
(require 'pcase))
;; ---------------------------------------------------------------------------
;; ;; Compatibility
(when (version< emacs-version "29.1")
(defsubst pos-bol (&optional n)
"Return the position at the line beginning."
(line-beginning-position n))
(defsubst pos-eol (&optional n)
"Return the position at the line end."
(line-end-position n)))
;; ---------------------------------------------------------------------------
;; Custom Variables
(defgroup evil-numbers nil
"Support number increment/decrement."
:group 'convenience)
(define-obsolete-variable-alias
'evil-numbers/padDefault 'evil-numbers-pad-default "evil-numbers v0.6")
(defcustom evil-numbers-pad-default nil
"Whether numbers are padded by default."
:type 'boolean)
(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."
:type '(choice (const nil) string))
(defcustom evil-numbers-case nil
"Case to use for hexadecimal numbers."
:type
'(choice
(const :tag "Current Case" nil)
(const :tag "Upper Case" upcase)
(const :tag "Lower Case" downcase)))
(defcustom evil-numbers-use-cursor-at-end-of-number nil
"When non-nil, recognize numbers directly before the cursor.
This doesn't match VIM's behavior."
:type 'boolean)
;; ---------------------------------------------------------------------------
;; Internal Variables
(defconst evil-numbers--chars-superscript "⁰¹²³⁴⁵⁶⁷⁸⁹")
(defconst evil-numbers--chars-subscript "₀₁₂₃₄₅₆₇₈₉")
(defconst evil-numbers--superscript-alist
(cons
(cons ?- ?⁻)
(cons
(cons ?+ ?⁺)
(mapcar
(lambda (i)
(cons
(string-to-char (number-to-string i))
(aref evil-numbers--chars-superscript i)))
(number-sequence 0 9)))))
(defconst evil-numbers--subscript-alist
(cons
(cons ?- ?₋)
(cons
(cons ?+ ?₊)
(mapcar
(lambda (i)
(cons
(string-to-char (number-to-string i))
(aref evil-numbers--chars-subscript i)))
(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)))
(cond
((and ch-src (memq ch-src sep-chars-list))
(push ch-src result))
(t
(push (pop str-dst-rev) result)))))
(apply #'string result)))
;; ---------------------------------------------------------------------------
;; Internal Utilities
;;
;; Not directly related to incrementing numbers.
(defun evil-numbers--case-category (str default)
"Categorize the case of STR or return DEFAULT when there is no case.
- default: No case.
- 1: Upper case.
- -1: Lower case.
- nil: Mixed case."
(let ((str-dn (downcase str))
(str-up (upcase str)))
(cond
((string-equal str str-dn)
(cond
((string-equal str str-up)
default)
(t
-1)))
(t
(cond
((string-equal str str-up)
1)
(t
nil))))))
(defun evil-numbers--format-binary (number &optional width fillchar)
"Format NUMBER as binary.
Fill up to WIDTH with FILLCHAR (defaults to ?0) if binary
representation of NUMBER is smaller."
(let ((nums (list))
(fillchar (or fillchar ?0)))
(while (> number 0)
(push (number-to-string (% number 2)) nums)
(setq number (truncate number 2)))
(let ((len (length nums)))
(apply #'concat
(cond
((and width (< len width))
(make-string (- width len) fillchar))
(t
""))
nums))))
(defun evil-numbers--format (num width base)
"Format NUM with at least WIDTH space in BASE."
(cond
((= base 2)
(evil-numbers--format-binary num width))
((= base 8)
(format (format "%%0%do" width) num))
((= base 16)
(format (format "%%0%dX" width) num))
((= 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
(cond
(is-forward
#'skip-chars-forward)
(t
#'skip-chars-backward)))
(clamp-fn
(cond
(is-forward
#'min)
(t
#'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.
When DO-CHECK is non-nil, any failure to match returns nil.
When DO-MATCH is non-nil, match data is set.
Each item in MATCH-CHARS is a cons pair.
- The first item is the argument to pass to
`skip-chars-forward' or `skip-chars-backward'.
- The second item specifies how many characters to match,
Valid values:
- Symbol `+' one or more.
- Symbol `*' zero or more.
- `integerp' this number exactly."
(catch 'result
(let* ((is-forward (< 0 dir))
(point-init (point))
;; Fill when `do-match' is set.
(match-list (list)))
;; Sanity check.
(when (cond
(is-forward
(> (point) limit))
(t
(< (point) limit)))
(error "Limit is on wrong side of point (internal error)"))
(unless is-forward
(setq match-chars (reverse match-chars)))
(pcase-dolist (`(,ch-skip ,ch-num ,ch-sep-optional) match-chars)
;; Beginning of the match.
(when do-match
(push (point) match-list))
(cond
((integerp ch-num)
(let ((skipped
(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
(evil-numbers--skip-chars-impl
ch-skip ch-sep-optional dir most-positive-fixnum limit)))
(when do-check
(unless (>= skipped 1)
(throw 'result nil)))))
;; No length checking needed as zero is acceptable.
;; Skip these characters if they exist.
((eq ch-num '*)
(evil-numbers--skip-chars-impl
ch-skip ch-sep-optional dir most-positive-fixnum limit))
((eq ch-num '\?)
(evil-numbers--skip-chars-impl ch-skip ch-sep-optional dir 1 limit))
(t
(error "Unknown type %S (internal error)" ch-skip)))
;; End of the match.
(when do-match
(push (point) match-list)))
;; Match 0 for the full range (expected at the beginning).
(when do-match
(setq match-list
(cond ; `point-init' `point' `match-list'.
(is-forward
(cons point-init (cons (point) (nreverse match-list))))
(t ; `point' `point-init' `match-list'.
(cons (point) (cons point-init match-list)))))
(set-match-data match-list)))
t))
(defun evil-numbers--swap-alist (alist)
"Swap association list ALIST."
(mapcar (lambda (x) (cons (cdr x) (car x))) alist))
(defun evil-numbers--translate-with-alist (alist string)
"Translate every symbol in STRING using ALIST."
(funcall (cond
((stringp string)
#'concat)
(t
#'identity))
(mapcar (lambda (c) (cdr (assoc c alist))) string)))
(defun evil-numbers--encode-super (x)
"Convert X string into super-script."
(evil-numbers--translate-with-alist evil-numbers--superscript-alist x))
(defun evil-numbers--decode-super (x)
"Convert X string from super-script into regular characters."
(evil-numbers--translate-with-alist
(evil-numbers--swap-alist evil-numbers--superscript-alist) x))
(defun evil-numbers--encode-sub (x)
"Convert X string into sub-script."
(evil-numbers--translate-with-alist evil-numbers--subscript-alist x))
(defun evil-numbers--decode-sub (x)
"Convert X string from sub-script into regular characters."
(evil-numbers--translate-with-alist
(evil-numbers--swap-alist evil-numbers--subscript-alist) x))
;; ---------------------------------------------------------------------------
;; Internal Implementation
(defun evil-numbers--inc-at-pt-impl-with-match-chars
(match-chars
;; Numeric & other options.
sign-group num-group base beg end padded do-case
;; Callbacks.
range-check-fn number-xform-fn decode-fn encode-fn)
"Perform the increment/decrement on the current line.
For MATCH-CHARS docs see `evil-numbers--match-from-skip-chars'.
NUM-GROUP is the match group used to evaluate the number.
SIGN-GROUP is the match group used for the sign ('-' or '+').
When PADDED is non-nil,
the number keeps it's current width (with leading zeroes).
When RANGE-CHECK-FN is non-nil, it's called with the match beginning & end.
A non nil result causes this function to return nil.
When all characters are found in sequence, evaluate the number in BASE,
replacing it by the result of NUMBER-XFORM-FN and return non-nil."
(save-match-data
(when (and (save-excursion
;; Skip backwards (as needed), there may be no
;; characters to skip back, so don't check the result.
(evil-numbers--match-from-skip-chars
match-chars -1 beg nil nil)
;; Skip forwards from the beginning, setting match data.
(evil-numbers--match-from-skip-chars match-chars 1 end t t))
;; Either there is no range checking or the range must
;; be accepted by the caller.
(or (null range-check-fn)
(funcall range-check-fn (match-beginning 0) (match-end 0))))
(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))))
(str-prev-strip
(cond
(sep-char
(evil-numbers--strip-chars str-prev sep-char))
(t
str-prev)))
(num-prev (string-to-number str-prev-strip base))
(num-next (funcall number-xform-fn num-prev))
(str-next
(evil-numbers--format
(abs num-next)
(cond
(padded
(- (match-end num-group) (match-beginning num-group)))
(t
1))
base)))
;; Maintain case.
(when do-case
;; Upper case (already set), no need to handle here.
(cond
;; Keep current case.
((null evil-numbers-case)
(when (eq -1 (or (evil-numbers--case-category str-prev -1) -1))
(setq str-next (downcase str-next))))
((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 number then sign, workaround emacs bug #74666.
;; The order replace runs isn't really important anyway,
;; the order may be restored once the bug is resolved in emacs.
;; Replace the number.
(replace-match (funcall encode-fn str-next) t t nil num-group)
;; Replace the sign (as needed).
(cond
;; From negative to positive.
((and (< num-prev 0) (not (< num-next 0)))
(replace-match "" t t nil sign-group))
;; From positive to negative.
((and (not (< num-prev 0)) (< num-next 0))
(replace-match (funcall encode-fn "-") t t nil sign-group)))
(goto-char (match-end num-group)))
t)))
(defun evil-numbers--inc-at-pt-impl
(beg end padded range-check-fn number-xform-fn)
"Increment the number at the current POINT by AMOUNT limited by BEG and END.
Keep padding when PADDED is non-nil.
See `evil-numbers--inc-at-pt-impl-with-match-chars' for details on
RANGE-CHECK-FN and NUMBER-XFORM-FN.
Return non-nil on success, leaving the point at the end of the number."
(or
;; 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" + ,evil-numbers-separator-chars))
;; Sign, number groups & base.
1 4 2
;; Other arguments.
beg end padded nil range-check-fn number-xform-fn
;; Decode & encode callbacks.
#'identity #'identity)
;; 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" + ,evil-numbers-separator-chars))
;; Sign & number groups.
1 4 8
;; Other arguments.
beg end padded nil range-check-fn number-xform-fn
;; Decode & encode callbacks.
#'identity #'identity)
;; 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:]" + ,evil-numbers-separator-chars))
;; Sign, number groups & base.
1 4 16
;; Other arguments.
beg end padded t range-check-fn number-xform-fn
;; Decode & encode callbacks.
#'identity #'identity)
;; Find decimal literals:
;; [0-9]+, e.g. 42 or 23.
(evil-numbers--inc-at-pt-impl-with-match-chars
`(("+-" \?) ("0123456789" + ,evil-numbers-separator-chars))
;; Sign, number groups & base.
1 2 10
;; Other arguments.
beg end padded nil range-check-fn number-xform-fn
;; Decode & encode callbacks.
#'identity #'identity)
;; Find decimal literals (super-script).
(evil-numbers--inc-at-pt-impl-with-match-chars
`(("⁺⁻" \?) (,evil-numbers--chars-superscript + nil))
;; Sign, number groups & base.
1 2 10
;; Other arguments.
beg end padded nil range-check-fn number-xform-fn
;; Decode & encode callbacks.
#'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 + nil))
;; Sign, number groups & base.
1 2 10
;; Other arguments.
beg end padded nil range-check-fn number-xform-fn
;; Decode & encode callbacks.
#'evil-numbers--decode-sub #'evil-numbers--encode-sub)))
(defun evil-numbers--inc-at-pt-impl-with-search
(amount beg end padded range-check-fn)
"Increment the number at the current POINT by AMOUNT limited by BEG and END.
Keep padding when PADDED is non-nil.
See `evil-numbers--inc-at-pt-impl-with-match-chars' for details on
RANGE-CHECK-FN.
Return non-nil on success, leaving the point at the end of the number."
(let ((found nil))
(save-match-data
;; Search for any text that might be part of a number,
;; if `evil-numbers--search-and-replace' cannot parse it - that's fine,
;; keep searching until `end'
;; This avoids doubling up on number parsing logic.
;;
;; Note that the while body is empty.
(while (and
;; Found item, exit the loop.
(null
(when (evil-numbers--inc-at-pt-impl
;; Clamp limits to line bounds.
;; The caller may use a range that spans lines to
;; allow searching and finding items across
;; multiple lines (currently used for selection).
(max beg (pos-bol))
(min end (pos-eol))
padded
range-check-fn
(lambda (n) (+ n amount)))
(setq found t)))
;; Search failed, exit the loop.
(re-search-forward (concat
"["
"[:xdigit:]"
evil-numbers--chars-superscript
evil-numbers--chars-subscript
"]")
end t))))
found))
;; ---------------------------------------------------------------------------
;; Public Functions
;;;###autoload (autoload 'evil-numbers/inc-at-pt "evil-numbers" nil t)
(evil-define-operator evil-numbers/inc-at-pt
(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.
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.
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, for example: increasing a
negative number to a positive will result in a number with a + sign."
:motion
nil
(interactive "*<c><R>")
(setq amount (or amount 1))
(setq padded
(cond
((consp padded)
(car padded))
(t
(funcall (cond
(padded
#'not)
(t
#'identity))
evil-numbers-pad-default))))
(cond
;; Handle selection (block or line).
;; Run this function in a loop (falling through to the `t' case).
((and beg end type)
(let ((count 1))
(save-excursion
(funcall (cond
((eq type 'block)
(lambda (f) (evil-apply-on-block f beg end nil)))
(t
(lambda (f) (funcall f beg end))))
(lambda (beg end)
(evil-with-restriction
beg end (goto-char beg)
(while (evil-numbers--inc-at-pt-impl-with-search
(* amount count) (point) (point-max) padded nil)
(when incremental
(setq count (+ count 1))))))))))
;; Handle the simple case, either the cursor is over a number,
;; or a number exists between the cursor and `end-of-line'.
(t
(let ((point-next
(save-excursion
(when (evil-numbers--inc-at-pt-impl-with-search
amount (pos-bol) (pos-eol) padded
;; Optional range checking function, only needed when
;; `evil-numbers-use-cursor-at-end-of-number' is not nil.
(cond
(evil-numbers-use-cursor-at-end-of-number
nil)
(t
;; VIM doesn't allow the number directly before
;; the cursor to be manipulated.
;; This function checks the range to disallow that case.
;;
;; Note that this turns out to be simpler than enforcing
;; the limit in the searching logic.
(let ((point-init (point)))
(lambda (_beg end) (< point-init end))))))
(point)))))
(cond
((null point-next)
;; Point not found, note that VIM doesn't report anything in this case.
(message "No number at point or until end of line")
nil)
(t
;; Moves point one position back to conform with VIM,
;; see `evil-adjust-cursor' for details.
(goto-char (1- point-next))
t))))))
;;;###autoload (autoload 'evil-numbers/dec-at-pt "evil-numbers" nil t)
(evil-define-operator evil-numbers/dec-at-pt
(amount beg end type &optional incremental padded)
"Decrement the number at point or after point before `end-of-line' by AMOUNT.
If a region is active, decrement all the numbers at a point by AMOUNT."
:motion nil
(interactive "*<c><R>")
(evil-numbers/inc-at-pt (- (or amount 1)) beg end type incremental padded))
;;;###autoload (autoload 'evil-numbers/inc-at-pt-incremental "evil-numbers" nil t)
(evil-define-operator evil-numbers/inc-at-pt-incremental
(amount beg end type padded)
"Increment the number at point or after point before `end-of-line' by AMOUNT.
When a region is active, increment all the numbers at a point by AMOUNT*n, where
n is the index of the number among the numbers in the region, starting at 1.
That is increment the first number by AMOUNT, the second by 2*AMOUNT,
and so on."
:motion nil
(interactive "*<c><R>")
(evil-numbers/inc-at-pt amount beg end type 'incremental padded))
;;;###autoload (autoload 'evil-numbers/dec-at-pt-incremental "evil-numbers" nil t)
(evil-define-operator evil-numbers/dec-at-pt-incremental
(amount beg end type padded)
"Like `evil-numbers/inc-at-pt-incremental' but with negated argument AMOUNT."
:motion nil
(interactive "*<c><R>")
(evil-numbers/inc-at-pt (- (or amount 1)) beg end type 'incremental padded))
(provide 'evil-numbers)
;; Local Variables:
;; fill-column: 80
;; elisp-autofmt-load-packages-local: ("evil-macros")
;; End:
;;; evil-numbers.el ends here