问题,无法找到可执行文件
最新版的eglot已经去掉了原先在eglot.el中为jdtls的特殊兼容代码,而使用jdtls最新的python脚本来启动。
但jdtls的Python脚本只提供了适合于*inx环境的(不带后缀的)jdtls可执行文件。这样以来Windows的环境就无法正常运行起来。
因为,eglot--executable-find函数(底层是executable-find)无法在Windows平台上找到可执行文件(jdtls.exe或者jdtls.bat)。这一点通过执行以上两个文件即可得出结论,以下均返回nil。
(eglot--executable-find "jdtls")
(executable-find "jdtls")
既然问题已经找到了,我们就尝试解决一下。不是找不到可执行文件么?我们来创建一个不就行了?
尝试,生成一个jdtls.exe
既然是使用Python写成的代码,而且jdtls中的代码极其简单(就是调用jdtls.py来组装一个java命令,然后通过os.system运行它),那么我们用pyinstaller生成一个即可。
pip install pyinstaller
pyinstaller --onefile jdtls
mv .\dist\jdtls.exe .
运行这个exe,发现失败了。失败是符合预期的,因为main函数中需要传递sys.argv[1:],但是失败的报错却是非预期的:
Error: Unable to access jarfile '<...省略一部分路径>/org.eclipse.equinox.launcher_1.6.<...省略一部分版本>.jar'
分析,关于jar的错误
实际上,我们直接使用python来运行也是这样的错误。
python .\jdtls
我们尝试修改jdtls.py文件,把最后组装的命令打印出来为:
java -Declipse.application=org.eclipse.jdt.ls.core.id1 -Dosgi.bundles.defaultStartLevel=4 -Declipse.product=org.eclipse.jdt.ls.core.product -Dosgi.checkConfiguration=true -Dosgi.sharedConfiguration.area='D:\Tools\java\jdtls\server\config_win' -Dosgi.sharedConfiguration.area.readOnly=true -Dosgi.configuration.cascaded=true -noverify -Xms1G --add-modules=ALL-SYSTEM --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED -jar 'D:\Tools\java\jdtls\server\plugins\org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar'
其中D:/Tools/java/jdtls/server是我本机存放jdtls的目录。
如果你对Windows下的CMD命令熟悉的话,大概一眼就能看出问题了。那就是路径的问题。预期是:
D:/Tools/java/jdtls/server/plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar
实际是:
'D:\Tools\java\jdtls\server\plugins\org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar'
斜杠与引号的问题。
解决,手写一个可执行文件
我们其实已经拿到了最终的命令,而且纵览整个jdtls以及jdtls.py,它全部的功能就是为了生成这条命令,那么我们直接来简单粗暴的写一个可执行文件来调用这条命令即可。方法有二。
方法一,还是用Python生成exe
但代码内容就非常粗暴了。
# jdtls-for-win.py
import os
import sys
if getattr(sys, "frozen", False):
dir_path = os.path.dirname(os.path.dirname(os.path.abspath(sys.executable)))
else:
dir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
cmd = (
"java"
" -Declipse.application=org.eclipse.jdt.ls.core.id1"
" -Dosgi.bundles.defaultStartLevel=4"
" -Declipse.product=org.eclipse.jdt.ls.core.product"
" -Dosgi.checkConfiguration=true"
f" -Dosgi.sharedConfiguration.area={dir_path}/config_win"
" -Dosgi.sharedConfiguration.area.readOnly=true"
" -Dosgi.configuration.cascaded=true"
" -noverify"
" -Xms1G"
" --add-modules=ALL-SYSTEM"
" --add-opens java.base/java.util=ALL-UNNAMED"
" --add-opens java.base/java.lang=ALL-UNNAMED"
f" -jar {dir_path}/plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar"
f" {' '.join(sys.argv[1:])}"
)
os.system(cmd)
然后生成exe文件,放到bin下即可。
pyinstaller --onefile jdtls-for-win.py
这样就解决了。
方法二,如果你不想生成一个exe
不论你是因为不想安装Python/Pyinstaller或者是其他的原因,你都可以不生成一个exe文件,而换成bat文件的。因为在Windows下,bat也是一个可执行文件的后缀。
但这里需要注意,bat中需要稍微费点精力来解决传参的问题:
@echo off
set CMD_LINE_ARGS=
:setArgs
if ""%1""=="""" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs
:doneSetArgs
java -Declipse.application=org.eclipse.jdt.ls.core.id1 -Dosgi.bundles.defaultStartLevel=4 -Declipse.product=org.eclipse.jdt.ls.core.product -Dosgi.checkConfiguration=true -Dosgi.sharedConfiguration.area=D:/Tools/java/jdtls/server/config_win -Dosgi.sharedConfiguration.area.readOnly=true -Dosgi.configuration.cascaded=true -noverify -Xms1G --add-modules=ALL-SYSTEM --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED -jar D:/Tools/java/jdtls/server/plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar %CMD_LINE_ARGS%
这样也能解决问题。
特殊提醒
以上的所有的前提是server/bin在环境变量PATH里面。