Emacs:删除表达式之间的空格

Emacs:删除表达式之间的空格,emacs,clojure,elisp,whitespace,key-bindings,Emacs,Clojure,Elisp,Whitespace,Key Bindings,在编写Clojure代码时,我经常在最后一个表达式和结束括号之间留下空格。差不多 (defn myfunction [arg] (do (expr1) (expr2)| )) 其中|是光标的位置。Emacs中是否有删除(expr2)和最后方括号之间空格的快捷方式?我们的目标是以 (defn myfunction [arg] (do (expr1) (expr2))) 根据@wvxvw上面的评论,您可以将以下内容添加到.emacs文件中。然后

在编写Clojure代码时,我经常在最后一个表达式和结束括号之间留下空格。差不多

(defn myfunction
  [arg]
  (do
    (expr1)
    (expr2)|
  ))
其中|是光标的位置。Emacs中是否有删除(expr2)和最后方括号之间空格的快捷方式?我们的目标是以

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

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


M-^
(命令
delete indentation
)已经完成了您所要求的操作,至少在您给出的示例中(以及类似的示例中)。请参阅(elisp)
用户级删除

以下是我对该问题解决方案的看法: 在您的示例中,您当时可能想做的是退出列表。 那么,为什么不将空白清理附加到列表退出呢

(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))))
所以现在,任何时候你退出一个列表,最接近的空白,被困在 删除了两个paren

我刚写完,欢迎提出改进建议, 但我已经用了几分钟,我喜欢它。
您可能还希望将其绑定到一个更好的快捷方式,C-M-e是
向上列表
的默认值将前缀参数发送到M-^:

C-u M-^
如果没有前缀,M-^将当前行与上一行合并


使用前缀(C-u),M-^将下一行与当前行连接起来。

下面是我自己编写的一个函数,用于解决这个特定问题

(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等。。。永远不要遇到像一个空格这样有用的空格函数

要扩大搜索范围,您可以通过按C-h i键进入Texinfo文档,从Emacs内部搜索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’).
12.1.1删除
---------------
删除意味着删除文本,而不是将其保存在kill环中。对于
大多数情况下,删除文本的Emacs命令是那些只删除文本的命令
一个字符或仅空白。
‘’
‘’
删除上一个字符或区域中的文本(如果是)
活动('delete-backward-char')。
‘’
删除下一个字符或区域中的文本(如果是)
活动('delete-forward-char')。
“C-d”
删除下一个字符(“Delete-char”)。
“M-\”
删除点周围的空格和制表符(“Delete-horizontal-space”)。
“M-”
删除点周围的空格和制表符,留下一个空格
('just-one-space')。
“C-x C-o”
删除当前行周围的空行(“删除-空行”)。
‘M-^’
通过删除中间的换行符以及任何
其后的缩进(“删除缩进”)。
我在那里没有看到任何和我想要的完全一样的东西。但是,通过使用apropos搜索和调出一些函数的帮助缓冲区,我可以看到它们是如何实现的,并使用相同的技术来编写我需要的确切函数

在simple.el.gz中,我看到函数只有一个空间,我看到附近有一个名为cycle spating的函数,它看起来很接近我需要的功能

(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保留nl返回模式)
“以智能的方式操纵点周围的空白。
在交互使用中,此函数在连续的
连续呼叫。
序列中的第一个调用的行为类似于“仅一个空格”。
它删除点周围的所有空格和制表符,只留下一个空格
\(或N个空格)。N是前缀参数。如果N为负,
它也会删除换行符,留下-N个空格。
\(如果PRESERVE-NL-BACK为非nil,则不会删除点之前的换行符。)
序列中的第二个调用删除所有空格。
序列中的第三个调用恢复原始空白(和点)。
如果模式为“单发”,则仅执行序列中的第一步。
如果模式为“快速”,则第一步不会导致任何更改
\(即,点周围正好有(abs N)个空格),
该函数直接进入第二步。
重复调用具有不同值N的函数将启动
每次都有新的序列。”
(互动“*p”)
(让((原始位置(点))
(跳过字符(如果(和n((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)))))