调整 Python 的 `include` 和 `includeexpr` 表达式后无法使 `include-search` 工作

Unable to get `include-search` working after tweaking `include` & `includeexpr` expression for Python

我正在尝试在我的 vim 配置中使用基于 include-search 的设置。这导致 [i[i[d]d 等命令无法按预期工作。此外,当 :checkpath! 为 运行 查看包含的文件时,它也没有按预期工作。 你能帮我告诉我我做错了什么以及我能做些什么来解决这个问题吗?

  1. 我的项目根目录如下所示。我故意不列出 venv 目录的条目,只是为了保持条目列表较小。

    \> tree -I "build|dist|*egg*|__pycache__" -L 2
    .
    ├── MANIFEST.in
    ├── README.md
    ├── acs_datamodels-0.0.1-py3-none-any.whl
    ├── ai_core-0.0.1-py3-none-any.whl
    ├── datamodels
    │   ├── __init__.py
    │   ├── fabric.py
    │   ├── literals.py
    │   └── precompute.py
    ├── encrypt.log
    ├── setup.py
    ├── tests
    │   ├── __init__.py
    │   ├── data
    │   ├── test_fabric.py
    │   └── test_precompute.py
    └── venv
        ├── bin
        ├── include
        ├── lib
        ├── lib64 -> lib
        └── pyvenv.cfg
    
  2. 我的:set path?长得像

    path=datamodels,venv/lib/python3.7/site-packages/*/**2
    
  3. 我的:set include?长得像

    ^\s*\(from\|import\)\s*\zs\(\S\+\|\S\+\s*import\{1}\s*\S\+\)\ze\($\|\s*as\|,\)
    
  4. 我的:set includeexpr?长得像

    includeexpr=PyInclude(v:fname)
    
  5. ~/.vim/after/ftplugin/python.vim内容如下

    set shiftwidth=4 tabstop=4 softtabstop=4 expandtab autoindent smartindent
    setlocal wildignore=*.pyc,bin,*egg*,__pycache__/*,build,dist
    setlocal include=^\s*\(from\\|import\)\s*\zs\(\S\+\\|\S\+\s*import\{1}\s*\S\+\)\ze\($\\|\s*as\\|,\)
    
    function! PyInclude(fname)
        echom "fname: " a:fname
        let parts = split(a:fname, ' import ')
        echom "parts: " parts
        let l = parts[0]    " (1) logging (2) ai_core.commons (3) datamodels.literals
        if len(parts) > 1
            let r = parts[1]    " (1) datamodels (2) datetime 
            let joined = join([l, r], '.')  " datetime.datetime, ai_core.commons.decode_token
            let fp = substitute(joined, '\.', '/', 'g') . '.py' " datetime/datetime, ai_core/commons/decode_token
            let found = glob(fp, 1)
            echom "parts > 1" found
            if len(found)
                return found
            endif
        endif
        let kp = substitute(l, '\.', '/', 'g') . '.py'
        echom "parts < 1" kp
        return kp    " ai_core/commons.py, datamodels/literals.py
    endfunction
    
    setlocal includeexpr=PyInclude(v:fname)
    
  6. :checkpath! 的输出如下。请注意输出还包含一些我添加的调试信息。如果您看到 from typing import Dictimport logging 已从 venv 目录中识别出来,但其他库:from ai_core.commons import decode_token 我从 wheel 文件安装的库无法识别,即使它们'在 venv 目录中。此外,无法识别当前文件相对路径 import datamodels.literals 中的其他模块。

    fname:  typing import Dict
    parts:  ['typing', 'Dict']
    parts > 1
    parts < 1 typing.py
    venv/lib/python3.7/site-packages/pip/_internal/utils/typing.py
    
    fname:  ai_core.commons import decode_token
    parts:  ['ai_core.commons', 'decode_token']
    parts > 1
    parts < 1 ai_core/commons.py
    ai_core.commons import decode_token  NOT FOUND
    
    fname:  logging
    parts:  ['logging']
    parts < 1 logging.py
    venv/lib/python3.7/site-packages/pip/_internal/utils/logging.py
    
    fname:  datamodels.literals
    parts:  ['datamodels.literals']
    parts < 1 datamodels/literals.py
    datamodels.literals  NOT FOUND
    

我从 this video, thanks a ton to Leeren 那里学到了这些,向我介绍了这些以及更多内容。

&path 是要在其中搜索文件的目录白名单。基本上,您可以将 include search 视为将文件名附加到 &path 中列出的每个目录,然后查看该路径是否通向某些东西(它实际上并不像那样工作,但对于问题而言,这是一个足够好的心智模型手)。

例如,如果您的文件名是 quux 而您的 &pathfoo,bar,baz,那么 Vim 将搜索以下文件:

foo/quux
bar/quux
baz/quux

这里,文件名是 datamodels/literals.py 而你的 &pathdatamodels,venv/lib/python3.7/site-packages/*/**2 所以 Vim 将搜索:

datamodels/datamodels/literals.py
venv/lib/python3.7/site-packages/*/**/datamodels/literals.py
venv/...

由于相当明显的原因而失败。

有两种方法可以解决这个问题:

  • 在您的 &path 前面加上 .,,,它告诉 Vim 在当前文件的目录和工作目录中搜索文件。您的 &path 将如下所示:

    .,,datamodels,venv/lib/python3.7/site-packages/*/**2
    

    这应该让 Vim 轻松找到 datamodels/literals.py:

    datamodels/literals.py    <-- BINGO!
    datamodels/datamodels/literals.py
    venv/lib/python3.7/site-packages/*/**/datamodels/literals.py
    venv/...
    
  • 制作你的 &includeexpr return literals.py,可以用你当前的 &path 找到,而不是 datamodels/literals.py:

    datamodels/literals.py    <-- BINGO!
    venv/lib/python3.7/site-packages/*/**/literals.py
    venv/...
    

IMO,你应该两者都做。

:set path=.,,venv/lib/python3/site-packages/**2 施展了魔法。更正为 **2 以表示具有 2 级深度的子​​目录,而不是我原来的 post 中的 */**2。 是的,正如 @romail 指出的那样,我确实尝试将 includeexpr 中的每个文件名附加到 path 中的目录列表中。谢谢。