82
82
(const :tag " De Bruijn" de-bruijn)
83
83
(const :tag " Default" nil )))
84
84
85
- (defcustom evilem-max-iterations 1000
85
+ (defcustom evilem-max-iterations 4000
86
86
" Maximum number of iterations allowed when collecting positions.
87
87
This is a safety measure to prevent infinite loops."
88
88
:type 'integer
@@ -159,91 +159,120 @@ This is a safety measure to prevent infinite loops."
159
159
160
160
(narrow-to-region (max beg (window-start ))
161
161
(min end (window-end ))))
162
-
163
- ; ; Detect motion direction based on function name
162
+
163
+ ; ; Advanced motion collection with better error handling
164
164
(let* ((motion-name (if (symbolp func) (symbol-name func) " unknown" ))
165
- (direction (cond
166
- ((string-match-p " previous\\ |backward\\ |up" motion-name) 'backward )
167
- ((string-match-p " next\\ |forward\\ |down" motion-name) 'forward )
168
- (t 'unknown )))
165
+ ; ; More fine-grained motion classification
166
+ (motion-type
167
+ (cond
168
+ ((string-match-p " line\\ |paragraph" motion-name) 'line )
169
+ ((string-match-p " word\\ |char\\ |sentence" motion-name) 'char )
170
+ (t 'unknown )))
171
+ (direction
172
+ (cond
173
+ ((string-match-p " previous\\ |backward\\ |up" motion-name) 'backward )
174
+ ((string-match-p " next\\ |forward\\ |down" motion-name) 'forward )
175
+ (t 'unknown )))
176
+ ; ; Track positions more efficiently with a hash table
177
+ (seen-positions (make-hash-table :test 'eql :size 100 ))
169
178
(prev-point nil )
170
- (position-count (make-hash-table :test 'equal ))
171
179
(stalled-count 0 )
172
- (making-progress t )
173
180
(iteration-count 0 ))
174
-
175
- (while (and making-progress
176
- (< iteration-count evilem-max-iterations)
177
- (< stalled-count 3 ) ; ; Break after no movement 3 times
181
+
182
+ (while (and (< iteration-count evilem-max-iterations)
183
+ (< stalled-count 3 ) ; ; Break after too many failures
178
184
(ignore-errors
179
185
(setq this-command func
180
186
last-command func)
181
-
187
+
182
188
; ; Execute the motion command
183
189
(let ((pre-pos (point )))
190
+ ; ; Run the motion function
184
191
(call-interactively func)
185
-
186
- ; ; Track if we're stuck at the same position
187
- (if (= pre-pos (point ))
188
- (setq stalled-count (1+ stalled-count))
189
- (setq stalled-count 0 ))
190
-
191
- ; ; For direction-based movements, also check if we're
192
- ; ; moving in the expected direction
193
- (when (and prev-point (not (eq direction 'unknown )))
194
- (when (or (and (eq direction 'backward ) (>= (point ) prev-point))
195
- (and (eq direction 'forward ) (<= (point ) prev-point)))
196
- ; ; If we're not moving in the expected direction,
197
- ; ; increment stall counter
198
- (setq stalled-count (1+ stalled-count)))))
199
-
200
- ; ; Handle overlays - completely mode-agnostic approach
201
- (unless include-invisible
202
- (let ((overlays (overlays-at (point )))
203
- (skipped nil ))
204
-
205
- ; ; Check all overlays at point
192
+
193
+ ; ; Check if we're making progress
194
+ (cond
195
+ ; ; Not moving at all
196
+ ((= pre-pos (point ))
197
+ (setq stalled-count (1+ stalled-count)))
198
+
199
+ ; ; Moving in the wrong direction
200
+ ((and prev-point
201
+ (not (eq direction 'unknown ))
202
+ (or (and (eq direction 'backward ) (> (point ) prev-point))
203
+ (and (eq direction 'forward ) (< (point ) prev-point))))
204
+ (setq stalled-count (1+ stalled-count)))
205
+
206
+ ; ; Otherwise, we're moving well
207
+ (t (setq stalled-count 0 ))))
208
+
209
+ ; ; Handle invisible text properties and overlays
210
+ (when (and (not include-invisible)
211
+ (or (invisible-p (point )) ; ; Check text properties too
212
+ (let ((ovs (overlays-at (point ))))
213
+ (seq-some (lambda (ov )
214
+ (overlay-get ov 'invisible ))
215
+ ovs))))
216
+ ; ; Skip over the invisible area in the appropriate direction
217
+ (let* ((pos (point ))
218
+ ; ; Find extent of invisibility
219
+ (invisible-beg pos)
220
+ (invisible-end pos)
221
+ ; ; Find overlays that might cause invisibility
222
+ (overlays (overlays-at pos)))
223
+
224
+ ; ; Process text properties first (they're often faster)
225
+ (while (and (> invisible-beg (point-min ))
226
+ (invisible-p (1- invisible-beg)))
227
+ (setq invisible-beg (1- invisible-beg)))
228
+
229
+ (while (and (< invisible-end (point-max ))
230
+ (invisible-p (1+ invisible-end)))
231
+ (setq invisible-end (1+ invisible-end)))
232
+
233
+ ; ; Then check overlays to see if they extend the invisible region
206
234
(dolist (ov overlays)
207
- ; ; Skip any overlays with invisible property
208
235
(when (overlay-get ov 'invisible )
209
- ; ; Jump to the appropriate end of the overlay based on motion direction
210
- (goto-char (if (eq direction 'backward )
211
- (max (point-min ) (1- (overlay-start ov)))
212
- (min (point-max ) (1+ (overlay-end ov)))))
213
- (setq skipped t )))
214
-
215
- ; ; Re-run motion if we skipped an overlay
216
- (when skipped
217
- (call-interactively func))))
218
-
219
- ; ; Cycle detection - if we've seen this position too many times
220
- (let ((pos-key (format " %s -%s " (point ) (get-buffer-window ))))
221
- (puthash pos-key
222
- (1+ (gethash pos-key position-count 0 ))
223
- position-count)
224
- (when (> (gethash pos-key position-count 0 ) 2 )
225
- (setq making-progress nil )))
226
-
236
+ (setq invisible-beg (min invisible-beg (overlay-start ov))
237
+ invisible-end (max invisible-end (overlay-end ov)))))
238
+
239
+ ; ; Now jump past the invisible region based on direction
240
+ (goto-char (if (eq direction 'backward )
241
+ (max (point-min ) (1- invisible-beg))
242
+ (min (point-max ) (1+ invisible-end))))
243
+
244
+ ; ; Rerun the motion for consistency
245
+ (call-interactively func)))
246
+
247
+ ; ; Efficiently track position visit counts using the hash table
248
+ (let ((pos-hash (point )))
249
+ (puthash pos-hash
250
+ (1+ (gethash pos-hash seen-positions 0 ))
251
+ seen-positions)
252
+ ; ; If we've seen this position too many times, stop
253
+ (when (> (gethash pos-hash seen-positions) 2 )
254
+ (setq stalled-count 3 )))
255
+
227
256
; ; Return t to continue the loop
228
257
t )
229
-
258
+
230
259
; ; Check that the new position is unique
231
260
(progn
232
261
(setq point (cons (point ) (get-buffer-window )))
233
262
(not (member point points))))
234
-
235
- ; ; Record the point and update tracking variables
263
+
264
+ ; ; Record this position and advance counters
236
265
(push point points)
237
266
(setq prev-point (point ))
238
267
(setq iteration-count (1+ iteration-count)))))))
239
-
240
- ; ; Handle multiple functions
268
+
269
+ ; ; Handle non-function case (list of functions)
241
270
(setq points (cl-remove-duplicates
242
271
(cl-mapcan (lambda (f )
243
272
(evilem--collect f scope all-windows))
244
273
func))))
245
-
246
- ; ; Post-process and return the points
274
+
275
+ ; ; Return the sorted points
247
276
(funcall (or collect-postprocess
248
277
#'evilem--default-collect-postprocess )
249
278
points)))
@@ -601,4 +630,4 @@ This is a safety measure to prevent infinite loops."
601
630
(define-key evilem-map " +" #'evilem-motion-next-line-first-non-blank )
602
631
603
632
(provide 'evil-easymotion )
604
- ; ;; evil-easymotion.el ends here
633
+ ; ;; evil-easymotion.el ends here
0 commit comments