Emacs:删除表达式间的空格

5
在编写Clojure代码时,我经常会在最后一个表达式和闭合括号之间留下空格。类似这样:
(defn myfunction
  [arg]
  (do
    (expr1)
    (expr2)|
  ))

|是光标的位置。在Emacs中是否有一种快捷方式可以删除(expr2)和最后括号之间的空格?目标是最终得到:

(defn myfunction
  [arg]
  (do
    (expr1)
    (expr2)))

2
在这种情况下,我只是使用“C-k M-\”,但也许paredit有更好的解决方法。 - user797257
5个回答

4

M-^(命令delete-indentation)已经可以满足您的要求,至少在您给出的示例(以及类似情况下)是如此。请参见(elisp)User-Level Deletion


1
是的,但你必须在底部行(包含“))”)的某个地方才能使其工作。 - nickie
这是正确的答案,也是 Evil-mode 复制 Vim 的 S-j 行为的方式。要与下一行连接,只需给它一个参数即可。虽然我建议窃取并绑定我链接的 evil-join 函数,它将为您选择的每一行运行 (join-line 1) - danneu
你可以从“更高的行”(包含(expr2)的行)使用它。你所要做的就是在另一个方向上运行negative-argumentdelete-indention。简而言之,M-- M-^.negative-argument是非常强大的参数,并且它绑定到所有修饰符C--M--C-M--,这使得在任何其他按键绑定之前使用它变得极其容易(你可以在不释放任何修饰符的情况下与任何东西一起使用它)。 - mpm

3

向 M-^ 发送前缀参数:

C-u M-^

没有前缀,M-^ 命令会将当前行与前一行合并。

加上前缀 (C-u),M-^ 命令会将下一行与当前行合并。


1

改进@wvxvw上面的评论,您可以将以下内容添加到您的.emacs文件中。然后,C-z m(或您选择的任何其他键组合)将执行您想要的操作。实际上,如果您在包含(expr1)的行的任何位置,它都会起作用。

