diff --git a/packages/vipeye-internal/src/analyzer/service.ts b/packages/vipeye-internal/src/analyzer/service.ts index 230f4842..bb7e1d41 100644 --- a/packages/vipeye-internal/src/analyzer/service.ts +++ b/packages/vipeye-internal/src/analyzer/service.ts @@ -67,7 +67,7 @@ import { findConfigFileHereOrUp, findPyprojectTomlFile, findPyprojectTomlFileHereOrUp, - findVpjInChildDirs, + findViperProjectConfigsInChildDirs, } from './serviceUtils'; import { SourceEnumerator } from './sourceEnumerator'; import { IPythonMode } from './sourceFile'; @@ -802,6 +802,9 @@ export class AnalyzerService { let projectRoot = executionRoot; let configFilePath: Uri | undefined; let pyprojectFilePath: Uri | undefined; + // Viper 扩展:工作区根下收集到的所有子项目配置(*.vpj / project.json), + // 供下方遍历累加各子项目的 source_dir / includes 到 defaultExtraPaths。 + let viperChildConfigPaths: Uri[] | undefined; if (commandLineOptions.configFilePath) { // If the config file path was specified, determine whether it's @@ -832,12 +835,15 @@ export class AnalyzerService { // project root directory contains the config file. configFilePath = findConfigFile(this.fs, projectRoot); - // Viper 扩展:LSP 模式下,如果工作区根没有配置文件,扫描一级子目录查找 .vpj。 - // 支持工作区根(如 Viper_VSCode)包含子项目(如 Test/project.vpj)的场景。 - // 找到 .vpj 后,projectRoot 调整为 .vpj 所在目录(后续 L842 逻辑), - // .vpj 的 includes 会被 _initializeFromViperProjectJson 解析为 defaultExtraPaths。 + // Viper 扩展:LSP 模式下,如果工作区根没有配置文件,扫描一级子目录查找所有 + // Viper 项目配置(*.vpj / project.json)。 + // 支持工作区根(如 TransPyC)包含多个子项目(如 TransPyV / ViperTemplateProject)的场景: + // 每个子项目的 source_dir(如 ./App)都应作为全局导入根(App 根级全局可导入), + // includes 作为额外搜索路径。projectRoot 调整为第一个配置所在目录(后续 L850 逻辑), + // 其余配置的 source_dir/includes 由下方遍历 _initializeFromViperProjectJson 累加。 if (!configFilePath && commandLineOptions.fromLanguageServer) { - configFilePath = findVpjInChildDirs(this.fs, projectRoot); + viperChildConfigPaths = findViperProjectConfigsInChildDirs(this.fs, projectRoot); + configFilePath = viperChildConfigPaths[0]; } // If viper is being executed from the command line, the working @@ -904,6 +910,22 @@ export class AnalyzerService { primaryConfig.configFileDirUri, configOptions ); + + // Viper 扩展:工作区根下收集到的其他子项目配置(*.vpj / project.json) + // 也要解析 source_dir / includes 到 defaultExtraPaths, + // 使各子项目的 App 根级全局可导入(如 TransPyV/App/lib 的 import lib.core.VLogger)。 + if (viperChildConfigPaths && viperChildConfigPaths.length > 1) { + for (let i = 1; i < viperChildConfigPaths.length; i++) { + const extraConfigs = this._getExtendedConfigurations(viperChildConfigPaths[i], undefined); + if (extraConfigs && extraConfigs.length > 0) { + this._initializeFromViperProjectJson( + extraConfigs[0].configFileJsonObj, + extraConfigs[0].configFileDirUri, + configOptions + ); + } + } + } } else { for (const config of configs) { configOptions.initializeFromJson( diff --git a/packages/vipeye-internal/src/analyzer/serviceUtils.ts b/packages/vipeye-internal/src/analyzer/serviceUtils.ts index 5812df56..07217365 100644 --- a/packages/vipeye-internal/src/analyzer/serviceUtils.ts +++ b/packages/vipeye-internal/src/analyzer/serviceUtils.ts @@ -69,30 +69,41 @@ export function findVpjFileInDir(fs: ReadOnlyFileSystem, dirUri: Uri): Uri | und return undefined; } -// Viper 扩展:在目录的一级子目录中查找 .vpj 文件。 -// 用于 LSP 模式下工作区根(如 Viper_VSCode)未直接包含 .vpj, -// 但子目录(如 Test/)包含 Viper 项目配置的场景。 +// Viper 扩展:在目录的一级子目录中查找所有 Viper 项目配置文件(*.vpj / project.json)。 +// 用于 LSP 模式下工作区根(如 TransPyC)未直接包含配置文件, +// 但子目录(如 TransPyV/)包含 Viper 项目配置的场景。 +// 每个子项目的 source_dir(如 ./App)都应作为全局导入根, +// includes 作为额外搜索路径 —— App 根级全局可导入。 // 仅扫描一级子目录(非递归),避免深层遍历开销。 -// 失败时返回 undefined。 -export function findVpjInChildDirs(fs: ReadOnlyFileSystem, dirUri: Uri): Uri | undefined { +// 失败或未找到时返回空数组。 +export function findViperProjectConfigsInChildDirs(fs: ReadOnlyFileSystem, dirUri: Uri): Uri[] { + const configs: Uri[] = []; try { if (!fs.existsSync(dirUri)) { - return undefined; + return configs; } const entries = fs.readdirEntriesSync(dirUri); for (const entry of entries) { - if (entry.isDirectory()) { - const childDir = dirUri.resolvePaths(entry.name); - const vpjFile = findVpjFileInDir(fs, childDir); - if (vpjFile) { - return vpjFile; - } + if (!entry.isDirectory()) { + continue; + } + const childDir = dirUri.resolvePaths(entry.name); + // 优先 .vpj(Viper Project):当目录同时存在 project.vpj 与 project.json 时,优先使用 .vpj。 + const vpjFile = findVpjFileInDir(fs, childDir); + if (vpjFile) { + configs.push(vpjFile); + continue; + } + // TransPyC project.json:仅当 schema 匹配(含 source_dir / compiler / linker 之一)时收集。 + const projectFileName = childDir.resolvePaths(viperProjectFileName); + if (fs.existsSync(projectFileName) && isViperProjectConfig(fs, projectFileName)) { + configs.push(fs.realCasePath(projectFileName)); } } } catch { // 读取目录失败:跳过 } - return undefined; + return configs; } // 校验 project.json / project.vpj 是否为 Viper(TransPyC)项目配置。