如何在Emacs中将Clojure源文件连接到正在运行的Clojure repl?

5

我正在为一个现有的J2EE Webapp添加功能,在Tomcat容器中,使用Clojure编写我的补充。 我的设置很简单:我只需调用由Clojure生成的静态方法,并从Clojure侧编写所有艰苦工作。 构建过程包括编译Clojure代码(lein uberjar),然后使用该类路径上的jar编译Java代码。

在Webapp init中,我调用了一个生成的类,它启动了一个带有(swank/start-repl)的Swank服务器。 我想能够将我的Aquamacs的slime连接到该服务器,并从那里进行交互式工作(达到一定程度,我不会尝试需要重新编译Java端的操作)。 但是我遇到了一个我不太理解的情况。 如果我执行\M-x slime-connect,我会得到一个REPL提示(在被通知没有下位Lisp进程之后,我认为这是可以的,因为下位Lisp进程在Emacs控制之外运行)。 我可以完美地评估形式,甚至可以检查诸如 my.own.namespace / my-var 这样的事物。 但是,如果我访问已经编译的Clojure代码文件,我似乎不能使slime将其识别为源。 考虑一个简单的Clojure文件:

(ns my.namespace
  (:gen-class
   :name my.namespace
   :methods [#^{:static true} [testFunc [] void]]))

(def *secret* "shhhh")

(defn -testFunc []
  (println (str "our secret is: " secret)))

假设这个代码被编译并包含在Web应用程序加载的uberjar中,我可以评估/检查`my.namespace / * secret *`。但是,如果我试图在代码缓冲区内评估,Slime会认为我在`user`命名空间中(这甚至可能有意义!)。但现在我只剩下一个可行的选项-我必须逐个评估文件中的所有表单!`\C-c \C-l`(加载源文件)什么都不做- 显然只返回nil并且没有其他输出。编译所有内容似乎就是这样- 它会编译,如果找到错误,则会显示错误,但不会更改我的命名空间。最奇怪的是`\C-〜`(同步软件包和目录),在Common Lisp中使用它正好做我想要的事情,但在这里它会永久冻结clojure REPL。
总之,有一个选择是切换到REPL,键入`(in-ns 'my.namespace)`,然后一切都能正常工作。但是,当Clojure文件数量增加时(因为代码缓冲区的命名空间不会自动更改!),这根本不够实际。
我的问题是,我是否缺少基本的命令/配置-或者这种行为发生的明显原因是什么?

引用问题:“编译所有内容似乎就是这样做” - 你的意思是什么?也就是说,当你说“编译所有内容”时,你指的是哪种加载Clojure代码的方法(Clojure代码总是被编译的,但你可能有一些SLIME函数在脑海中)。此外,你期望得到哪个确切的结果? - Michał Marczyk
编译所有内容 - 在Emacs中按下C-c C-k将编译文件缓冲区中的所有形式,呈现这些形式的错误,并有效地使重新定义(和新定义)的形式动态可用(例如,Web应用程序调用将使用这些新编译的代码)。抱歉我的表述可能不够清晰。 - Edgar Gonçalves
我原本期望的行为类似于使用 M-x swank-clojure-project 启动 SLIME 时所见到的行为:当切换到文件的缓冲区时,命名空间会自动更改,因此在那里评估某些内容将不会使用 user 命名空间。或者,使用 C-c C-k / C-c C-l 编译/加载缓冲区应该评估所有表单,包括最初的 (ns ...),从而切换到特定的命名空间并使所有后续变量属于它。这不是标准设置中预期的行为吗?(我可能看错了...) - Edgar Gonçalves
3个回答

5

我可能误解了你的问题,但是在访问emacs中的这个假设缓冲区时,你不能按下C-c C-k来编译当前Clojure实例(Slime连接到哪里)吗?

然后,在Slime缓冲区中,使用(in-ns 'my.namespace)切换到此命名空间。然后你应该可以访问在该命名空间中编译的内容。


嗯,正如我在问题的评论中所写的那样,编译将起作用,但我的观点是,在编译/加载文件后,我习惯于(in-ns 'my.namespace)是隐式的。在我使用SLIME的每个Common Lisp实现中都是这样工作的,如果我没记错的话;此外,在一个由10个或更多文件组成的项目中手动切换命名空间(特别是在需要进行多次Clojure重启时)会导致疲劳 - 不久之后就会变得疯狂:) - 所以,如果我没有做错什么,并且我正在进行的交互应该是这样的,那么我将编写一个解决方法! - Edgar Gonçalves
尽管现在的情况让我并不完全满意,但你回答了我的问题——这不是我的设置问题,而是“正常”的行为。这是第一次尝试解决此问题:http://gist.github.com/522706 - 当切换文件时它仍然不会自动更新,但添加一个键盘快捷方式到那个功能,你会节省一些时间。谢谢,Isaac! - Edgar Gonçalves
感谢提供 Emacs 函数,我可能需要将其添加到 .init 文件中。不自动切换到您刚刚评估的缓冲区的命名空间可能很有用:如果您没有在导入的库上设置 :reload,但又对该库进行了更改,则需要重新编译它,但您不想进入其命名空间。因此,默认值确实是有道理的,只是通常不太方便。 - Isaac
嗯,我得学习那个:reload技巧 - 它的缺点是什么,一个活动文件更新监视器吗?但我明白你的意思,并且同意 - 我会保持手动。我刚刚对fn进行了一些概括,见这里。如果没有命名空间或者使用前缀,它就会回退到C-c M-p - Edgar Gonçalves
我决定取消接受这个答案 - 尽管它提供了一个有用的函数(我仍将使用),但我找到了一个直接解决我提到的问题的方法。请查看我的答案。无论如何,谢谢你,Isaac! - Edgar Gonçalves
显示剩余2条评论

2

swank-clojure一直没有自动切换命名空间的功能,虽然它可能是一个可选的slime特性,可以与Clojure配合使用。但是,对于我来说,使用C-c M-p将repl切换到当前缓冲区的命名空间始终有效,我从未听说过有人遇到过问题。

您是否正在运行最新稳定版本的clojure-mode和slime-repl?您是否已安装swank-clojure.el?(您不应该需要它。)这可能是由于elisp库的版本不匹配引起的。如果这不是问题所在,那么可能是Aquamacs的错误;swank-clojure是设计用于GNU Emacs的。如果您是从主干运行而不是最新的elpa版本,则也可能是slime的错误。


1
我几个小时前意识到C-c M-p在我的设置(Aquamacs,clojure-mode 和 slime-repl 的最新稳定版本,clojure-1.2-rc1)上能够完美地工作。一直卡住的是C-c ~,但我猜测这个 SLIME 命令只适用于 Common Lisp 实现。我期望起作用的是在文件上执行 C-c C-l(加载) - 我期望名称空间会改变,但它没有! 为了让未来的读者受益,我在 http://gist.github.com/522706 中概述的函数也可以工作,但它不会询问名称空间 - 它从缓冲区中第一个(ns ...)形式猜测。 - Edgar Gonçalves

1

我刚刚发现解决这个问题的罪魁祸首是slime-redirect-inferior-output,它在slime-repl.el中被调用,而我设置了一个钩子来调用它。结果发现,如果没有inferior-lisp-process(也就是从emacs内部启动的swank服务器),它就无法正常工作。

因此,一个快速的解决方法是从该函数中删除error表单,就像this一样。现在钩子可以继续执行,并且命名空间会自动计算。正如预期的那样。感谢您的建议,尽管它们让我找到了这个解决方案!


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