那好像得靠 treesitter 了…?
我也好奇,有时间真应该研究下
我加了一个 API 叫做 puni-up-list
,wiki 里很多命令都用这个重构了,你想要的那个 kill-line
也用这个做了:Useful commands · AmaiKinono/puni Wiki · GitHub
Edit: 有了这个 API 以后就可以做一些比较神奇的事情了,比如 slurp:
barf、splice、split、raise 也都不是问题。不过我不太喜欢这些命令,所以只是玩玩
请问你能分享下 slurp 的函数吗,我借鉴下。
感觉 slurp 还是挺常用的,有了这些花里胡哨的东西,我就可以彻底删掉 paredit 了
先等等吧,我打算把它们都实现了 也许放在一个另外的文件里。能做全语言 paredit 为啥不做呢(
赞。已经用 puni-kill-line
来代替 paredit-kill
了。
不过在没有空白尾行的情况下有时会失败,例如:
(with-temp-buffer
(insert "print <<~EOF.gsub(/\\n/, ' ')
This is a
sample text.
EOF") ;; <---
(ruby-mode)
(goto-char (point-min))
(re-search-forward "~EOF" nil t)
(puni-kill-line) ;; kill .gsub(/\\n/, ' ')
(substring-no-properties (buffer-string)))
;; => "print <<~EOF.gsub(/\\n/, ' ')
;; This is a
;; sample text.
;; EOF"
添加空白尾行之后就可以删除了:
(with-temp-buffer
(insert "print <<~EOF.gsub(/\\n/, ' ')
This is a
sample text.
EOF\n") ;; <---
(ruby-mode)
(goto-char (point-min))
(re-search-forward "~EOF" nil t)
(puni-kill-line) ;; kill .gsub(/\\n/, ' ')
(substring-no-properties (buffer-string)))
;; => "print <<~EOFThis is a
;; sample text.
;; EOF
;; "
但是却多删了一个 \n
,我预期的结果是 print <<~EOF\nThis is a
,实际 print <<~EOFThis is a
。
另外,字符串当中的转义符号也无法删除: "foo|bar\n"
-> "foo|\n"
。
- macOS 10.12.6
- Emacs 28.0-20210819 (rev: fba64e1697174369b87e3de0c189a0fb0963c49c)
- puni-20210820.211 (rev: 5b1f7b7e13d0122bed564ca98408c32e3dbded95)
不过在没有空白尾行的情况下有时会失败,例如…
这个我觉得是 ruby-mode
的 forward-sexp
表现比较怪。在你说的没有空白尾行的情况下:
print <<~EOF|.gsub(/\n/, ' ')
在这里用 forward-sexp
,能工作,跳到的地方和有空白尾行的情况下一样,但是却会扔一个 no next sexp 的错误出来。这个应该是 smie-forward-sexp-command
干的,之后我再看一下。
因为 Puni 做的都是尽可能一般化的修正,问题就是如果 forward-sexp
动了光标,却扔了个错误,那我应该认为它成功了还是失败了。
但是却多删了一个
\n
,我预期的结果是print <<~EOF\nThis is a
我这没有这个问题。看一下你 kill-whole-line
的设置?
另外,字符串当中的转义符号也无法删除:
"foo|bar\n"
→"foo|\n"
。
已经追查到原因。在这里:
"foo|\n"
求值:
(skip-chars-forward (char-to-string (char-after)))
我期望它能跳过光标面前相同的字符,可是对 \
失效了。
Edit: 刚发现 skip-chars-forward
的参数是模仿正则表达式的,那就说得通了。可是我发现我不知道该咋写这个函数了
Edit2: 删不掉 \n
的问题修好了。ruby-mode
那个我还得想想。
看了一下,果然是我有设置这个变量。不过因为 paredit-kill
&awesome-pair-kill
有时候不太理会这个变量,所以非 lisp 场合我都用 evil-delete-line
来代替 kill line 操作,久而久之竟忘了它的存在:
(cl-labels ((test-kill
(kill-function)
(with-temp-buffer
(insert (concat "if cond:\n"
" pass\n"))
(python-mode)
(goto-char (point-min))
(re-search-forward "if" nil t)
(let ((kill-whole-line nil))
(funcall kill-function)
(insert "|")
(substring-no-properties (buffer-string))))))
(dolist (kill-function '(puni-kill-line awesome-pair-kill paredit-kill))
(princ (format "%s:\n" kill-function))
(print (test-kill kill-function))))
;; =>
;; puni-kill-line:
;;
;; "if|
;; pass
;; "
;; awesome-pair-kill:
;;
;; "if|
;; "
;; paredit-kill:
;;
;; "if|
;; "
Emacs 还是有很多细节需要处理的,刚刚提了一个新的 issue:Indentation changed after `puni-kill-line` · Issue #6 · AmaiKinono/puni · GitHub
谢谢。我注意到 smartparens 有 sp-no-reindent-after-kill-modes
和 sp-no-reindent-after-kill-indent-line-functions
,看来它也碰过这个坑。我想想有没有通用的方法。
Edit: 想到了。可以缩进两次,如果导致的 indent offset 一样,说明它算出了正确的位置;如果不一样,说明它只是在可能的位置中循环,那就回到未缩进时的位置。
一番奋战之后,终于把 ParEdit 那些花哨的表达式操作命令实现了:
-
web-mode
中的 slurp 和 barf注意到移动了的定界符会闪一下,操作的时候可以看得更清楚。
-
convolute!
实现了的操作有 slurp、barf、raise、splice、split、transpose、convolute,还有一个我自己设计的 squeeze(用来代替 rewrap)。
另外「重新缩进」的处理也重新设计过了,现在 Puni 尽量不重新计算缩进,有多行需要重新缩进的话,就把它们之间相对的缩进量恢复到使用 Puni 的命令之前的状态。
另外 puni-expand-region
现在也内置了。
新功能肯定会有些小问题,欢迎大家试用和提意见。
今天更新以后,你要的这个命令在 wiki 里有了更清晰的实现,所以敲你一下
哈哈,终于等到了。等到时有空了就去试试~
看来最近更新幅度比较大,先前遇到的几个问题都解决了。
forward-sexp
的确很好用,但有时也力有未逮,比如 "|(foo; bar)"
就因括号不平衡,而无法用 kill line 删除。又因在字符串内,无法判定其 Major mode,也就无法通过定制 forward-sexp-function
来解决。
另,不写测试的都艺高人大胆, manateelazycat, DogLooksGood 和楼主皆属此列。
这个我觉得比较无解。有时候人们希望 Puni 把字符串里的东西看作代码,有时候又希望看作纯文本。
Citre 是有测试的,Puni 没有是因为我真的不知道怎么写。Puni 的思路太奇怪了,如果我做的是 smartparens 那样的东西我就写了。
forward-sexp 遇到一些情况还是可以做的,这就是 paredit 和 awesome-pair 要做的事情,通过一些语法周围分析情况来处理一些极端情况。
反馈个问题,puni 对 Rust 里面的 <>
支持不是很好,举个例子:(|
表示光标位置)
Option<|>Result<i64>
// puni-slurp-forward,符合预期
Option<|Result><i64>
// puni-slurp-forward,不符合预期,正确的结果应该是 Option<|Result<i64>>
Option<|Result<>i64>
可以在 GitHub 上报一下吗,我有空会看
如果使用 (load "auctex.el" nil t t)
后打开 TeX 文件,发现 puni-kill-line
没有如帖子图片中展示的行为,只是单纯的删除一行。请问有人遇到一样的问题吗?