感谢反馈, enjoy it.
lsp-bridge 最新版本更新了:
-
Semantic Tokens 的功能目前可以做到开箱即用, 在对应语言开启 lsp-bridge-semantic-tokens-mode 选项即可, 之前需要手动配置各种 Semantic Tokens 所需的 Face
-
修复Elisp后端跳转定义后不能返回之前的跳转位置
-
优化了 Rust 语言补全候选词的显示算法, 去掉函数候选词前面的 pub const 等重复字符串,补全看起来更优雅
-
添加了对LSP Server进度显示的支持
-
添加对 TailwindCSS 的支持, 前端的同学现在可以混合 TaildwindCSS 和其他LSP Server, 比如 HTML 和 CSS等
-
增加了对局部格式化 RangeFormatting 协议的支持
-
修复了很多远程服务器代码补全时遇到的小问题
-
增加了nextls, graphql-lsp等新的语言服务器, 现在支持的语言服务器超过 80 个,官方多语言混合服务器支持超过 12 个
欢迎大家更新到最新的 lsp-bridge 版本,欢迎反馈问题,我周末有时间都会很快修复的。
怎么重现的?
我看错误是在 elisp 这里, 应该是你自定义的 lsp-bridge-get-multi-lang-server-by-project 函数的错误?
(setq lsp-bridge-get-multi-lang-server-by-project #'my/bridge-multi-server-detect)
(defun my/bridge-multi-server-detect (project-path filepath)
"Detect right server config for multi servers.."
(save-excursion
;; detect for web dev
(let* ((tailwindcss-p (directory-files
project-path
'full
"tailwind\\.config\\.\\(j\\|cj\\|mj\\|t\\)s\\'"))
(ext (file-name-extension filepath))
(jsx-p (or (string= ext "jsx")
(memq major-mode '(jtsx-jsx-mode js-jsx-mode))))
(tsx-p (or (string= ext "tsx")
(memq major-mode '(jtsx-tsx-mode
jtsx-typescript-mode
tsx-ts-mode))))
(html-p
(or (memq ext '("htm" "html"))
(memq major-mode '(mhtml-mode html-mode html-ts-mode web-mode))))
(css-p
(or (memq ext '("css"))
(memq major-mode '(css-mode css-ts-mode less-css-mode scss-mode)))))
(cond
((and tailwindcss-p jsx-p) "jsreact_tailwindcss")
((and tailwindcss-p tsx-p) "tsreact_tailwindcss")
((and tailwindcss-p html-p) "html_emmet_tailwindcss")
;; ((and tailwindcss-p html-p) "html_tailwindcss")
(html-p "html_emmet")
((and tailwindcss-p css-p) "css_emmet_tailwindcss")
;; ((and tailwindcss-p css-p) "css_tailwindcss")
(css-p "css_emmet")))))
我自定义的detect函数没问题啊,我git init之后就能正常使用了。
这里的 project_path 是根据 get_project_path 函数来获取文件所在的项目 root 路径, 分别根据 lsp-bridge-get-project-path-by-filepath, .dir-locals.el 或者 git init 来获取。
如果都不符合就返回文件的路径。
lsp-bridge 不论是单服务器还是多服务器, 理论上都是支持单文件补全的(只要设置的lsp 服务器支持单文件即可), 所以 lsp-bridge 不能根据 project_path 不是一个目录就去报错。
你遇到的错误是你自定义的函数调用了 directory-files , directory-files 这个函数如果发现路径不是目录就会直接报错。
上面是我的思考逻辑, 如果有更好的建议欢迎讨论。
哦哦,所以我得先检查 project-path是否为directory然后在调用directory-file,这样应该就行了。可是这不符合直觉啊,默认设为文件的目录应该更好吧。
主要是一些语言支持单文件就可以补全, 比如 Python, 这种类似的语言很多, 如果强制发现 project-path 不是目录就报错, 就会让这些单文件就可以补全的语言觉得莫名其妙。
我主要是没有想到两全其美的办法。
我觉得 languageId
的检测还是要找办法优化,要不然的话就得写一堆的
taiwindcss-{css,html,sveltve, vue,astro,…}.json 和 emmet-{css,html,javascriptreact,…}.json 来进行组合。
@manateelazycat 我想了一下,multi-server其实有两个层面的问题:
-
是要不要启动 emmet/tailwindcss 的问题,这可以通过 已经提供的 lsp-bridge-get-multi-lang-server-by-project 来解决,比如检测当前
project
是否有tailwindcss.config.js
配置文件来决定启动javascriptreact.json
或者javascriptreact-tailwindcss.json
-
是如何动态传入languageId 给 emmet/tailwindcss 来适应不同的文件,默认这些附加的server的
languageId
属性是应该一个数组,比如[html,css,javascriptreact, vue, ....]
, 写死在具体的json文件中。python端一旦检测到不是string,是数组,就调用 elisp的一个predicate 函数获取具体的返回值就行了,返回值只要合法中就可以启动。 predicate 函数应当类似于:
(defun lsp-bridge-multi-server-languageid-detect (projcet-path, filepath, server-name)
(cl-case server-name
('tailwindcss
(cond
((and (eq ext "html") ....). "html")
(.....))
('emmet-ls
(cond
((and ....))
)
反正这种 server 没几个,让用户自己去判断好了。这样最大的好处就是只需要一个 tailwindcss.json就行了,用户配置multi-server的时候无脑写,不用去留心到底是哪个 tailwindcss-css/html/javascriptreact…
在Single LSP Server的实现中, 是支持 languageIds 的, 根据不同的扩展名返回不同的 languageId, 参考 lsp-bridge/langserver/vscode-css-language-server.json at 4489fa4f5ee353ea94c02f1a4fdc5eb360ee29fd · manateelazycat/lsp-bridge · GitHub
Multi LSP Server中主要是 TailwindCSS LSP Server 比较特殊, 它需要动态的返回 languageId, 不能完全依据文件扩展名来判定。
我也看了你写的多语言自定义函数, 确实比较复杂, 也许你可以详细写一下你遇到的困难, 我看看有没有增强的办法, 你自己写的解决方法, 但是我不能明白你遇到的困难和场景, 导致我现在我看不懂你想表达的意思。
我理想中的配置是这样的:
-
taiwindcss.json 只需要一个, “languageId”:字段 把 tailwindcss-intellisense/packages/tailwindcss-language-service/src/util/languages.ts at master · tailwindlabs/tailwindcss-intellisense · GitHub 所有支持的languageId 写成数组,写死在json文件中。
-
自定义任何multi-server.json 比如 javascriptreact-tailwindcss.json, 都用到那一个tailwindcss.json.
-
python端启动tailwindcss.json时候检测是languageId是一个数组,就调用用户自定义的predicate函数获取具体文件的id string,只要这个string 在数组内,就用它来启动。 前面的predicate函数我写错了,应该要三个参数,两个path 外加一个python传递给函数的server-name才行,这样的话 python那边就不用搞那些 {“jsx” : “javascriptreact” …} 之类的东西了,以后如果还有其他的类似server,就只需要添加一个写满所有支持languageId的json文件即可,
感觉是一劳永逸的解决方法啊,不过我不会python…。。
这个文件我看了, 但是我没有看懂 TailwindCSS 的逻辑呀。
比如我有一个文件的扩展名, 怎么转换到连接里面哪些扩展名?
不,那是用户的事,用户自己写predicate函数来判断。 python端只需要检测到languageId是一个数组,就调用函数接收一个string返回值然后启动就行。 用户怎么判断,那就百无禁忌,astro项目,svelte项目啊各有各的特点,svelte文件可以直接用后缀判断,其他的有些不能用后缀判断的,那就看项目特点了,有没有特殊的配置文件啦,在特定的目录下的文件比如 src/ 都返回某一类值。。或者最笨的办法就是路径判断,,某一个project 路径match到了就返回某一个string… 额,其实 应该要提供的option的是一个predicate 函数列表,官方可以写一个default的,比如就根据后缀来predicate,如果返回nil就不启动server,用户自己可以追加自己的predicate函数在后面,一直到整个列表都没有返回值才宣告server failed。我觉得用后缀应该就能解决一大半问题了。
理一下哈:
- LSP 协议要求打开文件的时候传递 languageId lsp-bridge/core/lspserver.py at 4489fa4f5ee353ea94c02f1a4fdc5eb360ee29fd · manateelazycat/lsp-bridge · GitHub
- 大多数LSP Server 的 languageId 都是固定的, 写到 langserver.json 就好了
- 但是 TailwindCSS LSP Server 的 languageId 不能写死, 因为他支持很多扩展名
目前 lsp-bridge 的机制是:
- 有特殊的就写到 TAILWINDCSS_LANGUAGE_ID_DICT 里做字典对应
- 没有特殊的就反馈文件的扩展名给 TailwindCSS LSP Server
我的问题是, 难道 TAILWINDCSS_LANGUAGE_ID_DICT 这种字典对应关系都不行吗? 还是说不同的前端框架项目, 同样一个文件扩展名传递给 TailwindCSS 的 languageId 都不一样?
我主要是没有理解你的需求。
“同样一个文件扩展名传递给 TailwindCSS 的 languageId 都不一样”, 这就是核心问题,所以我说你就让用户自己去判断不同的项目,文件,该启动怎样的参数,官方就提供一个根据后缀来判断的default predicate,而且这个predicate还要放在列表的最后面,让用户的predicate在前面判断。
emmet-ls 支持 的 filetypes = { “css”, “eruby”, “html”, “javascript”, “javascriptreact”, “less”, “sass”, “scss”, “svelte”, “pug”, “typescriptreact”, “vue” } 其实还相对简单,tailwindcss才是终极魔王。
你其实就是想让我给一个 Elisp 的接口, 传入 project-path 和 file-name , 然后根据用户自定义的 elisp 函数, 返回一个 languageId ?
只要 TailwindCSS 的 languageId 返回对了, 其他 Web LSP Server 和 TailwindCSS 的混合补全就走 lsp-bridge 正常的 multi-server.json ?
不,这个接口还需要一个额外的参数,就是server-name,是三个参数。然后我就可以
用project-path
filepath
和 server-name
(是tailwindcss还是emmet-ls还是将来出现的其他server“)来精准的返回languageId了,说白了哪怕我用最笨的路径match都能百分之一百的启动对。而且后续维护也省心,不需要的python那里搞一堆字典来match,如果将来的有新的server了,只需要增加一个single server.json 就行了。
给我5分钟吧
我去写关于json的pr