region active的时候使用transient-map

我想像expand-region一样,再选择字符的时候临时使用一套案件绑定,比如y -> kill-ring-save

具体实现比较头疼,expand-region有明确的启动函数,只要在那个函数里设置就好了。普适性再region-active的时候启动临时transient-map的要怎么搞呢?

push-mark加advise吗?有没有更好的方法?

1 个赞

给set mark加吧 用Emacs加advice是家常便饭

可以给某个region设置key map,比如macrostep就会在expand出来的部分加map,buffer里其他部分不受影响

或许可以试试 activate-mark-hook

(defun activate-mark-hook@set-transient-map ()
  (set-transient-map
   (let ((map (make-sparse-keymap)))
     (define-key map "n" #'next-line)
     (define-key map "p" #'previous-line)
     (define-key map "q" #'keyboard-quit)
     (define-key map "y" (lambda (b e)
                           (interactive "r")
                           (kill-new (buffer-substring b e))
                           (message "Region saved")))
     map)
   #'region-active-p))

(add-hook 'activate-mark-hook #'activate-mark-hook@set-transient-map)

;; 卸载
;; (remove-hook 'activate-mark-hook activate-mark-hook@set-transient-map)

注意上面没有直接绑定你提到的 kill-ring-save,因为这个命令会取消选中区域。


另外也可以考虑下 Hydra,它有一套专门的方案。具体怎么实现不清楚,我几乎没怎么用过 expand-region 和 Hydra。

2 个赞

是这样,但是我觉得的这个方法要求有一个明确的启动函数,我想我的设置能零修改适用所有场景。

@xuchunyang 我现在有个问题……

我把y绑定到kill-ring-save(把Y绑定到你的自制函数)。但是还是有一点问题。我每次按y(或者其他命令)以后 transient map 还会起效。一开始我发现kill-region以后region-active-p还会返回t,所以我吧kill-ring-save改成(lambda (beg end) (interactive "r") (kill-ring-save beg end) (deactivate-mark t)),然而还是不行。之后我试了其他命令比如keyboard-quit,结果是一样的,执行以后transient map没有失效,依然可以激活绑定的命令。

测试代码:

(defun activate-mark-hook@set-transient-map ()
  (set-transient-map
   (let ((map (make-sparse-keymap)))
     (define-key map "y" (lambda (beg end) (interactive "r") (kill-ring-save beg end) (deactivate-mark t)))
     (define-key map "i" (lambda () (interactive) (message "OMG region-activate-p is %s" (prin1-to-string (region-active-p)))))
     map)
   #'region-active-p))

(add-hook 'activate-mark-hook #'activate-mark-hook@set-transient-map)

y后再按i,lambda函数会运行。

P.S. 我把(lambda () (print (region-active-p))绑定到post-command-hook,按y运行我的加强版kill-ring-save和其他命令后打印出来的是nil。你可以试试:

(add-hook 'post-command-hook (lambda () (print (region-active-p)))

上个月解决了我楼上说的那个问题,今天想起来贴出来:

(我在stackvoerflow上看到的解决办法,具体链接我忘了……)

一个有意思的地方:用这个方法我可以把复制和粘贴绑定到一个按键上(C-y),非常实用。

(defconst angel-transient-mode-map-alist
  `((mark-active
     ,@(let ((map (make-sparse-keymap)))
         ;; operations
         (define-key map "p" (lambda (b e)
                               (interactive "r") (delete-region b e) (yank)))
         (define-key map (kbd "M-p") #'counsel-yank-pop)
         (define-key map "x" #'exchange-point-and-mark)
         (define-key map ";" #'comment-dwim)
         (define-key map "y" #'kill-ring-save)
         (define-key map (kbd "C-y") #'kill-ring-save)
         (define-key map "Y" (lambda
                               (b e)
                               (interactive "r")
                               (kill-new (buffer-substring b e))
                               (message "Region saved")))
         ;; isolate
         (define-key map "s" #'isolate-quick-add)
         (define-key map "S" #'isolate-long-add)
         (define-key map "d" #'isolate-quick-delete)
         (define-key map "D" #'isolate-long-delete)
         (define-key map "c" #'isolate-quick-change)
         (define-key map "C" #'isolate-long-change)
         ;; mark things
         (define-key map "f" #'er/mark-defun)
         (define-key map "w" #'er/mark-word)
         (define-key map "W" #'er/mark-symbol)
         (define-key map "P" #'mark-paragraph)
         ;; inner & outer
         ;; (define-key map "C-i" inner-map)
         ;; (define-key map "C-a" outer-map)
         ;; (define-key inner-map "q" #'er/mark-inside-quotes)
         ;; (define-key outer-map "q" #'er/mark-outside-quotes)
         ;; (define-key inner-map "b" #'er/mark-inside-pairs)
         ;; (define-key outer-map "b" #'er/mark-outside-pairs)
         (define-key map "q" #'er/mark-inside-quotes)
         (define-key map "b" #'er/mark-inside-pairs)

         ;; expand-region
         (define-key map (kbd "C--") #'er/contract-region)
         (define-key map (kbd "C-=") #'er/expand-region)
         map))))

(add-to-list 'emulation-mode-map-alists
             'angel-transient-mode-map-alist t)

大师的最新配置

;;; Transient map in region (y p)

;; Unlike `emulation-mode-map-alists', luna-def-key allows more
;; flexibility for the predicate.
(luna-def-key
 :when (lambda ()
         (and mark-active (not (derived-mode-p 'magit-status-mode))))
 :keymaps 'override
 "p" '("override-paste" . (lambda (b e)
                            (interactive "r")
                            (delete-region b e) (yank)))
 "x" #'exchange-point-and-mark
 ";" #'comment-dwim
 "y" #'kill-ring-save
 "C-y" #'kill-ring-save
 "Y" '("copy-&-keep-region" .
       (lambda
         (b e)
         (interactive "r")
         (kill-new (buffer-substring b e))
         (message "Region saved")))
 "r" #'query-replace+
 "R" #'query-replace+delete)