Evil text object to select nearby file path

  |   Source

CREATED: <2015-08-08 Sat>

UPDATED: <2015-08-24 Mon>

Insert below code into ~/.emacs:

;; {{ nearby file path as text object,
;;      - "vif" to select only basename
;;      - "vaf" to select the full path
;;
;;  example: "/hello/world" "/test/back.exe"
;;               "C:hello\\hello\\world\\test.exe" "D:blah\\hello\\world\\base.exe"
;;
;; tweak evil-filepath-is-nonname to re-define a path
(defun evil-filepath-is-separator-char (ch)
  "Check ascii table"
  (let (rlt)
    (if (or (= ch 47)
            (= ch 92))
        (setq rlt t))
    rlt))

(defun evil-filepath-not-path-char (ch)
  "Check ascii table for charctater "
  (let (rlt)
    (if (or (and (<= 0 ch) (<= ch 32))
            (= ch 34) ; double quotes
            (= ch 39) ; single quote
            (= ch 40) ; (
            (= ch 41) ; )
            (= ch 60) ; <
            (= ch 62) ; >
            (= ch 91) ; [
            (= ch 93) ; ]
            (= ch 96) ; `
            (= ch 123) ; {
            (= ch 125) ; }
            (= 127 ch))
        (setq rlt t))
    rlt))

(defun evil-filepath-char-not-placed-at-end-of-path (ch)
  (or (= 44 ch) ; ,
      (= 46 ch) ; .
      ))

(defun evil-filepath-calculate-path (b e)
  (let (rlt f)
    (when (and b e)
      (setq b (+ 1 b))
      (when (save-excursion
                (goto-char e)
                (setq f (evil-filepath-search-forward-char 'evil-filepath-is-separator-char t))
                (and f (>= f b)))
        (setq rlt (list b (+ 1 f) (- e 1)))))
    rlt))

(defun evil-filepath-get-path-already-inside ()
  (let (b e)
    (save-excursion
      (setq b (evil-filepath-search-forward-char 'evil-filepath-not-path-char t)))
    (save-excursion
      (setq e (evil-filepath-search-forward-char 'evil-filepath-not-path-char))
      (when e
        (goto-char (- e 1))
        ;; example: hello/world,
        (if (evil-filepath-char-not-placed-at-end-of-path (following-char))
            (setq e (- e 1)))
        ))
    (evil-filepath-calculate-path b e)))

(defun evil-filepath-search-forward-char (fn &optional backward)
  (let (found rlt (limit (if backward (point-min) (point-max))) out)
    (save-excursion
      (while (not out)
        ;; for the char, exit
        (if (setq found (apply fn (list (following-char))))
            (setq out t)
          ;; reach the limit, exit
          (if (= (point) limit)
              (setq out t)
            ;; keep moving
            (if backward (backward-char) (forward-char)))))
      (if found (setq rlt (point))))
    rlt))

(defun evil-filepath-extract-region ()
  "Find the closest file path"
  (let (rlt b f1 f2)

    (if (and (not (evil-filepath-not-path-char (following-char)))
             (setq rlt (evil-filepath-get-path-already-inside)))
        ;; maybe (point) is in the middle of the path
        t
      ;; need search forward AND backward to find the right path
      (save-excursion
        ;; path in backward direction
        (when (setq b (evil-filepath-search-forward-char 'evil-filepath-is-separator-char t))
          (goto-char b)
          (setq f1 (evil-filepath-get-path-already-inside))))
      (save-excursion
        ;; path in forward direction
        (when (setq b (evil-filepath-search-forward-char 'evil-filepath-is-separator-char))
          (goto-char b)
          (setq f2 (evil-filepath-get-path-already-inside))))
      ;; pick one path as the final result
      (cond
       ((and f1 f2)
        (if (> (- (point) (nth 2 f1)) (- (nth 0 f2) (point)))
            (setq rlt f2)
          (setq rlt f1)))
       (f1
        (setq rlt f1))
       (f2
        (setq rlt f2))))

    rlt))

(evil-define-text-object evil-filepath-inner-text-object (&optional count begin end type)
  "File name of nearby path"
  (let ((selected-region (evil-filepath-extract-region)))
    (if selected-region
        (evil-range (nth 1 selected-region) (nth 2 selected-region) :expanded t))))

(evil-define-text-object evil-filepath-outer-text-object (&optional NUM begin end type)
  "Nearby path"
  (let ((selected-region (evil-filepath-extract-region)))
    (if selected-region
        (evil-range (car selected-region) (+ 1 (nth 2 selected-region)) type :expanded t))))

(define-key evil-inner-text-objects-map "f" 'evil-filepath-inner-text-object)
(define-key evil-outer-text-objects-map "f" 'evil-filepath-outer-text-object)
;; }}
Comments powered by Disqus