🐛 fix(vscode): 代码阅读工作区索引跳过嵌套的 checkout 和忽略文件忽略的,任务名不再查到两处
This commit is contained in:
1 parent
de355661f0
commit
113b02657c
7 files changed
+202
-12
No files matched your search
@@ -0,0 +1,77 @@
|
||||
import type { Ignore } from 'ignore'
|
||||
import ignore from 'ignore'
|
||||
|
||||
/** 读一个工作区文件夹里的东西,路径相对这个文件夹、用 / 分隔。 */
|
||||
export interface FolderReader {
|
||||
/** 有没有这个文件或目录。 */
|
||||
exists: (path: string) => Promise<boolean>
|
||||
/** 文本文件的内容;没有、读不了时没有。 */
|
||||
read: (path: string) => Promise<string | undefined>
|
||||
}
|
||||
|
||||
/** 和 VS Code 搜索一样读的忽略文件(search.useIgnoreFiles)。 */
|
||||
const IGNORE_FILES = ['.gitignore', '.ignore']
|
||||
|
||||
/** path 所在的各层目录,从工作区文件夹('')往下,不含 path 自己。 */
|
||||
function directoriesOf(path: string): string[] {
|
||||
const parts = path.split('/').slice(0, -1)
|
||||
return ['', ...parts.map((_, i) => parts.slice(0, i + 1).join('/'))]
|
||||
}
|
||||
|
||||
function joined(directory: string, name: string): string {
|
||||
return directory ? `${directory}/${name}` : name
|
||||
}
|
||||
|
||||
/**
|
||||
* 工作区文件夹里建索引时还要跳过的文件(files.exclude、search.exclude 之外):
|
||||
* ① 在自己是一份 checkout 的子目录里(带 `.git` 文件或目录:子仓库、git worktree,例如 `.claude/worktrees/…`)——
|
||||
* 那是另一份代码的副本,算进来同一个定义会出现两次;工作区文件夹自己的 `.git` 不算。
|
||||
* ② useIgnoreFiles 开着时(同 VS Code 的 search.useIgnoreFiles,默认开),被路上各层目录的 `.gitignore`、`.ignore` 忽略的:
|
||||
* 每层的规则按 gitignore 写法、相对那一层目录判断;哪一层忽略了就跳过,下层不能再把上层忽略的加回来。
|
||||
* 工作区文件夹上面的目录、全局的忽略文件、`.git/info/exclude` 不读(VS Code 的 search.useParentIgnoreFiles、
|
||||
* search.useGlobalIgnoreFiles 默认也不读)。
|
||||
* 一个实例记着查过的目录,一次扫描、一个文件改动各用一个。
|
||||
*/
|
||||
export function folderScope(reader: FolderReader, useIgnoreFiles: boolean): { skips: (path: string) => Promise<boolean> } {
|
||||
const checkouts = new Map<string, Promise<boolean>>()
|
||||
const rules = new Map<string, Promise<Ignore | undefined>>()
|
||||
|
||||
const isCheckout = (directory: string): Promise<boolean> => {
|
||||
let found = checkouts.get(directory)
|
||||
if (!found) {
|
||||
found = reader.exists(joined(directory, '.git'))
|
||||
checkouts.set(directory, found)
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
const rulesIn = (directory: string): Promise<Ignore | undefined> => {
|
||||
let found = rules.get(directory)
|
||||
if (!found) {
|
||||
found = Promise.all(IGNORE_FILES.map(name => reader.read(joined(directory, name)))).then((texts) => {
|
||||
const written = texts.filter((text): text is string => text !== undefined)
|
||||
return written.length > 0 ? ignore().add(written.join('\n')) : undefined
|
||||
})
|
||||
rules.set(directory, found)
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
return {
|
||||
async skips(path) {
|
||||
const directories = directoriesOf(path)
|
||||
for (const directory of directories.slice(1)) {
|
||||
if (await isCheckout(directory))
|
||||
return true
|
||||
}
|
||||
if (!useIgnoreFiles)
|
||||
return false
|
||||
for (const directory of directories) {
|
||||
const rule = await rulesIn(directory)
|
||||
if (rule?.ignores(directory ? path.slice(directory.length + 1) : path))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user