【问题标题】:Killing buffers whose names start with a particular string杀死名称以特定字符串开头的缓冲区
【发布时间】:2011-07-03 02:59:29
【问题描述】:

这是我的问题: 我使用 Emacs 并获得了很多一直无用的缓冲区,例如 *Messages* 或 *Completions*。

我想绑定 \C-y 以关闭所有以 * 开头的缓冲区,除了 *shell*(和 *shell* )缓冲区。

为此,我想在我的 .emacs 文件中添加一些 Emacs-Lisp:

(defun string-prefix s1 s2
  (if (> (string-length s1) (string-length s2)) nil
    (string=? s1 (substring s2 0 (string-length s1))) ))

(defun curry2
  (lambda (f)
    (lambda (x)
      (lambda (y)
    (f x y) ))))

(defun filter
  (lambda (f l)
    (if (null? l) '()
      (let ((rest (cdr l)))
    (if (f (car l)) (cons (car l) rest)
      rest) ))))


(defun kill-useless (arg)
  (interactive "p")
  (map 'kill-buffer
       (filter
    (not ((curry2 string-prefix) "*shell*"))
    (list-buffers)
    ) ))

(global-set-key "\C-y" 'kill-useless)

我已经使用 Scheme 测试了 string-prefixcurry2filter 看起来很简单。 遗憾的是我无法让kill-useless 正常工作。

上面写着filter: Invalid function: (curry2 string-prefix)

现在,问题是我在 Emacs-Lisp 上有点烂,除了 Scheme,我真的不使用任何 Lisp,而在 Scheme (MIT) 中,这是可行的:

(filter ((curry2 string-prefix?) "*shell") '("*shell*" "*sh22" "eel"))
;Value 5: ("*shell*")

我想要:

  1. 修复我的代码的方法
  2. 有关如何以不同方式执行此操作的建议

谢谢!

【问题讨论】:

  • *Message* 缓冲区是一个有用的缓冲区:每次 Emacs 告诉你某事时,它都会放在那里,因此你可以例如将其复制并粘贴到 google 以在 google 上找到解决方案。还要注意,当您可能会使用某种模式(如 magit)时,它们会以有用的方式使用此类缓冲区。
  • 是的,我最终也保留了那个缓冲区。感谢您的提示!

标签: emacs lisp elisp


【解决方案1】:

C-h f kill-matching-buffers RET

kill-matching-buffers 是一个交互式编译的 Lisp 函数 `files.el'。

(kill-matching-buffers REGEXP &optional INTERNAL-TOO)

杀死名称与指定 REGEXP 匹配的缓冲区。 可选的第二个参数指示是否也终止内部缓冲区。

【讨论】:

  • +1 表示内置函数,但实际使用示例会更有帮助。
  • 它一直在询问我是否要关闭每个缓冲区(即使未修改) - 有没有办法避免这种情况?
  • 是的,那个功能的用户界面真的很糟糕。实际上,一个人开始非常快地按下y,当一个 modified 缓冲区出现时,你在注意到问题略有不同之前已经杀死了它......
  • @Zitrax 如果您在 kill-matching-buffers 上执行 C-h f,它会提供更多详细信息。您可以避免使用 (kill-matching-bufers REGEXP nil t) 之类的提示关闭每个缓冲区(它是执行此操作的 t - nil 用于将 internal-too 设置为 nil (如果需要,您可以更改它 - 检查文档每个选项的作用)
【解决方案2】:

另一种方法:

(require 'cl)

(defun is-useless-buffer (buffer)
  (let ((name (buffer-name buffer)))
    (and (= ?* (aref name 0))
         (not (string-match "^\\*shell\\*" name)))))

(defun kill-useless-buffers ()
  (interactive)
  (loop for buffer being the buffers
        do (and (is-useless-buffer buffer) (kill-buffer buffer))))

【讨论】:

  • 我最喜欢这种方法。我还添加了检查以保留 *Messages* 缓冲区。恭喜肖恩!你赢了 :) 另外,(require 'cl) 到底做了什么?
  • 它引入了 Emacs 的 Common Lisp 包,这是(非常方便的)“循环”宏所在的地方。整个包非常有用;我的 .emacs 文件中的第一行是 (require 'cl)。见gnu.org/software/emacs/manual/html_mono/cl.html
  • @EmmanuelGoldstein 然后根据is-useless-buffer,您可以添加一些日志记录以查看您认为无用的缓冲区是否真的无用。例如,您可以将do (and ...) 部分更改为do (message "Buffer %s %s useless" (buffer-name buffer) (if (is-useless-buffer buffer) "is" "is not"))
  • @EmmanuelGoldstein 如果不清楚,在执行上述代码后(例如在运行 Emacs 时将其包含在启动文件中),您仍然需要通过键入来运行 kill-useless-buffers 命令M-x(按住 meta/alt 并按 x)、kill-useless-buffers 和 Return 键。此外,不会有明显的迹象表明缓冲区已被杀死,除非您在运行命令时碰巧看到了一个。我刚启动 Emacs,创建了一些名为 *foo**bar**baz* 的缓冲区,然后运行该命令,这些缓冲区被删除了。
  • 要更改此代码以删除名称以星号开头的缓冲区以外的所有内容,只需更改 is-useless-buffer 函数的主体(从第二行的左括号到下一个到-最后一行的最后一个右括号)到(string-match-p (rx string-start (not (any "*"))) (buffer-name buffer))。然后您需要评估修改后的定义以使其生效,最简单的方法是在光标位于函数内部时按 control-meta-X。
【解决方案3】:

请记住,elisp 不是方案,甚至不是普通的 lisp。语法和语义不同。例如,defun 需要用括号括起来的参数列表。此外,在 elisp 中实际上不可能进行柯里化。

幸运的是,elisp 内置了你想做的大部分事情。对于string-prefix,您可以使用string-prefix-p 内置函数。对于filter,您可以使用remove-if-not,或者反过来使用remove-if

对于柯里化,您可以使用 apply-partially 内置函数。要获得匹配前缀为 "*shell*" 的字符串的函数,请尝试以下操作:

(apply-partially 'string-prefix-p "*shell*")

你可以这样使用它:

(mapcar
 (apply-partially 'string-prefix-p "*shell*")
 '("*shell*" "foo" "*shell*<2>"))

; results in
(t nil t)

(require 'cl) ; for remove-if
(remove-if
 (apply-partially 'string-prefix-p "*shell*")
 '("*shell*" "foo" "*shell*<2>"))

; results in
("foo")

【讨论】:

  • 感谢您的提醒,但我已将 答案 交给了提供有效解决方案的人。试图摆弄这项工作不会太有趣!
【解决方案4】:

为了安全起见,最好在删除之前先查看要删除的内容。

Icicles 中,默认情况下 C-x k 是一个多命令,您可以使用它来终止与您的 minibuffer 输入匹配的任意数量的缓冲区。在这种情况下,您可以键入 * TAB 以查看所有以 * 开头的缓冲区名称作为完成候选。

然后,您可以通过多种方式缩小匹配范围。当剩下的所有匹配项都是您想要的,点击C-! 删除所有这些缓冲区。

在您介绍的情况下,您不想删除名为 *shell... 的缓冲区。因此,在* TAB 之后,您将点击S-SPC,然后输入另一个匹配模式:shell,然后是S-TAB。这会缩小到只有您不想想要杀死的*shell... 缓冲区。然后您点击C-~ 减去这些匹配项(补充)。这留下了除 shell 缓冲区之外的所有缓冲区。点击C-!,他们都被杀了。

您还可以通过在 *Completions*:C-mouse-2 中按住 control 键单击它们的名称来终止单个缓冲区。

更一般地说,在 Icicles 每个 读取缓冲区名称的多命令允许您使用 S-delete(Shift + Delete 键)来终止缓冲区候选者。

http://www.emacswiki.org/emacs/Icicles_-_Multi-Commands

http://www.emacswiki.org/emacs/Icicles_-_More_About_Multi-Commands

【讨论】:

    【解决方案5】:

    我发现kill-matching-functions 提示我输入未修改的缓冲区,这不是我想要的。下面的函数将杀死与前缀匹配的缓冲区(如问题标题中所示)。不完全是您想要的,但也许像我这样从 Google 来到这里的人会发现这很有用。

    (require 'cl)
    (defun kill-buffers-with-prefix (prefix)
      "Kill buffers whose names start with the given prefix"
      (interactive "sPrefix to kill: ")
      (loop for buffer in (buffer-list)
            do (if (string-prefix-p prefix (buffer-name buffer))
                   (kill-buffer buffer))))
    

    【讨论】:

      【解决方案6】:

      杀死所有其他缓冲区

      (defun px-kill-other-buffers ()
        "Kill all other buffers."
        (interactive)
        (mapc 'kill-buffer (delq (current-buffer) (buffer-list))))
      

      搜索字符串的开头

      (defun string-starts-with-p (string prefix)
          "Return t if STRING starts with prefix."
          (and
           (string-match (rx-to-string `(: bos ,prefix) t) string)
           t))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-07-04
        • 1970-01-01
        • 2013-08-21
        • 2015-01-02
        • 1970-01-01
        • 2017-08-08
        • 2014-11-26
        相关资源
        最近更新 更多