我之前用 (defun my//enable-cquery-if-compile-commands-json ()
是防止(user-error "cannot find project root")
现在打算用
(defun cquery//enable ()
(ignore-errors (lsp-cquery-enable)))
了
我之前用 (defun my//enable-cquery-if-compile-commands-json ()
是防止(user-error "cannot find project root")
现在打算用
(defun cquery//enable ()
(ignore-errors (lsp-cquery-enable)))
了
我倒是觉得这样挺好,这样不是所有项目都会索引吧,想索引自己加个cquery识别的文件就行了。
lsp-python emacs-cquery 都有项目根目录识别问题。提了一个讨论issue [Discussion] project root detection logic · Issue #293 · emacs-lsp/lsp-mode · GitHub
转贴一下:
对于 lsp-python
(lsp-define-stdio-client lsp-python "python"
(lsp-make-traverser #'(lambda (dir)
(directory-files
dir
nil
"\\(__init__\\|setup\\)\\.py")))
'("pyls"))
用__init__.py
判断根很不好,因为子目录也可能有__init__.py
倒是可以像projectile那样,用.git作为project root
我认为没有一个万全的方法,总有例外的情况。即使以 .git 作为判断依据,那些不进入版本控制的三方资源有可能直接一个 git repo 放在工程目录底下:
find . -type d -name ".git"
./.git
./vendor/phpunit/phpunit/.git
...
是不是可以考虑增加一个手动的方法(作为补充),例如 (lsp-open-project-root DIR)
,明确把 DIR
作为 root,不管它上层是什么情况。
想了一下,这样也有问题:如果同时指定了 foo 和 foo/bar 为 project root,然后打开 foo/bar/qux.c 算哪个项目的?这种情况只能启动多个 Emacs 实例了,一个实例一个 project。
我現在用了一個比較噁心的辦法
(cl-defun cquery--get-root ()
"Return the root directory of a cquery project."
(when cquery-project-root-function
(-when-let (root (funcall cquery-project-root-function))
(cl-return-from cquery--get-root root)))
(cl-loop for root in cquery-project-roots do
(when (string-prefix-p (expand-file-name root) buffer-file-name)
(cl-return-from cquery--get-root root)))
(or
(and (require 'projectile nil t) (ignore-errors (projectile-project-root)))
(expand-file-name (or (locate-dominating-file default-directory "compile_commands.json")
(locate-dominating-file default-directory ".cquery")
(user-error "Could not find cquery project root")))))
(defcustom cquery-project-root-function
nil
"A function used to find the project root.
The following methods are applied in order to get the project root.
* `cquery-project-root-function'
* `cquery-project-roots'
* projectile
* `.cquery' or `compile_commands.json'
"
:type 'function
:group 'cquery)
(defcustom cquery-project-roots
nil
"A list of project roots that will be matched against the source filename first
to get the project root, before consulting `projectile' or `project'.
This is useful when your project has subprojects. Otherwise `projectile' and
`project' may think the file resides in a subproject and thus the file
does not belong to the current workspace.
"
:type '(repeat directory)
:group 'cquery)
如果是我,我就添加一个用户命令,让用户手动设置当前buffer对应的文件属于哪个project,检测不靠谱的时候,不如让用户自己选择
file local variable 这种吧,原来用来判断的还是可以留着
现在 lsp-mode 的问题是一旦一个文件设置了对应的 workspace ,很难平滑退出……切换project不行的
你显然没认真看前面各位的回帖。
README.md
比 __init__.py
还不靠谱。以 cquery 为例,third_party 下的每个目录都是一个完整的 git 项目,都有 README.md
:
⋊> find ~/repos/c-c++/cquery -name "README.md"
~/repos/c-c++/cquery/README.md
~/repos/c-c++/cquery/third_party/doctest/doc/html_generated/strapdown.js/README.md
~/repos/c-c++/cquery/third_party/doctest/README.md
~/repos/c-c++/cquery/third_party/loguru/README.md
~/repos/c-c++/cquery/third_party/msgpack-c/README.md
~/repos/c-c++/cquery/third_party/rapidjson/bin/jsonschema/README.md
~/repos/c-c++/cquery/third_party/rapidjson/contrib/natvis/README.md
~/repos/c-c++/cquery/third_party/sparsepp/README.md
你也许会说,向上递归查找,找到最顶层 README.md
。问题是,谁规定了项目目录之外不能有 README.md
,谁保证只有项目顶层有 README.md
?就我这个例子,~/repos
是用来搜集代码的,我就可能放一个 ~/repos/README.md
或 ~/repos/c-c++/README.md
作为目录摘要。
用 .git/.svn/...
这些还相对靠谱一些。但还是有例外的情况,比如我就想窝在 ~/repos/c-c++/cquery/third_party/msgpack-c
这个子项目里面怎么办,所以还得有个手工设定的方法。
嗯,我现在自己的配置用
(setq cquery-project-roots '("~/Dev/llvm-project" "~/Dev/llvm"))
因为不希望 ~/Dev/llvm/{projects,tools}/*/.git
子项目被识别为单独的LSP workspace
projectile 有个
(require 'projectile)
(add-to-list 'projectile-globally-ignored-directories ".cquery_cached_index")
(这些杂事如果要把cquery加到 spacemacs +tools/cquery or +lang/c-c++ 都需要清理的吧…)我elisp很弱,期待别人把这个捡起来… Add +tools/cquery layer by MaskRay · Pull Request #10236 · syl20bnr/spacemacs · GitHub
为什么会由多个项目根目录? 怎么搞得这么复杂? 是为了同时打开多个项目? 还是一个项目分散在多个目录?
这里讨论的是当存在 sub-project 时,不希望 sub-project 下的文件在 lsp-mode workspace 角度被视为 sub-project 的一部分。
因为 language server 检索整个项目(包含sub-project),打开的文件也要和根 project 对应才能用上 lsp信息
一个项目分散在多个目录
cquery 也是能处理的。但其中一个目录要配置成主要的 (lsp-mode.el 中的 :rootUri
),并且下面放 .cquery
或 compile_commands.json
包含其他根目录所有文件的编译命令。
如果lsp-cquery不支持同时多个workspace, 最好是初始化(启动language server)的时候提示用户选择workspace目录, 之后就固定用这个目录.
如果lsp-cquery支持同时多个workspace, 这个有点混乱, 一个文件可能属于多个workspace,只能让用户手工选择, 或者让用户提供规则或优先级顺序.
自动检测结果最好提示一下用户, 让用户知道, 否则很疑惑. 我是把那个get-root的函数改掉了.
有compile_commands.json的时候, cquery是不是只索引compile_commands.json里面包含的文件? 会索引其他文件吗?
另外就是,python有一个package auto-virtualenv,则是使用project root下一个文件作为标识
C/C++ 把包扔给系统管了,然后编译的时候各种参数、环境变量。
我觉得就识别有.cquery
或compile_commands.json
的目录就行了,这个是cquery
自身可控的,就算compile_commands.json
是构建工具生成的,不完全由cquery
控制,但是cmake
不是也可以include
别的目录吗,项目根目录一般也把别的目录包含进来了。
搜了一下,还是有不少 c/c++ 包管理器的:
为什么没有一个成为主流呢,应该是难以达成共识,就像 Linux 发行版一样。
一开始就把系统软件包跟开发混为一用了,也没有 global / local 之分。版本依赖怎么办,就各种环境变量 flag 来处理,然后有了各种复杂无比的构建工具 make/cmake/waf/ninja… 一代代开发者都是这么熬过来的,然后还有点上瘾了。
这个我感觉可能性不大