Emacs Lisp中的"shell-command-on-region"

23
在GNU Emacs中,我想对当前选择的文本运行一个名为figlet的程序。然后,我希望注释生成的区域。
我已经使用标准的Emacs命令找到了如何实现:
- 使用C-<space>设置单词起始点 - 将光标移动到单词末尾 - C-u M-x shell-command-on-region RET figlet RET - M-x comment-region RET
然而,我无法编写一个Emacs Lisp程序来完成所有这些操作。以下是我的尝试:
(defun figlet-region () 
  (interactive)
  (push-mark)
  (shell-command-on-region "figlet")
  (comment-region (mark) (point))
  (pop-mark)
)

(global-set-key "\C-c\C-f" 'figlet-region)

然后 C-<space>; M-x figlet-region 产生垃圾:

figlet-region: 参数数量错误:#[(start end command &optional output-buffer replace error-buffer display-error-buffer) "ÆÇÈ  
\"!É
'jÊ!j;j
0Wb
?Ë`Ì\"Í ÎQÎDRÎÉ!\"&
ffÏ )ãÐqÑ!#Ò#p=¬É$]d|e^|Íed ΠÎD¡ÎÉ!\"&â%&#qÉ$Á&%Ó *Í ÉØ#DÚ#É!\"&*#Ô!#ÕÖ×!8WrÐ!qd`Z'o   ØcÙÉ\"d'Zb)(Úp!)Û!*" [error-buffer small-temporary-file-directory temporary-file-directory exit-status error-file replace make-temp-file expand-file-name "scor" nil ...] 9 1945557 (let (string) (unless (mark) (error "The mark is not set now, so there is no region")) (setq string (read-from-minibuffer "Shell command on region: " nil nil nil (quote shell-command-history))) (list (region-beginning) (region-end) string current-prefix-arg current-prefix-arg shell-command-default-error-buffer t))], 1

答案

(defun figlet-region (&optional b e) 
  (interactive "r")
  (shell-command-on-region b e "figlet" (current-buffer) t)
  (comment-region (mark) (point)))

(这是基于Trey Jackson的答案。)

示例(Lisp交互模式)

;;  _   _                 _        
;; | |_| |__   __ _ _ __ | | _____ 
;; | __| '_ \ / _` | '_ \| |/ / __|
;; | |_| | | | (_| | | | |   <\__ \
;;  \__|_| |_|\__,_|_| |_|_|\_\___/

示例(CPerl模式)

#  _   _                 _        
# | |_| |__   __ _ _ __ | | _____ 
# | __| '_ \ / _` | '_ \| |/ / __|
# | |_| | | | (_| | | | |   <\__ \
#  \__|_| |_|\__,_|_| |_|_|\_\___/
3个回答

25

我不确定您尝试通过推送和弹出标记来实现什么,我认为通过执行以下操作您可以获得相同的功能:

(defun figlet-region (&optional b e) 
  (interactive "r")
  (shell-command-on-region b e "figlet")
  (comment-region b e))

interactive的参数告诉Emacs将区域(光标和标记)作为前两个参数传递给命令。


请注意,shell-command-on-region 可能会修改区域,因此最后一行应该像我的答案一样写成 (comment-region (mark) (point)) - sds
在我的回答中使用的形式不会修改区域(除非你从缓冲区“Shell Command Output”中调用它,这很奇怪)。如果你要使用(mark)(point),那么通过参数传递区域是没有意义的,因为当交互式调用时,它将无法按预期工作。 - Trey Jackson
想象一下,如果shell-command-on-region用空字符串替换区域b-e,那么comment-region会受到影响吗? - sds
是的,如果 shell-command-on-region 被调用时带有更多的参数,那么你所说的是正确的。但在我的例子中它是这样的,所以你的观点并不适用。我的答案并没有解决他问题的全部,但他已经想出来了。 - Trey Jackson

9

在lisp程序中使用交互式命令(如shell-command-on-region)并不是一个很好的想法。您应该改用call-process-region:

(defun figlet-region (&optional b e) 
  (interactive "r")
  (call-process-region b e "figlet" t t)
  (comment-region (mark) (point)))

它应该更能够抵御各种用户选项。


请注意,在调用“call-process-region”时,在<command>后使用第一个“t”会使区域被删除。 该函数的签名为:<start end program &optional *delete* destination display &rest args>。 - Alexandro de Oliveira

5

我不确定垃圾来自哪里,但错误本身来自于shell-command-region。当在elisp中使用时,它期望至少3个参数:STARTENDCOMMAND

另外,在一般情况下,在函数中修改标记不是一个好的做法。以下是push-mark文档对此的说明:

新手Emacs Lisp程序员经常会尝试将标记用于错误的目的。有关更多信息,请参见`set-mark'的文档。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接