(global-set-key "\C-zm" 'join-lines-removing-spaces)
(defun join-lines-removing-spaces ()
  "Join the current line with the next, removing all whitespace at this point."
  (move-end-of-line nil)
  (kill-line)
  (delete-horizontal-space))

1
这是我对解决此问题的看法: 在您的示例中,您可能想要做的是退出列表。 那么为什么不将空格清理附加到列表退出上呢?
(global-set-key (kbd "C-M-e") 'up-list-robust)

(defun up-list-robust ()
  (interactive)
  (remove-gaps)
  (let ((s (syntax-ppss)))
      (when (nth 3 s)
        (goto-char (nth 8 s)))
      (ignore-errors (up-list))
      (remove-gaps)))

(defun remove-gaps ()
  (when (looking-back ")\\([ \t\n]+\\))")
    (delete-region (match-beginning 1)
                   (match-end 1))))

现在,每当您退出列表时,最接近两个括号之间的空格将被删除。
我刚写完这段内容,欢迎提出改进意见,但我已经使用了几分钟,我很喜欢它。您可能还想将其绑定到更好的快捷方式上,C-M-eup-list 的默认设置。

0
这是我为自己编写的一个函数,用于解决这个特定的问题。
(defun delete-surrounding-whitespace ()
  (interactive)
  (let ((skip-chars "\t\n\r "))
    (skip-chars-backward skip-chars)
    (let* ((start (point))
           (end (progn
                  (skip-chars-forward skip-chars)
                  (point))))
      (delete-region start end))))

在教授钓鱼技巧而非提供鱼的精神下,我将分享如何从Emacs内部发现这一点。

如果您知道要查找更多信息的函数或变量的名称,则可以使用apropos进行深入挖掘。但是如果您不知道命令可能被称为什么怎么办?

例如,我可能会使用apropos并搜索del.*white、zap.*space、del.*space等,但从未遇到像just-one-space这样有用的空格函数。

为了扩大搜索范围,您可以通过按C-h i进入Texinfo文档来搜索Emacs文档。按mEmacs进入文档的Emacs特定部分(某些软件包也会有相应的部分)。进入Emacs部分后,按s进行搜索,并执行类似delete.*white的搜索,然后您将进入文档的删除部分,其中将看到各种有用的删除方法。

12.1.1 Deletion
---------------

Deletion means erasing text and not saving it in the kill ring.  For the
most part, the Emacs commands that delete text are those that erase just
one character or only whitespace.

‘<DEL>’
‘<BACKSPACE>     Delete the previous character, or the text in the region if it is
     active (‘delete-backward-char’).
<Delete>     Delete the next character, or the text in the region if it is
     active (‘delete-forward-char’).

‘C-d’
     Delete the next character (‘delete-char’).

‘M-\’
     Delete spaces and tabs around point (‘delete-horizontal-space’).
‘M-<SPC>’
     Delete spaces and tabs around point, leaving one space
     (‘just-one-space’).
‘C-x C-o’
     Delete blank lines around the current line (‘delete-blank-lines’).
‘M-^’
     Join two lines by deleting the intervening newline, along with any
     indentation following it (‘delete-indentation’).

我没有看到任何东西完全符合我要找的。但是,通过使用apropos搜索并拉起一些函数的帮助缓冲区,我可以看到它们是如何实现的,并使用相同的技术编写我需要的确切函数。

在simple.el.gz中查看just-one-space函数时,我看到了一个名为cycle-spacing的附近函数,看起来它接近具有我所需功能。

(defun cycle-spacing (&optional n preserve-nl-back mode)
  "Manipulate whitespace around point in a smart way.
In interactive use, this function behaves differently in successive
consecutive calls.

The first call in a sequence acts like `just-one-space'.
It deletes all spaces and tabs around point, leaving one space
\(or N spaces).  N is the prefix argument.  If N is negative,
it deletes newlines as well, leaving -N spaces.
\(If PRESERVE-NL-BACK is non-nil, it does not delete newlines before point.)

The second call in a sequence deletes all spaces.

The third call in a sequence restores the original whitespace (and point).

If MODE is `single-shot', it only performs the first step in the sequence.
If MODE is `fast' and the first step would not result in any change
\(i.e., there are exactly (abs N) spaces around point),
the function goes straight to the second step.

Repeatedly calling the function with different values of N starts a
new sequence each time."
  (interactive "*p")
  (let ((orig-pos    (point))
    (skip-characters (if (and n (< n 0)) " \t\n\r" " \t"))
    (num         (abs (or n 1))))
    (skip-chars-backward (if preserve-nl-back " \t" skip-characters))
    (constrain-to-field nil orig-pos)
    (cond
     ;; Command run for the first time, single-shot mode or different argument
     ((or (eq 'single-shot mode)
      (not (equal last-command this-command))
      (not cycle-spacing--context)
      (not (eq (car cycle-spacing--context) n)))
      (let* ((start (point))
         (num   (- num (skip-chars-forward " " (+ num (point)))))
         (mid   (point))
         (end   (progn
              (skip-chars-forward skip-characters)
              (constrain-to-field nil orig-pos t))))
    (setq cycle-spacing--context  ;; Save for later.
          ;; Special handling for case where there was no space at all.
          (unless (= start end)
                (cons n (cons orig-pos (buffer-substring start (point))))))
    ;; If this run causes no change in buffer content, delete all spaces,
    ;; otherwise delete all excess spaces.
    (delete-region (if (and (eq mode 'fast) (zerop num) (= mid end))
               start mid) end)
        (insert (make-string num ?\s))))

     ;; Command run for the second time.
     ((not (equal orig-pos (point)))
      (delete-region (point) orig-pos))

     ;; Command run for the third time.
     (t
      (insert (cddr cycle-spacing--context))
      (goto-char (cadr cycle-spacing--context))
      (setq cycle-spacing--context nil)))))

我能够简化它,因为我不需要处理有条件地删除换行符并保留n个剩余空格。


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