Julia, Julia!
我最近几个月来使用 Julia 替代 Python ,切实感受到了它的优缺点。
优点包括语法美观易用、性能更高、自带的 REPL 和包管理。
缺点是生态不完善,包括:许多数据服务商只提供 Python 版本的 SDK ;周边工具没 Python 的多、好用;许多包数年不更新,缺少维护。
比如 Lsp ,目前成熟的只有 LanguageServer.jl ,但它启动速度慢,占用内存高,而 JETLS.jl 仍处于早期开发阶段,功能不完善,无法与 Python 相比(其有 Pyright 、 ty 等)。为了提高启动速度, Julia 提供 JulicC 和 sysimage ,但编译过程非常吃机器性能与内存,且耗费时间,我的一个 2C4G 的机器总是在这个过程中卡死。另一个需要编译的过程是首次下载或更新 package 时,对于一些大型包,小机器同样吃不消。
于是为了在 emacs 中为 Julia 提供 lsp ,我需要在配置中添加如下代码1:
(defconst my/julia-lsp-script-content
"import Pkg;
try
using LanguageServer
catch
Pkg.add(\"LanguageServer\")
using LanguageServer
end;
project_path = pwd();
depot_path = get(ENV, \"JULIA_DEPOT_PATH\", \"\");
symbol_server_path = joinpath(homedir(), \".cache\", \"emacs\", \"julia_lsp_symbol_store\");
mkpath(symbol_server_path);
server = LanguageServer.LanguageServerInstance(stdin, stdout, project_path, depot_path, nothing, symbol_server_path, true)
server.runlinter = true;
run(server);"
"The startup script for Julia LanguageServer.")
(add-to-list 'eglot-server-programs
`(julia-mode . ("julia" "--project=@eglot" "--startup-file=no" "--history-file=no" "--quiet" "--depwarn=no" "-e"
,my/julia-lsp-script-content)))
现在许多语言都支持了 tree-sitter , emacs 在这方面有一个社区支持版本 julia-ts-mode ,目前已经一年没有更新。只能使用 julia-mode 。
emacs 有许多包(julia-repl ,julia-vterm)为 Julia 提供了与 REPL 的交互,比如打开 REPL 并激活当前 .jl 文件的 Project ,发送当前行或当前区域的代码到 REPL ,执行整个 .jl 文件等。我使用 vterm 作为后端,但总是无法继承 dirnev 与 envrc 激活的环境变量。为此我定义了几个函数替代这些包,实现与 REPL 的交互:
(use-package julia-mode
:ensure t
:mode ("\\.jl\\'" . julia-mode)
:hook ((julia-mode . corfu-mode)
(julia-mode . eglot-ensure))
:bind (:map julia-mode-map
("C-c C-z" . mj/julia-create-repl)
("C-c C-c" . mj/julia-send-region-or-line)
("C-c C-b" . mj/julia-send-buffer)
("C-c C-f" . mj/runic-format-buffer))
:config
(defun mj/runic-format-buffer ()
(interactive)
(let ((p (point)))
(shell-command-on-region
(point-min) (point-max)
"runic --stdin-filename=-"
(current-buffer) t)
(goto-char p)))
(defun mj/julia-create-repl ()
(interactive)
(let* ((buf-name "*julia*")
(julia-buf (get-buffer buf-name)))
(if julia-buf
(pop-to-buffer julia-buf)
(save-selected-window
(let ((target-win (or (window-in-direction 'right)
(window-in-direction 'left)
(split-window-right))))
(with-selected-window target-win
(vterm buf-name)
(vterm-send-string "clear\n")
(vterm-send-string "julia\n")
(mj/julia-send-code "using Pkg; Pkg.activate(Base.current_project())")
))))))
(defun mj/julia-send-code (code)
(let ((the-buffer-name "*julia*"))
(if-let* ((buf (get-buffer the-buffer-name)))
(with-current-buffer buf
(vterm-send-string code t)
(vterm-send-return))
(message "Buffer does not exist"))))
(defun mj/julia-send-buffer ()
(interactive)
(if-let* ((file (buffer-file-name)))
(mj/julia-send-code (format "include(\"%s\")" file))
(message "Buffer is not visiting a file")))
(defun mj/julia-send-region-or-line ()
(interactive)
(let (code)
(if (use-region-p)
(setq code (buffer-substring-no-properties (region-beginning) (region-end)))
(setq code (thing-at-point 'line t)))
(when code
(let ((trimmed-code (string-trim-right code)))
(mj/julia-send-code trimmed-code)))))
)
format 使用 Runic 替代了 Lsp 自带的 format 功能。