lsp-bridge -- 速度最快的语法补全插件

补丁合并了, 感谢

我顺便把 emmet-ls 的默认 languageId 也设置为空字符串, 这样根据 html 或者 css 的扩展名传递 languageId 给 emmet-ls lsp server

这个我漏看了,我去试试自定义函数了~

1 个赞

现在的话,打开.jsx文件,第一步默认返回的" jsx", 第二步获取到的javascriptract.json字段是"javascriptreact",最终以谁为准呢?我觉得对于single-server 获取id进而覆盖意义不大反而给人感觉更混乱。

不关心的语言不处理它就好了,直接返回nil让lsp-bridge python端根据json来处理就好了。

@manateelazycat get-multi-server-by-project 那个函数始终在报错,最简配置:

(require 'lsp-bridge)

(setq lsp-bridge-python-command "python3.11")
(setq lsp-bridge-enable-log t)
(setq lsp-bridge-get-multi-lang-server-by-project 'my/bridge-multi-server-detect)
(defun my/bridge-multi-server-detect (project_path filepath)
  (and (string= (file-name-extension filepath) "css") "css_tailwindcss"))

(global-lsp-bridge-mode)

打开一个project下的任意css文件得到:

Eval in Emacs: (lsp-bridge--first-start '62793)
Eval in Emacs: (message '"[LSP-Bridge] found language server: /Users/liuyinz/Library/pnpm/tailwindcss-language-server")
Eval in Emacs: (message '"[LSP-Bridge] found language server: /Users/liuyinz/Library/pnpm/vscode-css-language-server")
Eval in Emacs: (message '"[LSP-Bridge] found language server: /Users/liuyinz/Library/pnpm/tailwindcss-language-server")
Eval in Emacs: (message '"[LSP-Bridge] found language server: /Users/liuyinz/Library/pnpm/vscode-css-language-server")
Start lsp server (tailwindcss, vscode-css-language-server) for /Users/liuyinz/code/react/job-site
Eval in Emacs: (message '"[LSP-Bridge] Active project 'job-site', enjoy hacking!")
Handlers: [<class 'core.handler.completion.Completion'>,
 <class 'core.handler.completion_item.CompletionItem'>,
 <class 'core.handler.find_define.FindDefine'>,
 <class 'core.handler.find_type_define.FindTypeDefine'>,
 <class 'core.handler.find_implementation.FindImplementation'>,
 <class 'core.handler.find_references.FindReferences'>,
 <class 'core.handler.peek.PeekFindDefine'>,
 <class 'core.handler.peek.PeekFindReferences'>,
 <class 'core.handler.hover.Hover'>,
 <class 'core.handler.signature_help.SignatureHelp'>,
 <class 'core.handler.prepare_rename.PrepareRename'>,
 <class 'core.handler.rename.Rename'>,
 <class 'core.handler.jdt_uri_resolver.JDTUriResolver'>,
 <class 'core.handler.deno_uri_resolver.DenoUriResolver'>,
 <class 'core.handler.code_action.CodeAction'>,
 <class 'core.handler.formatting.Formatting'>,
 <class 'core.handler.range_formatting.RangeFormatting'>,
 <class 'core.handler.execute_command.ExecuteCommand'>,
 <class 'core.handler.workspace_symbol.WorkspaceSymbol'>,
 <class 'core.handler.call_hierarchy.PrepareCallHierarchy'>,
 <class 'core.handler.call_hierarchy.CallHierarchy'>,
 <class 'core.handler.call_hierarchy.PrepareCallHierarchyIncomingCalls'>,
 <class 'core.handler.call_hierarchy.PrepareCallHierarchyOutgoingCalls'>,
 <class 'core.handler.call_hierarchy.CallHierarchyIncomingCalls'>,
 <class 'core.handler.call_hierarchy.CallHierarchyOutgoingCalls'>,
 <class 'core.handler.document_symbol.DocumentSymbol'>,
 <class 'core.handler.jdtls.jdtls_list_overridable_methods.JdtlsListOverridableMethods'>,
 <class 'core.handler.jdtls.jdtls_add_overridable_methods.JdtlsAddOverridableMethods'>,
 <class 'core.handler.inlay_hint.InlayHint'>,
 <class 'core.handler.semantic_tokens.SemanticTokens'>]

--- [09:10:43.260899] Send initialize request (4455) to 'tailwindcss' for project job-site
{
   "id": 4455,
   "method": "initialize",
   "params": {
      "processId": 95825,
      "rootPath": "/Users/liuyinz/code/react/job-site",
      "clientInfo": {
         "name": "emacs",
         "version": "lsp-bridge"
      },
      "rootUri": "file:///Users/liuyinz/code/react/job-site",
      "capabilities": {
         "workspace": {
            "configuration": true,
            "symbol": {
               "resolveSupport": {
                  "properties": []
               }
            }
         },
         "textDocument": {
            "completion": {
               "completionItem": {
                  "snippetSupport": true,
                  "deprecatedSupport": true,
                  "tagSupport": {
                     "valueSet": [
                        1
                     ]
                  },
                  "resolveSupport": {
                     "properties": [
                        "documentation",
                        "detail",
                        "additionalTextEdits"
                     ]
                  }
               }
            },
            "codeAction": {
               "dynamicRegistration": false,
               "codeActionLiteralSupport": {
                  "codeActionKind": {
                     "valueSet": [
                        "quickfix",
                        "refactor",
                        "refactor.extract",
                        "refactor.inline",
                        "refactor.rewrite",
                        "source",
                        "source.organizeImports"
                     ]
                  }
               },
               "isPreferredSupport": true
            },
            "inlayHint": {
               "dynamicRegistration": false
            },
            "publishDiagnostics": {
               "relatedInformation": true,
               "tagSupport": {
                  "valueSet": [
                     1,
                     2
                  ]
               },
               "versionSupport": true,
               "codeDescriptionSupport": true,
               "dataSupport": true
            }
         },
         "window": {
            "workDoneProgress": true
         }
      },
      "initializationOptions": {}
   },
   "message_type": "request",
   "jsonrpc": "2.0"
}
Traceback (most recent call last):
  File "/Users/liuyinz/.config/emacs/lib/lsp-bridge/lsp_bridge.py", line 512, in event_dispatcher
    getattr(self, func_name)(*func_args)
  File "/Users/liuyinz/.config/emacs/lib/lsp-bridge/lsp_bridge.py", line 766, in _do
    open_file_success = self.open_file(filepath)  # _do is called inside event_loop, so we can block here.
                        ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/liuyinz/.config/emacs/lib/lsp-bridge/lsp_bridge.py", line 584, in open_file
    create_file_action_with_multi_servers(filepath, multi_lang_server_info, multi_servers)
  File "/Users/liuyinz/.config/emacs/lib/lsp-bridge/core/fileaction.py", line 45, in create_file_action_with_multi_servers
    action = FileAction(filepath, None, None, multi_servers_info, multi_servers, external_file_link)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/liuyinz/.config/emacs/lib/lsp-bridge/core/fileaction.py", line 114, in __init__
    self.set_lsp_server()
  File "/Users/liuyinz/.config/emacs/lib/lsp-bridge/core/fileaction.py", line 126, in set_lsp_server
    lsp_server.attach(self)
  File "/Users/liuyinz/.config/emacs/lib/lsp-bridge/core/lspserver.py", line 303, in attach
    self.send_did_open_notification(fa)
  File "/Users/liuyinz/.config/emacs/lib/lsp-bridge/core/lspserver.py", line 459, in send_did_open_notification
    "languageId": self.get_language_id(fa),
                  ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/liuyinz/.config/emacs/lib/lsp-bridge/core/lspserver.py", line 440, in get_language_id
    match_language_id = get_emacs_func_result("get-language-id", self.project_path, fa.filepath, self.server_name, extension_name)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/liuyinz/.config/emacs/lib/lsp-bridge/core/utils.py", line 250, in get_emacs_func_result
    result = epc_client.call_sync(method_name, args)    # type: ignore
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/liuyinz/.local/lib/python3.11/site-packages/epc/handler.py", line 402, in call_sync
    return self._blocking_request(self.call, timeout, name, args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/liuyinz/.local/lib/python3.11/site-packages/epc/handler.py", line 384, in _blocking_request
    return bc.result(timeout=timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/liuyinz/.local/lib/python3.11/site-packages/epc/handler.py", line 116, in result
    raise reply
epc.handler.ReturnError: [Symbol('wrong-number-of-arguments'), Symbol('#'), Brackets(I=[[Symbol('project_path'), Symbol('filepath')], [[Symbol('and'), [Symbol('string='), [Symbol('file-name-extension'), Symbol('filepath')], 'css'], 'css_tailwindcss']], []]), 4]


--- [09:10:43.358664] Recv response (4455) from 'tailwindcss' for project job-site
{
   "jsonrpc": "2.0",
   "id": 4455,
   "result": {
      "capabilities": {
         "textDocumentSync": 1,
         "hoverProvider": true,
         "colorProvider": true,
         "codeActionProvider": true,
         "documentLinkProvider": {},
         "completionProvider": {
            "resolveProvider": true,
            "triggerCharacters": [
               "\"",
               "'",
               "`",
               " ",
               ".",
               "(",
               "[",
               "!",
               "/",
               ":"
            ]
         }
      }
   }
}

--- [09:10:43.453628] Send initialized notification to 'tailwindcss' for project job-site
{
   "method": "initialized",
   "params": {},
   "message_type": "notification",
   "jsonrpc": "2.0"
}

--- [09:10:43.455383] Recv workspace/configuration request (0) from 'tailwindcss' for project job-site
{
   "jsonrpc": "2.0",
   "id": 0,
   "method": "workspace/configuration",
   "params": {
      "items": [
         {
            "section": "editor"
         }
      ]
   }
}

--- [09:10:43.512621] Recv workspace/configuration request (1) from 'tailwindcss' for project job-site
{
   "jsonrpc": "2.0",
   "id": 1,
   "method": "workspace/configuration",
   "params": {
      "items": [
         {
            "section": "tailwindCSS"
         }
      ]
   }
}

--- [09:10:43.512707] Send workspace/didChangeConfiguration notification to 'tailwindcss' for project job-site
{
   "method": "workspace/didChangeConfiguration",
   "params": {
      "settings": {}
   },
   "message_type": "notification",
   "jsonrpc": "2.0"
}

--- [09:10:43.512832] Send response to server request 0 to 'tailwindcss' for project job-site
{
   "id": 0,
   "result": [
      null
   ],
   "message_type": "response",
   "jsonrpc": "2.0"
}

--- [09:10:43.575778] Send response to server request 1 to 'tailwindcss' for project job-site
{
   "id": 1,
   "result": [
      null
   ],
   "message_type": "response",
   "jsonrpc": "2.0"
}

--- [09:10:43.611682] Recv window/logMessage notification from 'tailwindcss' for project job-site
{
   "jsonrpc": "2.0",
   "method": "window/logMessage",
   "params": {
      "type": 4,
      "message": "[Global] Creating projects: [{\"folder\":\"/Users/liuyinz/code/react/job-site\",\"configPath\":\"/Users/liuyinz/code/react/job-site/tailwind.config.js\",\"isUserConfigured\":false,\"documentSelector\":[{\"pattern\":\"/Users/liuyinz/code/react/job-site/tailwind.config.js\",\"priority\":0},{\"pattern\":\"/Users/liuyinz/code/react/job-site/**\",\"priority\":3}]}]"
   }
}
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '())
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '((:key "card" :icon "search" :label "card" :displayLabel "card" :annotation "Search Word" :backend "search-file-words") (:key "color" :icon "search" :label "color" :displayLabel "color" :annotation "Search Word" :backend "search-file-words") (:key "center" :icon "search" :label "center" :displayLabel "center" :annotation "Search Word" :backend "search-file-words")))
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '((:key "hover" :icon "search" :label "hover" :displayLabel "hover" :annotation "Search Word" :backend "search-file-words") (:key "height" :icon "search" :label "height" :displayLabel "height" :annotation "Search Word" :backend "search-file-words")))
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '((:key "height" :icon "search" :label "height" :displayLabel "height" :annotation "Search Word" :backend "search-file-words")))
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '((:key "height" :icon "search" :label "height" :displayLabel "height" :annotation "Search Word" :backend "search-file-words")))
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '((:key "height" :icon "search" :label "height" :displayLabel "height" :annotation "Search Word" :backend "search-file-words")))
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '((:key "hover" :icon "search" :label "hover" :displayLabel "hover" :annotation "Search Word" :backend "search-file-words") (:key "height" :icon "search" :label "height" :displayLabel "height" :annotation "Search Word" :backend "search-file-words")))
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '())
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '((:key "hover" :icon "search" :label "hover" :displayLabel "hover" :annotation "Search Word" :backend "search-file-words") (:key "height" :icon "search" :label "height" :displayLabel "height" :annotation "Search Word" :backend "search-file-words")))
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '((:key "height" :icon "search" :label "height" :displayLabel "height" :annotation "Search Word" :backend "search-file-words")))
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '((:key "height" :icon "search" :label "height" :displayLabel "height" :annotation "Search Word" :backend "search-file-words")))
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '((:key "height" :icon "search" :label "height" :displayLabel "height" :annotation "Search Word" :backend "search-file-words")))
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '((:key "hover" :icon "search" :label "hover" :displayLabel "hover" :annotation "Search Word" :backend "search-file-words") (:key "height" :icon "search" :label "height" :displayLabel "height" :annotation "Search Word" :backend "search-file-words")))
Eval in Emacs: (lsp-bridge-search-backend--record-items '"search-file-words" '())

*** lsp-bridge-try-completion execute predicate 'lsp-bridge-not-only-blank-before-cursor' failed with result: 'nil'

*** lsp-bridge-try-completion execute predicate 'lsp-bridge-not-delete-command' failed with result: 'nil'

*** lsp-bridge-try-completion execute predicate 'lsp-bridge-not-delete-command' failed with result: 'nil'

*** lsp-bridge-try-completion execute predicate 'lsp-bridge-not-delete-command' failed with result: 'nil'

*** lsp-bridge-try-completion execute predicate 'lsp-bridge-not-delete-command' failed with result: 'nil'

*** lsp-bridge-try-completion execute predicate 'lsp-bridge-not-delete-command' failed with result: 'nil'

*** lsp-bridge-try-completion execute predicate 'lsp-bridge-not-delete-command' failed with result: 'nil'

报错哪里用 print 打印一下, 看看到底是什么值, 感觉Python端传了错误的参数给 Elisp 端

我用这个函数试了一下,这是结果:

(setq lsp-bridge-get-multi-lang-server-by-project 'my/bridge-multi-server-detect)
(defun my/bridge-multi-server-detect (project_path filepath &rest args)
  (message "project: %S, filepath: %S, args: %S" project_path filepath args)
  (when (string= (file-name-extension filepath) "css") "css_emmet"))
;; message
;; 识别到css server时候:
project: "/Users/liuyinz/code/react/job-site", filepath: "/Users/liuyinz/Code/react/job-site/src/index.css", args: nil

;;识别到 emmet server的时候:
project: "/Users/liuyinz/code/react/job-site", filepath: "/Users/liuyinz/Code/react/job-site/src/index.css", args: ("/Users/liuyinz/code/react/job-site#emmet-ls" "css")

当我用了&rest 参数后,这次没报错,但是依然无法补全。 因为log文件还是在不停的打印补全信息,很长时间后才停下来,一旦重新输入字符,又会不停的打印,感觉像是把server所有的补全项一股脑的冒出来了。。

我尝试了 css_emmet 和 css_tailwindcss,都是同样的表现,可能跟具体的server无关,在这次 languageId 改动之前,emmet/tailwindcss提供的补全就时灵时不灵的,不过single server没问题就。

你是不是把 lsp-bridge-enable-log 选项打开了? 这个选项只有在调试的时候才能开, 默认不能打开, 影响性能。

我把log关了再试了一次,发现 emmet补全完全没问题了,不过tailwindcss还是没反应,lsp启动了但是没有补全。我记得之前有一段时间是有用的,现在又不行了,不知道是哪出的问题。。。以后再排查吧。 现在先解决函数参数错误的问题吧,虽然我现在加了一个&rest 可以用,我怀疑是不是写get-language-id的函数传的四个参数中间搞混了?

应该是前天晚上太疲劳, 写错名字了

这个补丁修复了

(setq lsp-bridge-get-language-id 'my/bridge-language-id-detect)
(defun my/bridge-language-id-detect (project_path filepath &rest args)
  (message "project: %S, filepath: %S, args: %S" project_path filepath args)
  (when (string= (file-name-extension filepath) "css") "css_emmet"))

已经修好了 :grin:

1 个赞
  "completion": ["pyright"],
  "diagnostics": ["ruff"],

python的补全使用pyright,这个很明显用到了。 诊断和修复用的是ruff,这个在lsp-bridge-mode里咋用呢?或者说咋感觉到用上了?

自问自答吧, ruff应该是干了这两件事情

lsp-bridge-code-format
lsp-bridge-code-action

目的: 打开 zsh 系列文件时不启动bash-language-serve

想法:

  1. 移除默认mode-list
(setq lsp-bridge-single-lang-server-mode-list
      (remove (rassoc "bash-language-server" lsp-bridge-single-lang-server-mode-list)
              lsp-bridge-single-lang-server-mode-list))
  1. 在 函数 lsp-bridge-get-single-lang-server-by-project 中进行判断,只有非zsh文件才返回 “bash-language-server”

问题: 我发现一旦完成了第一步,就不会执行第二步了,判断函数优先级不是应该是最高的吗? @manateelazycat

或者另一种思路,一旦发现是zsh文件,就选择断开server和文件的关联,有这样的函数吗?

目前 lsp-bridge–get-single-lang-server-func 的实现优先级是:

  1. lsp-bridge-get-single-lang-server-by-project
  2. lsp-bridge-get-single-lang-server-by-extension
  3. lsp-bridge-get-single-lang-server-by-file-mode

major mode 是最后判断的, 我觉得你打开 zshrc 类似的文件, major-mode 应该是 sh-mode, 所以 lsp-bridge 分别不出来 bashrc 和 zshrc 的区别。

按道理, 你应该直接用 lsp-bridge-get-single-lang-server-by-project 判断即可

我现在就是用函数判断的,是sh-mode并且filename不匹配 “.zshenv,zsh,zshrc"才返回“bash-language- server”,可是zsh文件的话函数返回nil,就会fallback到lsp-bridge-single-lang-server-mode-list, 依然会自动开启。可是我一旦在第一步里移除所有bash设置,发现判断函数都失效了,所以想问他们会不会互相干扰

相互干扰可能性很低, 建议你把不同的文件和mode配置发送到github, 我下班有时间能重现, 我就知道原因是什么。

现在多种因素只是文字估计有信息差。

有懂csharp的大佬帮忙看看 Support csharp-ls · Issue #524 · manateelazycat/lsp-bridge · GitHub

主要是翻译lsp-mode到lsp-bridge,主要难点是有个解压方式,不懂lsp-bridge怎么接入这一块。

1 个赞

csharp linux好搭建吗?

如果linux可以搭建,我晚上可以研究一下。

在github回复了,linux下可以的。有事直接在github issue联系。