✨ feat(vscode): 代码阅读加 Celery 库规则,定时任务表里的任务名能跳到任务函数
This commit is contained in:
1 parent
905d7845b5
commit
68104e2cba
22 files changed
+2228
-139
No files matched your search
@@ -0,0 +1,81 @@
|
||||
import type { WorkspaceFolder } from 'vscode'
|
||||
import type { IndexHost, WorkspaceFile } from './workspaceIndex'
|
||||
import * as path from 'node:path'
|
||||
import { minimatch } from 'minimatch'
|
||||
import { RelativePattern, Uri, workspace } from 'vscode'
|
||||
|
||||
/** 建索引时总不看的目录:虚拟环境、依赖、Python 字节码缓存。 */
|
||||
const ALWAYS_EXCLUDED = ['**/.venv', '**/node_modules', '**/__pycache__']
|
||||
|
||||
/** 这个工作区文件夹里不看的:files.exclude、search.exclude 里开着的(带 when 条件的不算),加上总不看的目录。 */
|
||||
function excludedPatterns(folder: WorkspaceFolder): string[] {
|
||||
const enabled = (section: string): string[] =>
|
||||
Object.entries(workspace.getConfiguration(section, folder.uri).get<Record<string, unknown>>('exclude') ?? {})
|
||||
.filter(([, on]) => on === true)
|
||||
.map(([pattern]) => pattern)
|
||||
return [...new Set([...enabled('files'), ...enabled('search'), ...ALWAYS_EXCLUDED])]
|
||||
}
|
||||
|
||||
/** 排除规则写的可能是目录(`**\/.git`),目录里的文件也算排除。 */
|
||||
function isExcluded(relative: string, patterns: readonly string[]): boolean {
|
||||
return patterns.some(pattern => minimatch(relative, pattern, { dot: true }) || minimatch(relative, `${pattern}/**`, { dot: true }))
|
||||
}
|
||||
|
||||
/**
|
||||
* 交给 findFiles 的排除 glob:让它别去翻 .venv 这种大目录。带花括号、逗号的写法拼不进一个 `{…}`,
|
||||
* 留给找到之后再按 isExcluded 筛。
|
||||
*/
|
||||
function excludeGlob(patterns: readonly string[]): string {
|
||||
return `{${patterns.filter(pattern => !/[{},]/.test(pattern)).flatMap(pattern => [pattern, `${pattern}/**`]).join(',')}}`
|
||||
}
|
||||
|
||||
function workspaceFile(uri: Uri): { file: WorkspaceFile, folder: WorkspaceFolder } | undefined {
|
||||
const folder = workspace.getWorkspaceFolder(uri)
|
||||
if (!folder)
|
||||
return undefined
|
||||
return { folder, file: { uri: uri.toString(), folder: folder.uri.toString(), path: path.posix.relative(folder.uri.path, uri.path) } }
|
||||
}
|
||||
|
||||
/** 库规则的工作区索引在 VS Code 里怎么找文件、读文件、盯改动;解析交给调用方(语言插件 + 解析运行时)。 */
|
||||
export function workspaceIndexHost(parse: IndexHost['parse'], report: IndexHost['report']): IndexHost {
|
||||
return {
|
||||
async list(include) {
|
||||
const found = await Promise.all((workspace.workspaceFolders ?? []).map(async (folder) => {
|
||||
const patterns = excludedPatterns(folder)
|
||||
const uris = await workspace.findFiles(new RelativePattern(folder, include), excludeGlob(patterns))
|
||||
return uris.flatMap((uri) => {
|
||||
const file = workspaceFile(uri)?.file
|
||||
return file && !isExcluded(file.path, patterns) ? [file] : []
|
||||
})
|
||||
}))
|
||||
return found.flat()
|
||||
},
|
||||
async read(file) {
|
||||
return new TextDecoder('utf-8').decode(await workspace.fs.readFile(Uri.parse(file.uri)))
|
||||
},
|
||||
watch(include, listener) {
|
||||
const forward = (kind: 'change' | 'create') => (uri: Uri): void => {
|
||||
const found = workspaceFile(uri)
|
||||
if (found && minimatch(found.file.path, include, { dot: true }) && !isExcluded(found.file.path, excludedPatterns(found.folder)))
|
||||
listener({ kind, file: found.file })
|
||||
}
|
||||
// 删目录时 VS Code 只报目录本身,匹配不上 include,所以删除另用一个什么都盯的
|
||||
const files = workspace.createFileSystemWatcher(include, false, false, true)
|
||||
const deletions = workspace.createFileSystemWatcher('**/*', true, true, false)
|
||||
const subscriptions = [
|
||||
files,
|
||||
deletions,
|
||||
files.onDidChange(forward('change')),
|
||||
files.onDidCreate(forward('create')),
|
||||
deletions.onDidDelete((uri) => {
|
||||
const found = workspaceFile(uri)
|
||||
if (found)
|
||||
listener({ kind: 'delete', file: found.file })
|
||||
}),
|
||||
]
|
||||
return { dispose: () => subscriptions.forEach(subscription => subscription.dispose()) }
|
||||
},
|
||||
parse,
|
||||
report,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user