diff --git a/cli/package-lock.json b/cli/package-lock.json index 1cfffe4..0f75ee2 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ibm/sourceorbit", - "version": "1.2.0", + "version": "1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@ibm/sourceorbit", - "version": "1.2.0", + "version": "1.3.0", "license": "Apache 2", "dependencies": { "@modelcontextprotocol/sdk": "^1.12.1", diff --git a/cli/src/targets/index.ts b/cli/src/targets/index.ts index e81a290..0f5a47f 100644 --- a/cli/src/targets/index.ts +++ b/cli/src/targets/index.ts @@ -68,7 +68,7 @@ export interface FileOptions { */ export class Targets { - private languageProvider: TargetsLanguageProvider; + static LanguageProvider: TargetsLanguageProvider = new TargetsLanguageProvider(); /* pathCache and resolvedSearches are used for file resolving. */ private pathCache: { [path: string]: true | string[] } | undefined; @@ -89,7 +89,6 @@ export class Targets { constructor(private cwd: string, private fs: ReadFileSystem) { this.logger = new Logger(); - this.languageProvider = new TargetsLanguageProvider(this); } static get ignoredObjects() { @@ -97,7 +96,7 @@ export class Targets { } getSearchGlob(): string { - return this.languageProvider.getGlob(); + return Targets.LanguageProvider.getGlob(); } public getCwd() { @@ -143,7 +142,7 @@ export class Targets { } private extCanBeProgram(ext: string): boolean { - return [`MODULE`, `PGM`].includes(this.languageProvider.getObjectType(ext)); + return [`MODULE`, `PGM`].includes(Targets.LanguageProvider.getObjectType(ext)); } public async resolvePathToObject(localPath: string, newText?: string) { @@ -335,7 +334,7 @@ export class Targets { // TODO: move this to language provider getObjectType(relativePath: string, ext: string): ObjectType { - const objType = this.languageProvider.getObjectType(ext); + const objType = Targets.LanguageProvider.getObjectType(ext); if (!objType) { this.logger.fileLog(relativePath, { @@ -389,7 +388,7 @@ export class Targets { text: textMatch }; - await this.languageProvider.handleLanguage(filePath, content, options); + await Targets.LanguageProvider.handleLanguage(this, filePath, content, options); } catch (e) { this.logger.fileLog(relative, { message: `Failed to parse file.`, diff --git a/cli/src/targets/languages.ts b/cli/src/targets/languages.ts index f10d6b8..e2ae908 100644 --- a/cli/src/targets/languages.ts +++ b/cli/src/targets/languages.ts @@ -1,7 +1,7 @@ import { FileOptions, ObjectType, Targets } from "."; import { clExtensions, clleTargetCallback, clObjects } from "./languages/clle"; import { ddsExtension, ddsObjects, ddsTargetCallback } from "./languages/dds"; -import { rpgExtensions, rpgleTargetParser, rpgObjects } from "./languages/rpgle"; +import { rpgleExtensions, rpgleObjects, rpgleTargetCallback } from "./languages/rpgle"; import { sqlExtensions, sqlObjects, sqlTargetCallback } from "./languages/sql"; import { binderExtensions, binderObjects, binderTargetCallback } from "./languages/binder"; import { cmdExtensions, cmdObjects, cmdTargetCallback } from "./languages/cmd"; @@ -19,19 +19,14 @@ export class TargetsLanguageProvider { private languageTargets: LanguageGroup[] = []; private extensionMap: ExtensionMap = {}; - constructor(private readonly targets: Targets) { - const rpgleTargets = new rpgleTargetParser(this.targets); - + constructor() { this.registerLanguage(clExtensions, clleTargetCallback, clObjects); this.registerLanguage(sqlExtensions, sqlTargetCallback, sqlObjects); this.registerLanguage(ddsExtension, ddsTargetCallback, ddsObjects); this.registerLanguage(binderExtensions, binderTargetCallback, binderObjects); this.registerLanguage(cmdExtensions, cmdTargetCallback, cmdObjects); this.registerLanguage(noSourceObjects, noSourceTargetCallback, noSourceTargetObjects); - - this.registerLanguage(rpgExtensions, (tazrgets, relativePath, content, options) => { - return rpgleTargets.rpgleTargetCallback(targets, relativePath, content, options); - }, rpgObjects); + this.registerLanguage(rpgleExtensions, rpgleTargetCallback, rpgleObjects); } public getExtensions() { @@ -43,16 +38,11 @@ export class TargetsLanguageProvider { return `**/*.{${allExtensions.join(`,`)},${allExtensions.map(e => e.toUpperCase()).join(`,`)}}`; } - public static getStandardGlob() { - const allExtensions = [...clExtensions, ...sqlExtensions, ...ddsExtension, ...binderExtensions, ...cmdExtensions, ...rpgExtensions]; - return `**/*.{${allExtensions.join(`,`)},${allExtensions.map(e => e.toUpperCase()).join(`,`)}}`; - } - - public async handleLanguage(relativePath: string, content: string, options: FileOptions = {}) { + public async handleLanguage(targets: Targets, relativePath: string, content: string, options: FileOptions = {}) { const ext = relativePath.split('.').pop()?.toLowerCase(); const language = this.languageTargets.find(lang => lang.extensions.includes(ext)); if (ext && language) { - await language.callback(this.targets, relativePath, content, options); + await language.callback(targets, relativePath, content, options); } } diff --git a/cli/src/targets/languages/rpgle.ts b/cli/src/targets/languages/rpgle.ts index 4afb709..d85351f 100644 --- a/cli/src/targets/languages/rpgle.ts +++ b/cli/src/targets/languages/rpgle.ts @@ -7,229 +7,254 @@ import { asPosix, toLocalPath, trimQuotes } from "../../utils"; import { isSqlFunction } from "../../languages/sql"; import { ExtensionMap } from "../languages"; -export const rpgExtensions = [`sqlrpgle`, `rpgle`]; -export const rpgObjects: ExtensionMap = { +export const rpgleExtensions = [`sqlrpgle`, `rpgle`]; +export const rpgleObjects: ExtensionMap = { sqlrpgle: `MODULE`, rpgle: `MODULE`, } interface RpgLookup { - lookup: string, - line?: number + lookup: string, + line?: number } -export class rpgleTargetParser { - private parser: Parser; - private includeFileCache: { [path: string]: string } = {}; +const includeFileCache: { [path: string]: string } = {}; - constructor(private targets: Targets) { - this.parser = this.setupParser(targets); - } - - public async rpgleTargetCallback(targets: Targets, localPath: string, content: string, options: FileOptions) { - const cache = await this.parser.getDocs( - localPath, - content, - { - ignoreCache: true, - withIncludes: true - } - ); +export async function rpgleTargetCallback(targets: Targets, localPath: string, content: string, options: FileOptions) { + const parser = setupParser(targets); - if (cache) { - const ileObject = await this.targets.resolvePathToObject(localPath, options.text); - - const pathDetail = path.parse(localPath); - // define internal imports - ileObject.imports = cache.procedures - .filter((proc: any) => proc.keyword[`EXTPROC`] && !proc.keyword[`EXPORT`]) - .map(ref => { - const keyword = ref.keyword; - let importName: string = ref.name; - const extproc: string | boolean = keyword[`EXTPROC`]; - if (extproc) { - if (extproc === true) importName = ref.name; - else importName = extproc; - } - - if (importName.includes(`:`)) { - const parmParms = importName.split(`:`); - importName = parmParms.filter(p => !p.startsWith(`*`)).join(``); - } + const cache = await parser.getDocs( + localPath, + content, + { + ignoreCache: true, + withIncludes: true + } + ); + + if (cache) { + const ileObject = await targets.resolvePathToObject(localPath, options.text); + + const pathDetail = path.parse(localPath); + // define internal imports + ileObject.imports = cache.procedures + .filter((proc: any) => proc.keyword[`EXTPROC`] && !proc.keyword[`EXPORT`]) + .map(ref => { + const keyword = ref.keyword; + let importName: string = ref.name; + const extproc: string | boolean = keyword[`EXTPROC`]; + if (extproc) { + if (extproc === true) importName = ref.name; + else importName = extproc; + } - if (importName.startsWith(`*`)) { - importName = ref.name; - } else { - importName = trimQuotes(importName); - } + if (importName.includes(`:`)) { + const parmParms = importName.split(`:`); + importName = parmParms.filter(p => !p.startsWith(`*`)).join(``); + } - return importName; - }); + if (importName.startsWith(`*`)) { + importName = ref.name; + } else { + importName = trimQuotes(importName); + } - // define exported functions - if (cache.keyword[`NOMAIN`]) { - ileObject.type = `MODULE`; + return importName; + }); - // Note that we store exports as uppercase. - ileObject.exports = cache.procedures - .filter((proc: any) => proc.keyword[`EXPORT`]) - .map(ref => ref.name.toUpperCase()); - } + // define exported functions + if (cache.keyword[`NOMAIN`]) { + ileObject.type = `MODULE`; - infoOut(`${ileObject.systemName}.${ileObject.type}: ${ileObject.relativePath}`); + // Note that we store exports as uppercase. + ileObject.exports = cache.procedures + .filter((proc: any) => proc.keyword[`EXPORT`]) + .map(ref => ref.name.toUpperCase()); + } - if (cache.includes && cache.includes.length > 0) { - ileObject.headers = []; + infoOut(`${ileObject.systemName}.${ileObject.type}: ${ileObject.relativePath}`); - cache.includes.forEach((include: IncludeStatement) => { - // RPGLE includes are always returned as posix paths - // even on Windows. We need to do some magic to convert here for Windows systems - include.toPath = toLocalPath(include.toPath); + if (cache.includes && cache.includes.length > 0) { + ileObject.headers = []; - const includeDetail = path.parse(include.toPath); + cache.includes.forEach((include: IncludeStatement) => { + // RPGLE includes are always returned as posix paths + // even on Windows. We need to do some magic to convert here for Windows systems + include.toPath = toLocalPath(include.toPath); - if (includeDetail.ext.toLowerCase() !== `.rpgleinc`) { - const possibleName = includeDetail.name.toLowerCase().endsWith(`.pgm`) ? includeDetail.name.substring(0, includeDetail.name.length - 4) : includeDetail.name; + const includeDetail = path.parse(include.toPath); - if (this.targets.suggestions.renames) { - const renameLogPath = this.targets.getRelative(include.toPath); + if (includeDetail.ext.toLowerCase() !== `.rpgleinc`) { + const possibleName = includeDetail.name.toLowerCase().endsWith(`.pgm`) ? includeDetail.name.substring(0, includeDetail.name.length - 4) : includeDetail.name; - // We need to make sure the .rpgleinc rename is most important - if (this.targets.logger.exists(renameLogPath, `rename`)) { - this.targets.logger.flush(renameLogPath); - } + if (targets.suggestions.renames) { + const renameLogPath = targets.getRelative(include.toPath); - this.targets.logger.fileLog(renameLogPath, { - message: `Rename suggestion`, - type: `rename`, - change: { - rename: { - path: include.toPath, - newName: `${possibleName}.rpgleinc` - } - } - }); - } else { - this.targets.logger.fileLog(this.targets.getRelative(include.toPath), { - message: `referenced as include, but should use the '.rpgleinc' extension.`, - type: `warning`, - }); + // We need to make sure the .rpgleinc rename is most important + if (targets.logger.exists(renameLogPath, `rename`)) { + targets.logger.flush(renameLogPath); } - } - const theIncludePath = asPosix(this.targets.getRelative(include.toPath)); - - ileObject.headers.push(theIncludePath); - - if (this.targets.suggestions.includes) { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `Will update to use unix style path.`, - type: `includeFix`, - line: include.line, + targets.logger.fileLog(renameLogPath, { + message: `Rename suggestion`, + type: `rename`, change: { - lineContent: (options.isFree ? `` : ``.padEnd(6)) + `/copy '${theIncludePath}'` + rename: { + path: include.toPath, + newName: `${possibleName}.rpgleinc` + } } }); } else { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `Include at line ${include.line} found, to path '${theIncludePath}'`, - type: `info`, - line: include.line, + targets.logger.fileLog(targets.getRelative(include.toPath), { + message: `referenced as include, but should use the '.rpgleinc' extension.`, + type: `warning`, }); } - }); - } + } - const target: ILEObjectTarget = { - ...ileObject, - deps: [] - }; + const theIncludePath = asPosix(targets.getRelative(include.toPath)); - // This usually means .pgm is in the name - if (ileObject.type === `PGM` && cache.keyword[`NOMAIN`]) { - const possibleName = pathDetail.name.toLowerCase().endsWith(`.pgm`) ? pathDetail.name.substring(0, pathDetail.name.length - 4) : pathDetail.name; + ileObject.headers.push(theIncludePath); - if (this.targets.suggestions.renames) { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `Rename suggestion`, - type: `rename`, + if (targets.suggestions.includes) { + targets.logger.fileLog(ileObject.relativePath, { + message: `Will update to use unix style path.`, + type: `includeFix`, + line: include.line, change: { - rename: { - path: localPath, - newName: possibleName + pathDetail.ext - } + lineContent: (options.isFree ? `` : ``.padEnd(6)) + `/copy '${theIncludePath}'` } - }) + }); } else { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `type detected as ${ileObject.type} but NOMAIN keyword found.`, - type: `warning`, + targets.logger.fileLog(ileObject.relativePath, { + message: `Include at line ${include.line} found, to path '${theIncludePath}'`, + type: `info`, + line: include.line, }); } - } + }); + } - // This usually means it's source name is a module (no .pgm) but doesn't have NOMAIN. - // We need to do this for other language too down the line - if (ileObject.type === `MODULE` && !cache.keyword[`NOMAIN`]) { - if (this.targets.suggestions.renames) { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `Rename suggestion`, - type: `rename`, - change: { - rename: { - path: localPath, - newName: pathDetail.name + `.pgm` + pathDetail.ext - } + const target: ILEObjectTarget = { + ...ileObject, + deps: [] + }; + + // This usually means .pgm is in the name + if (ileObject.type === `PGM` && cache.keyword[`NOMAIN`]) { + const possibleName = pathDetail.name.toLowerCase().endsWith(`.pgm`) ? pathDetail.name.substring(0, pathDetail.name.length - 4) : pathDetail.name; + + if (targets.suggestions.renames) { + targets.logger.fileLog(ileObject.relativePath, { + message: `Rename suggestion`, + type: `rename`, + change: { + rename: { + path: localPath, + newName: possibleName + pathDetail.ext } - }); - } else { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `type detected as ${ileObject.type} but NOMAIN keyword was not found. Is it possible the extension should include '.pgm'?`, - type: `warning`, - }); - } + } + }) + } else { + targets.logger.fileLog(ileObject.relativePath, { + message: `type detected as ${ileObject.type} but NOMAIN keyword found.`, + type: `warning`, + }); } + } - if (cache.keyword[`BNDDIR`]) { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `has the BNDDIR keyword. 'binders' property in iproj.json should be used instead.`, - type: `info`, + // This usually means it's source name is a module (no .pgm) but doesn't have NOMAIN. + // We need to do this for other language too down the line + if (ileObject.type === `MODULE` && !cache.keyword[`NOMAIN`]) { + if (targets.suggestions.renames) { + targets.logger.fileLog(ileObject.relativePath, { + message: `Rename suggestion`, + type: `rename`, + change: { + rename: { + path: localPath, + newName: pathDetail.name + `.pgm` + pathDetail.ext + } + } + }); + } else { + targets.logger.fileLog(ileObject.relativePath, { + message: `type detected as ${ileObject.type} but NOMAIN keyword was not found. Is it possible the extension should include '.pgm'?`, + type: `warning`, }); } + } - // Find external programs - cache.procedures - .filter((proc: any) => proc.keyword[`EXTPGM`]) - .map((ref): RpgLookup => { - const keyword = ref.keyword; - let fileName = ref.name; - const extpgm = keyword[`EXTPGM`]; - if (extpgm) { - if (extpgm === true) fileName = ref.name; - else fileName = trimQuotes(extpgm); + if (cache.keyword[`BNDDIR`]) { + targets.logger.fileLog(ileObject.relativePath, { + message: `has the BNDDIR keyword. 'binders' property in iproj.json should be used instead.`, + type: `info`, + }); + } + + // Find external programs + cache.procedures + .filter((proc: any) => proc.keyword[`EXTPGM`]) + .map((ref): RpgLookup => { + const keyword = ref.keyword; + let fileName = ref.name; + const extpgm = keyword[`EXTPGM`]; + if (extpgm) { + if (extpgm === true) fileName = ref.name; + else fileName = trimQuotes(extpgm); + } + + return { + lookup: fileName.toUpperCase(), + line: ref.position ? ref.position.range.line : undefined + }; + }) + .forEach((ref: RpgLookup) => { + // Don't add ignored objects (usually system APIs) + if (Targets.ignoredObjects.includes(ref.lookup)) return; + // Don't add itself + if (ref.lookup === ileObject.systemName) return; + + const resolvedObject = targets.searchForObject({ systemName: ref.lookup, type: `PGM` }); + if (resolvedObject) { + // because of legacy fixed CALL, there can be dupliicate EXTPGMs with the same name :( + if (!target.deps.some(d => d.systemName === resolvedObject.systemName && d.type && resolvedObject.type)) { + target.deps.push(resolvedObject) } + } + + else { + targets.logger.fileLog(ileObject.relativePath, { + message: `No object found for reference '${ref.lookup}'`, + type: `warning`, + line: ref.line + }); + } + }); + + // Scan the multiple scopes available in an RPGLE program + const scopes = [cache, ...cache.procedures.map(p => p.scope)].filter(s => s); + + for (const scope of scopes) { + + // Find external data structure sources + scope.structs + .filter((struct: any) => struct.keyword[`EXTNAME`]) + .map((struct): RpgLookup => { + const keyword = struct.keyword; + const value = trimQuotes(keyword[`EXTNAME`]); return { - lookup: fileName.toUpperCase(), - line: ref.position ? ref.position.range.line : undefined + lookup: value.split(`:`)[0].toUpperCase(), + line: struct.position ? struct.position.range.line : undefined }; }) .forEach((ref: RpgLookup) => { - // Don't add ignored objects (usually system APIs) - if (Targets.ignoredObjects.includes(ref.lookup)) return; - // Don't add itself - if (ref.lookup === ileObject.systemName) return; - - const resolvedObject = this.targets.searchForObject({ systemName: ref.lookup, type: `PGM` }); - if (resolvedObject) { - // because of legacy fixed CALL, there can be dupliicate EXTPGMs with the same name :( - if (!target.deps.some(d => d.systemName === resolvedObject.systemName && d.type && resolvedObject.type)) { - target.deps.push(resolvedObject) - } - } - + const resolvedObject = targets.searchForObject({ systemName: ref.lookup, type: `FILE` }); + if (resolvedObject) target.deps.push(resolvedObject) else { - this.targets.logger.fileLog(ileObject.relativePath, { + targets.logger.fileLog(ileObject.relativePath, { message: `No object found for reference '${ref.lookup}'`, type: `warning`, line: ref.line @@ -237,240 +262,210 @@ export class rpgleTargetParser { } }); - // Scan the multiple scopes available in an RPGLE program - const scopes = [cache, ...cache.procedures.map(p => p.scope)].filter(s => s); - - for (const scope of scopes) { - - // Find external data structure sources - scope.structs - .filter((struct: any) => struct.keyword[`EXTNAME`]) - .map((struct): RpgLookup => { - const keyword = struct.keyword; - const value = trimQuotes(keyword[`EXTNAME`]); - - return { - lookup: value.split(`:`)[0].toUpperCase(), - line: struct.position ? struct.position.range.line : undefined - }; - }) - .forEach((ref: RpgLookup) => { - const resolvedObject = this.targets.searchForObject({ systemName: ref.lookup, type: `FILE` }); - if (resolvedObject) target.deps.push(resolvedObject) - else { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `No object found for reference '${ref.lookup}'`, + // Find external files + scope.files + .map((file): RpgLookup => { + let possibleName: string = file.name; + const keyword = file.keyword; + + const extNameValue = keyword[`EXTFILE`]; + if (extNameValue) { + possibleName = trimQuotes(extNameValue).split(`:`)[0] + } + + if (possibleName.toLowerCase() === `*extdesc`) { + const extDescValue = keyword[`EXTDESC`]; + if (extDescValue) { + possibleName = trimQuotes(extDescValue); + } else { + targets.logger.fileLog(ileObject.relativePath, { + message: `*EXTDESC is used for '${file.name}' but EXTDESC keyword not found`, type: `warning`, - line: ref.line }); } - }); + } - // Find external files - scope.files - .map((file): RpgLookup => { - let possibleName: string = file.name; - const keyword = file.keyword; + return { + lookup: possibleName.toUpperCase(), + line: file.position ? file.position.range.line : undefined + }; + }) + .forEach((ref: RpgLookup) => { + if (Targets.ignoredObjects.includes(ref.lookup)) return; - const extNameValue = keyword[`EXTFILE`]; - if (extNameValue) { - possibleName = trimQuotes(extNameValue).split(`:`)[0] - } + const previouslyScanned = target.deps.some((r => (ref.lookup === r.systemName || ref.lookup === r.longName?.toUpperCase()) && r.type === `FILE`)); + if (previouslyScanned) return; - if (possibleName.toLowerCase() === `*extdesc`) { - const extDescValue = keyword[`EXTDESC`]; - if (extDescValue) { - possibleName = trimQuotes(extDescValue); - } else { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `*EXTDESC is used for '${file.name}' but EXTDESC keyword not found`, - type: `warning`, - }); - } - } + const resolvedObject = targets.searchForObject({ systemName: ref.lookup, type: `FILE` }); + if (resolvedObject) target.deps.push(resolvedObject) + else { + targets.logger.fileLog(ileObject.relativePath, { + message: `No object found for reference '${ref.lookup}'`, + type: `warning`, + line: ref.line + }); + } + }); - return { - lookup: possibleName.toUpperCase(), - line: file.position ? file.position.range.line : undefined - }; - }) - .forEach((ref: RpgLookup) => { - if (Targets.ignoredObjects.includes(ref.lookup)) return; - - const previouslyScanned = target.deps.some((r => (ref.lookup === r.systemName || ref.lookup === r.longName?.toUpperCase()) && r.type === `FILE`)); - if (previouslyScanned) return; - - const resolvedObject = this.targets.searchForObject({ systemName: ref.lookup, type: `FILE` }); - if (resolvedObject) target.deps.push(resolvedObject) - else { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `No object found for reference '${ref.lookup}'`, - type: `warning`, - line: ref.line - }); - } - }); + // We ignore anything with hardcoded schemas + scope.sqlReferences + .filter(ref => !ref.description) + .map((ref): RpgLookup => ({ + lookup: trimQuotes(ref.name, `"`).toUpperCase(), + line: ref.position ? ref.position.range.line : undefined + })) + .forEach((ref: RpgLookup) => { + const previouslyScanned = target.deps.some((r => (ref.lookup === r.systemName || ref.lookup === r.longName?.toUpperCase()) && r.type === `FILE`)); + if (previouslyScanned) return; + const resolvedObject = targets.searchForObject({ systemName: ref.lookup, type: `FILE` }); + if (resolvedObject) target.deps.push(resolvedObject) + else if (!isSqlFunction(ref.lookup)) { + targets.logger.fileLog(ileObject.relativePath, { + message: `No object found for reference '${ref.lookup}'`, + type: `warning`, + line: ref.line + }); + } + }); - // We ignore anything with hardcoded schemas - scope.sqlReferences - .filter(ref => !ref.description) - .map((ref): RpgLookup => ({ - lookup: trimQuotes(ref.name, `"`).toUpperCase(), - line: ref.position ? ref.position.range.line : undefined - })) - .forEach((ref: RpgLookup) => { - const previouslyScanned = target.deps.some((r => (ref.lookup === r.systemName || ref.lookup === r.longName?.toUpperCase()) && r.type === `FILE`)); - if (previouslyScanned) return; - const resolvedObject = this.targets.searchForObject({ systemName: ref.lookup, type: `FILE` }); - if (resolvedObject) target.deps.push(resolvedObject) - else if (!isSqlFunction(ref.lookup)) { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `No object found for reference '${ref.lookup}'`, - type: `warning`, - line: ref.line - }); - } - }); + // Find external data areas + scope.structs + .filter((struct: any) => struct.keyword[`DTAARA`]) + .map((ref): RpgLookup => { + const keyword = ref.keyword; + let fileName: string = ref.name; + const dtaara = keyword[`DTAARA`]; + if (dtaara) { + if (dtaara === true) fileName = ref.name; + else fileName = trimQuotes(dtaara); + } - // Find external data areas - scope.structs - .filter((struct: any) => struct.keyword[`DTAARA`]) - .map((ref): RpgLookup => { - const keyword = ref.keyword; - let fileName: string = ref.name; - const dtaara = keyword[`DTAARA`]; - if (dtaara) { - if (dtaara === true) fileName = ref.name; - else fileName = trimQuotes(dtaara); - } + return { + lookup: fileName.toUpperCase(), + line: ref.position ? ref.position.range.line : undefined + }; + }) + .forEach((ref: RpgLookup) => { + if (Targets.ignoredObjects.includes(ref.lookup.toUpperCase())) return; - return { - lookup: fileName.toUpperCase(), - line: ref.position ? ref.position.range.line : undefined - }; - }) - .forEach((ref: RpgLookup) => { - if (Targets.ignoredObjects.includes(ref.lookup.toUpperCase())) return; - - const resolvedObject = this.targets.searchForObject({ systemName: ref.lookup, type: `DTAARA` }); - if (resolvedObject) target.deps.push(resolvedObject) - else { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `No object found for reference '${ref.lookup}'`, - type: `warning`, - line: ref.line - }); - } - }); + const resolvedObject = targets.searchForObject({ systemName: ref.lookup, type: `DTAARA` }); + if (resolvedObject) target.deps.push(resolvedObject) + else { + targets.logger.fileLog(ileObject.relativePath, { + message: `No object found for reference '${ref.lookup}'`, + type: `warning`, + line: ref.line + }); + } + }); - scope.variables - .filter((struct: any) => struct.keyword[`DTAARA`]) - .map((ref): RpgLookup => { - const keyword = ref.keyword; - let fileName: string = ref.name; - const dtaara = keyword[`DTAARA`]; - if (dtaara) { - if (dtaara === true) fileName = ref.name; - else fileName = trimQuotes(dtaara); - } + scope.variables + .filter((struct: any) => struct.keyword[`DTAARA`]) + .map((ref): RpgLookup => { + const keyword = ref.keyword; + let fileName: string = ref.name; + const dtaara = keyword[`DTAARA`]; + if (dtaara) { + if (dtaara === true) fileName = ref.name; + else fileName = trimQuotes(dtaara); + } - return { - lookup: fileName.toUpperCase(), - line: ref.position ? ref.position.range.line : undefined - }; - }) - .forEach((ref: RpgLookup) => { - const resolvedObject = this.targets.searchForObject({ systemName: ref.lookup, type: `DTAARA` }); - if (resolvedObject) target.deps.push(resolvedObject) - else { - this.targets.logger.fileLog(ileObject.relativePath, { - message: `No object found for reference '${ref.lookup}'`, - type: `warning`, - line: ref.line - }); - } - }); - } + return { + lookup: fileName.toUpperCase(), + line: ref.position ? ref.position.range.line : undefined + }; + }) + .forEach((ref: RpgLookup) => { + const resolvedObject = targets.searchForObject({ systemName: ref.lookup, type: `DTAARA` }); + if (resolvedObject) target.deps.push(resolvedObject) + else { + targets.logger.fileLog(ileObject.relativePath, { + message: `No object found for reference '${ref.lookup}'`, + type: `warning`, + line: ref.line + }); + } + }); + } - // TODO: did we duplicate this? - // We also look to see if there is a `.cmd` object with the same name - const resolvedObject = this.targets.searchForObject({ systemName: ileObject.systemName, type: `CMD` }); - if (resolvedObject) this.targets.createOrAppend(resolvedObject, target); + // TODO: did we duplicate this? + // We also look to see if there is a `.cmd` object with the same name + const resolvedObject = targets.searchForObject({ systemName: ileObject.systemName, type: `CMD` }); + if (resolvedObject) targets.createOrAppend(resolvedObject, target); - if (target.deps.length > 0) - infoOut(`Depends on: ${target.deps.map(d => `${d.systemName}.${d.type}`).join(` `)}`); + if (target.deps.length > 0) + infoOut(`Depends on: ${target.deps.map(d => `${d.systemName}.${d.type}`).join(` `)}`); - this.targets.addNewTarget(target); - } + targets.addNewTarget(target); } +} - setupParser(targets: Targets): Parser { - const parser = new Parser(); +function setupParser(targets: Targets): Parser { + const parser = new Parser(); - parser.setIncludeFileFetch(async (baseFile: string, includeFile: string) => { - if (includeFile.startsWith(`'`) && includeFile.endsWith(`'`)) { - includeFile = includeFile.substring(1, includeFile.length - 1); - } + parser.setIncludeFileFetch(async (baseFile: string, includeFile: string) => { + if (includeFile.startsWith(`'`) && includeFile.endsWith(`'`)) { + includeFile = includeFile.substring(1, includeFile.length - 1); + } - let file: string; + let file: string; - if (includeFile.includes(`,`)) { - // If the member include path is qualified with a source file - // then we should convert to be a unix style path so we can - // search the explicit directories. - includeFile = includeFile.replace(/,/g, `/`) + `.*`; + if (includeFile.includes(`,`)) { + // If the member include path is qualified with a source file + // then we should convert to be a unix style path so we can + // search the explicit directories. + includeFile = includeFile.replace(/,/g, `/`) + `.*`; - // Keep making the path less specific until we find a possible include - let parts = includeFile.split(`/`); - while (!file && parts.length > 0) { - file = await targets.resolveLocalFile(includeFile); + // Keep making the path less specific until we find a possible include + let parts = includeFile.split(`/`); + while (!file && parts.length > 0) { + file = await targets.resolveLocalFile(includeFile); - if (!file) { - parts.shift(); - includeFile = parts.join(`/`); - } + if (!file) { + parts.shift(); + includeFile = parts.join(`/`); } - } else if (!includeFile.includes(`/`)) { - const parent = path.basename(path.dirname(baseFile)); - console.log(parent); - includeFile = `${parent}/${includeFile}`; + } + } else if (!includeFile.includes(`/`)) { + const parent = path.basename(path.dirname(baseFile)); + console.log(parent); + includeFile = `${parent}/${includeFile}`; - file = await targets.resolveLocalFile(includeFile); - } else { - file = await targets.resolveLocalFile(includeFile); - } + file = await targets.resolveLocalFile(includeFile); + } else { + file = await targets.resolveLocalFile(includeFile); + } - if (file) { - if (this.includeFileCache[file]) { - return { - found: true, - uri: file, - content: this.includeFileCache[file] - } + if (file) { + if (includeFileCache[file]) { + return { + found: true, + uri: file, + content: includeFileCache[file] + } - } else { - const content = await targets.rfs.readFile(file); - this.includeFileCache[file] = content; + } else { + const content = await targets.rfs.readFile(file); + includeFileCache[file] = content; - return { - found: true, - uri: file, - content: content - } + return { + found: true, + uri: file, + content: content } } + } - return { - found: false - }; - }); + return { + found: false + }; + }); - parser.setTableFetch(async (table: string, aliases = false) => { - // Can't support tables in CLI mode I suppose? - return []; - }); + parser.setTableFetch(async (table: string, aliases = false) => { + // Can't support tables in CLI mode I suppose? + return []; + }); - return parser; - } + return parser; } \ No newline at end of file diff --git a/vs/client/package-lock.json b/vs/client/package-lock.json index 6cbe936..216ebe7 100644 --- a/vs/client/package-lock.json +++ b/vs/client/package-lock.json @@ -24,7 +24,7 @@ }, "../../cli": { "name": "@ibm/sourceorbit", - "version": "1.2.0", + "version": "1.3.0", "dev": true, "license": "Apache 2", "dependencies": { diff --git a/vs/package-lock.json b/vs/package-lock.json index fec1e7c..c7c6270 100644 --- a/vs/package-lock.json +++ b/vs/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-sourceorbit", - "version": "1.2.0", + "version": "1.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vscode-sourceorbit", - "version": "1.2.0", + "version": "1.3.0", "hasInstallScript": true, "license": "MIT", "devDependencies": { diff --git a/vs/server/package-lock.json b/vs/server/package-lock.json index b098550..f68561e 100644 --- a/vs/server/package-lock.json +++ b/vs/server/package-lock.json @@ -24,7 +24,7 @@ }, "../../cli": { "name": "@ibm/sourceorbit", - "version": "1.2.0", + "version": "1.3.0", "license": "Apache 2", "dependencies": { "@modelcontextprotocol/sdk": "^1.12.1", diff --git a/vs/server/src/server.ts b/vs/server/src/server.ts index 911268b..0e51927 100644 --- a/vs/server/src/server.ts +++ b/vs/server/src/server.ts @@ -1,4 +1,4 @@ -import { TargetsLanguageProvider } from '@ibm/sourceorbit'; +import { Targets } from '@ibm/sourceorbit'; import { createConnection, InitializeParams, @@ -32,9 +32,9 @@ connection.onInitialize((params: InitializeParams) => { if (hasWorkspaceFolderCapability) { result.capabilities.workspace = { fileOperations: { - didCreate: { filters: [{ pattern: { glob: TargetsLanguageProvider.getStandardGlob(), options: { ignoreCase: true } } }] }, - didRename: { filters: [{ pattern: { glob: TargetsLanguageProvider.getStandardGlob(), options: { ignoreCase: true } } }] }, - didDelete: { filters: [{ pattern: { glob: TargetsLanguageProvider.getStandardGlob(), options: { ignoreCase: true } } }] } + didCreate: { filters: [{ pattern: { glob: Targets.LanguageProvider.getGlob(), options: { ignoreCase: true } } }] }, + didRename: { filters: [{ pattern: { glob: Targets.LanguageProvider.getGlob(), options: { ignoreCase: true } } }] }, + didDelete: { filters: [{ pattern: { glob: Targets.LanguageProvider.getGlob(), options: { ignoreCase: true } } }] } }, workspaceFolders: { supported: true,