其实就是把命令行包装了一下,免去每次拼装命令行的麻烦。在 Emacs 中无非就是下载/拷贝文件到某处,解压然后跳过顶层目录。所以我把命令分成三组,按需组合:
- copy (
cp
)
- download (
curl
)
- extract (
tar
, unzip
)
都是 macOS1 和 Linux 内置的命令(Windows Powershell 应该也有这些/类似的命令,但是我不懂怎么使用 ╮(╯_╰)╭ 所以就空着以后再填了)。
使用方法:
(async-copy-file
url-or-local-file ;; 根据输入的文件名自动选用 copy / download
output-dir
;; 以下是可选参数
:overwrite t ;; 覆盖已存在文件
:extract-arg '(unzip :strip-component 1) ;; 使用 unzip 解压文件 (如果是 tar.xx 格式的则选用 tar),
;; 并去掉第 1 级目录。
;; :dry-run t ;; 打印最终命令,而非执行
:finish-fn ;; 命令执行完后的回调
(lambda (output-buffer)
(kill-buffer output-buffer)
(message "==> Finish callback")))
我原本也想只用 Emacs 内置函数来实现,但是 url-copy-file
看不到下载进度让人有点着急,我还是习惯弹出一个 *output*
缓冲,看着下载进度比较踏实(如果实在不想看到这个 *output*
也是有办法屏蔽的)。
而且 url-copy-file
是先下载到内存,完成之后才写入目标文件,遇到大文件恐怕不太靠谱(我看看 async-copy-file
是不是可以给 curl 命令加上断点续传的参数)。
最关键的是仍然绕不开命令行,我稍早就写了一个 url-copy-file
函数 + async + unzip
命令的包:GitHub - twlz0ne/unzip.el: Unzip wrapper for Emacs
1 macOS 下的 tar 其实是 bsdtar,支持 zip 解压,但是我并没有使用这项功能,为了和 Linux 保持一致。
UPDATE
除了弹窗显示进度,也可以考虑在 modeline 转圈圈提示,具体参考 #14 楼
4 个赞
cireu
2
GNU工具比BSD本家残疾还真是少见,难道是zip的意识形态问题?
dired-aux.el
有个dired-compress-file
,可以解压/压缩文件,也许可以省点功夫。
我也喜欢进度条。url-copy-file
应该可以实现进度条,要下载的文件大小一般是预先知道的,已经下载的大小也是知道的,有机会折腾看看。
对,这也是个痛点,如果要用它下载了 Debian DVD 文件那就惨了,即便 Emacs 不卡死,你的电脑也至少要有 3.6 GB 的空闲内存,因为 Emacs 要把这 3.6 GB 的内容写到一个 Buffer 中,想到于 Emacs 直接打开这个文件。
还有 url-copy-file
是同步的,应该用 url.el
写一个异步的,用 url-retrieve
?命令 package-refresh-contents
就支持异步,但印象中 url.el
不是很靠谱,Emacs 作为编辑器,是一个用户程序,不像专门的编程语言、环境那么可靠。
cireu
4
有libcurl的dyn module,不过十分简陋
用异步的 shell-command
或者直接 async-shell-command
也有进度条
M-! curl -O https://mirrors.tuna.tsinghua.edu.cn/gnu/emacs/emacs-26.2.tar.gz &
试了下
(async-copy-file "http://example.com/index.html" "index.html")
完成的时候报告
error in process sentinel: funcall: Symbol's function definition is void: nil
error in process sentinel: Symbol's function definition is void: nil
finish-fn
没有写,是必填参数?
我看 url-copy-file
没啥好改的,除非改它底层的 url-retrieve-synchronously
函数。 url-retrieve-synchronously
倒是有打印「进度」,不过输出在调试信息中:
(setq asynch-buffer
(url-retrieve url (lambda (&rest ignored)
(url-debug 'retrieval "Synchronous fetching done (%S)" (current-buffer))
(setq retrieval-done t
asynch-buffer (current-buffer)))
nil silent inhibit-cookies))
文件大小可以从 HTTP 头字段 Content-Length
中得到。
这个是结束的时候打印一次。要显示进度条(百分比)应该需要了解更 url.el
底层的实现,估计比较困难。
好东西,改天试试看。希望可以用到lsp-python-ms里
cireu
11
lsp-python-ms,lsp-java,似乎是启动时ensure server然后下载安装。但是lsp目前支持不了异步启动。
可以自己写个 lsp 启动函数和 ensure 函数,然后在回调函数里启动 lsp
对我也正想说这个方案,但要控制下不循环调用就可以了。
添加了一个不弹窗口的 async-copy-file-quiet
函数(仍然可以手动切换到 *async-copy-file*
查看命令行详细输出)。
然后通过 spinner 在 modeline 转圈圈提示:
(let ((curr-buffer (current-buffer)))
(spinner-start)
(async-copy-file-quiet
(lsp-python-ms-latest-nupkg-url)
"~/ms-pyls"
:overwrite t
:extract-arg 'unzip
:finish-fn
`(lambda (_)
(with-current-buffer ,curr-buffer
(spinner-stop))
(message "Done"))))
虽然不是百分比进度提示,但至少表明「进程还在跑」。也许可以改造 spinner 实现百分比进度提示。
我又想到了一个最小干扰的进度提示方法:lv-message
在 minibuffer 上面加一行,用来显示 *async-copy-file*
最后一行的内容。无论是下载、解压还是拷贝, *async-copy-file*
的最后一行都在滚动。
另一个方法是,用 Header Lines 显示进度。