diff --git a/CHANGELOG.md b/CHANGELOG.md index ec4c571..a103f3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log All notable changes to the "crs-al-language-extension" extension: -## [1.5.26] - 2023-07-12 +## [1.5.28] - 2023-07-12 Fix from [James Pearson](https://github.com/jimmymcp): pull request [#282 - Do not add quotes to object names if not necessary when reorganising](https://github.com/waldo1001/crs-al-language-extension/pull/282/files) - thanks! ## [1.5.25] - 2023-01-20 diff --git a/README.md b/README.md index 44c67d4..b47b858 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ tableextension 50100 "Just Some Table Extension" extends Customer //18 * `DisableWaldoSnippets`: Disables the CRS snippets that come with this extension. When you change the setting, you need to restart VSCode twice. Once for disabling the snippets on activation (at that time, the snippets are still loaded). And the second time to actually not load the snippets anymore. * `SkipWarningMessageOnRenameAll`: Skips the Warning when renaming all files which can disturb custom VS tasks. * `RenameWithGit`: Use 'git mv' to rename a file. This keeps history of the file, but stages the rename, which you should commit separately. **The feature is still in preview-mode, therefore the default value is 'false'** - +* `ReorganizeByNamespace`: This is a feature that allows for the automatic reorganization of files by creating folder structures based on the namespaces defined within the files. **The feature is still in preview-mode, therefore the default value is 'false'** ## Skip String manipulation You can skip string manipulation by adding comments to your code: diff --git a/package-lock.json b/package-lock.json index 3f8c667..92acfa3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "crs-al-language-extension", - "version": "1.5.26", + "version": "1.5.28", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "crs-al-language-extension", - "version": "1.5.26", + "version": "1.5.28", "license": "MIT", "dependencies": { "applicationinsights": "^2.2.0", @@ -64,13 +64,14 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/@azure/core-http": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-2.2.6.tgz", - "integrity": "sha512-Lx7A3k2JIXpIbixfUaOOG79WNSo/Y7dhZ0LaLhaayyZ6PwQdVsEQXAR+oIPqPSfgPzv7RtwPSVviJ2APrsQKvQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-2.3.2.tgz", + "integrity": "sha512-Z4dfbglV9kNZO177CNx4bo5ekFuYwwsvjLiKdZI4r84bYGv3irrbQz7JC3/rUfFH2l4T/W6OFleJaa2X0IaQqw==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.3.0", "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.1", "@azure/logger": "^1.0.0", "@types/node-fetch": "^2.5.0", "@types/tunnel": "^0.0.3", @@ -81,10 +82,10 @@ "tslib": "^2.2.0", "tunnel": "^0.0.6", "uuid": "^8.3.0", - "xml2js": "^0.4.19" + "xml2js": "^0.5.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/@azure/core-http/node_modules/tslib": { @@ -109,6 +110,23 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, + "node_modules/@azure/core-util": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.2.tgz", + "integrity": "sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-util/node_modules/tslib": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", + "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + }, "node_modules/@azure/logger": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz", @@ -3023,11 +3041,10 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3065,9 +3082,9 @@ "license": "ISC" }, "node_modules/xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -3203,13 +3220,14 @@ } }, "@azure/core-http": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-2.2.6.tgz", - "integrity": "sha512-Lx7A3k2JIXpIbixfUaOOG79WNSo/Y7dhZ0LaLhaayyZ6PwQdVsEQXAR+oIPqPSfgPzv7RtwPSVviJ2APrsQKvQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-2.3.2.tgz", + "integrity": "sha512-Z4dfbglV9kNZO177CNx4bo5ekFuYwwsvjLiKdZI4r84bYGv3irrbQz7JC3/rUfFH2l4T/W6OFleJaa2X0IaQqw==", "requires": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.3.0", "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.1", "@azure/logger": "^1.0.0", "@types/node-fetch": "^2.5.0", "@types/tunnel": "^0.0.3", @@ -3220,7 +3238,7 @@ "tslib": "^2.2.0", "tunnel": "^0.0.6", "uuid": "^8.3.0", - "xml2js": "^0.4.19" + "xml2js": "^0.5.0" }, "dependencies": { "tslib": { @@ -3246,6 +3264,22 @@ } } }, + "@azure/core-util": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.2.tgz", + "integrity": "sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", + "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + } + } + }, "@azure/logger": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz", @@ -5248,9 +5282,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true }, "workerpool": { @@ -5277,9 +5311,9 @@ "dev": true }, "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", "requires": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" diff --git a/package.json b/package.json index 43bc30f..e7b7e40 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "crs-al-language-extension", "displayName": "waldo's CRS AL Language Extension", "description": "Make working with the (Dynamics NAV / 365) AL Language easier and more efficient.", - "version": "1.5.26", + "version": "1.5.28", "publisher": "waldo", "icon": "images/waldo.png", "author": { @@ -267,6 +267,12 @@ "description": "Use 'git mv' to rename a file. This keeps history of the file, but stages the rename, which you should commit separately. The feature is still in preview-mode, therefore the default value is 'false'", "scope": "resource" }, + "CRS.ReorganizeByNamespace": { + "type": "boolean", + "default": false, + "description": "This is a feature that allows for the automatic reorganization of files by creating folder structures based on the namespaces defined within the files. The feature is still in preview-mode, therefore the default value is 'false'", + "scope": "resource" + }, "CRS.SearchObjectNamesRegexPattern": { "type": "string", "default": "^\\w+ (\\d* )?\"*", diff --git a/src/NAVObject.ts b/src/NAVObject.ts index ffbc26f..775e674 100644 --- a/src/NAVObject.ts +++ b/src/NAVObject.ts @@ -8,6 +8,7 @@ export class NAVObject { public objectType: string; public objectId: string; public objectName: string; + public objectNamespace: string; public objectActions: NAVObjectAction[] = new Array(); public tableFields: NAVTableField[] = new Array(); public pageFields: NAVPageField[] = new Array(); @@ -155,6 +156,7 @@ export class NAVObject { this.objectType = ''; this.objectId = ''; this.objectName = ''; + this.objectNamespace = ''; this.extendedObjectName = ''; this.extendedObjectId = ''; var ObjectNamePattern = '"[^"]*"' // All characters except " @@ -273,7 +275,11 @@ export class NAVObject { return null } } - + var patternObject = new RegExp('namespace\\s+([A-Za-z0-9_.]+?)\\s*;', "i"); + var match = this.NAVObjectText.match(patternObject); + if (match && match[1]) { + this.objectNamespace = match[1]; + } this.objectType = this.objectType.trim().toString(); this.objectId = this.objectId.trim().toString(); this.objectName = this.objectName.trim().toString().replace(/["]/g, ''); diff --git a/src/Settings.ts b/src/Settings.ts index 656ac9d..64bdf4b 100644 --- a/src/Settings.ts +++ b/src/Settings.ts @@ -34,6 +34,7 @@ export class Settings { static readonly DisableDefaultAlSnippets = 'DisableDefaultAlSnippets'; static readonly DisableCRSSnippets = 'DisableCRSSnippets'; static readonly RenameWithGit = 'RenameWithGit'; + static readonly ReorganizeByNamespace = 'ReorganizeByNamespace'; static readonly Browser = 'browser'; static readonly Incognito = 'incognito'; static readonly packageCachePath = 'packageCachePath'; @@ -100,6 +101,7 @@ export class Settings { this.SettingCollection[this.DisableCRSSnippets] = this.getSetting(this.DisableCRSSnippets); this.SettingCollection[this.PublicWebBaseUrl] = this.getSetting(this.PublicWebBaseUrl); this.SettingCollection[this.RenameWithGit] = this.getSetting(this.RenameWithGit); + this.SettingCollection[this.ReorganizeByNamespace] = this.getSetting(this.ReorganizeByNamespace); this.SettingCollection[this.SearchObjectNamesRegexPattern] = this.getSetting(this.SearchObjectNamesRegexPattern); this.SettingCollection[this.DependencyGraphIncludeTestApps] = this.getSetting(this.DependencyGraphIncludeTestApps); this.SettingCollection[this.DependencyGraphExcludeAppNames] = this.getSetting(this.DependencyGraphExcludeAppNames); diff --git a/src/WorkspaceFiles.ts b/src/WorkspaceFiles.ts index c710227..4008786 100644 --- a/src/WorkspaceFiles.ts +++ b/src/WorkspaceFiles.ts @@ -88,7 +88,7 @@ export class WorkspaceFiles { if (navObject.objectFileName && navObject.objectFileName != '' && fixedname && fixedname != '') { let objectFolder = path.join(vscode.workspace.getWorkspaceFolder(fileName).uri.fsPath, this.getDestinationFolder(navObject, settings)); - let objectTypeFolder = path.join(objectFolder, this.getObjectTypeFolder(navObject)); + let objectTypeFolder = path.join(objectFolder, this.getObjectTypeFolder(navObject, settings));//Boe let objectSubFolder = path.join(objectTypeFolder, this.getObjectSubFolder(navObject)); let destinationFileName = path.join(objectSubFolder, fixedname); @@ -97,9 +97,10 @@ export class WorkspaceFiles { return fileName.fsPath; } else { - (!fs.existsSync(objectFolder)) ? fs.mkdirSync(objectFolder) : ''; - (!fs.existsSync(objectTypeFolder)) ? fs.mkdirSync(objectTypeFolder) : ''; - (!fs.existsSync(objectSubFolder)) ? fs.mkdirSync(objectSubFolder) : ''; + //(!fs.existsSync(objectFolder)) ? fs.mkdirSync(objectFolder) : ''; + //(!fs.existsSync(objectTypeFolder)) ? fs.mkdirSync(objectTypeFolder) : ''; + //(!fs.existsSync(objectSubFolder)) ? fs.mkdirSync(objectSubFolder) : ''; + this.createDirectoryIfNotExists(objectSubFolder); withGit = withGit ? withGit : (git.isGitRepositorySync() && settings[Settings.RenameWithGit]) this.DoRenameFile(fileName, destinationFileName, withGit) @@ -308,13 +309,19 @@ export class WorkspaceFiles { return mySettings[Settings.AlSubFolderName] } - static getObjectTypeFolder(navObject: NAVObject): string { + static getObjectTypeFolder(navObject: NAVObject, mySettings: any): string { if (navObject.objectCodeunitSubType) { if (navObject.objectCodeunitSubType.toLowerCase() == 'test') { return '' } } + if (mySettings[Settings.ReorganizeByNamespace]) + { + let directoryPath = path.join(...navObject.objectNamespace.split(".")) + return directoryPath + } + return navObject.objectType } @@ -326,6 +333,20 @@ export class WorkspaceFiles { return ""; } + static createDirectoryIfNotExists(dir) { + const segments = dir.split(path.sep); + let currentPath = segments[0]; + + for (let i = 1; i < segments.length; i++) { + if (segments[i]) { + currentPath = path.join(currentPath, segments[i]); + if (!fs.existsSync(currentPath)) { + fs.mkdirSync(currentPath); + } + } + } + } + static async CreateGraphVizDependencyGraph() { crsOutput.showOutput(`Creating dependency graph (GraphViz) from apps in this workspace`, true); diff --git a/src/test/suite/NAVTestObjectLibrary.ts b/src/test/suite/NAVTestObjectLibrary.ts index e0ca45b..323a50e 100644 --- a/src/test/suite/NAVTestObjectLibrary.ts +++ b/src/test/suite/NAVTestObjectLibrary.ts @@ -113,6 +113,27 @@ export function getNormalCodeunitWithLongName(): NAVTestObject { return object; } +export function getNormalCodeunitWithNamespace(): NAVTestObject { + let object = new NAVTestObject; + + object.ObjectFileName = 'Cod50100.justAName.al' + object.ObjectText = ` + namespace spycoclown.test; + using Microsoft.Inventory.Item; + codeunit 50100 "Test Overload" + { + [EventSubscriber(ObjectType::Codeunit, Codeunit::LogInManagement, 'OnAfterLogInStart', '', false, false)] + local procedure TestOverLoad() + var + Item: Record Item; + begin + item.CalculateClassification(true, 'namespace'); + end; + } + ` + return object; +} + export function getTestCodeunit(): NAVTestObject { let object = new NAVTestObject; diff --git a/src/test/suite/WorkspaceFiles.test.ts b/src/test/suite/WorkspaceFiles.test.ts index d256b8c..6df5060 100644 --- a/src/test/suite/WorkspaceFiles.test.ts +++ b/src/test/suite/WorkspaceFiles.test.ts @@ -34,7 +34,7 @@ suite("WorkspaceFiles Tests", () => { let navTestObject = NAVTestObjectLibrary.getTestCodeunit(); let navObject = new NAVObject(navTestObject.ObjectText, testSettings, navTestObject.ObjectFileName); - let foldersuggestion = WorkspaceFiles.getObjectTypeFolder(navObject); + let foldersuggestion = WorkspaceFiles.getObjectTypeFolder(navObject,testSettings); assert.strictEqual(foldersuggestion.toLowerCase(), ''); }) @@ -44,9 +44,20 @@ suite("WorkspaceFiles Tests", () => { let navTestObject = NAVTestObjectLibrary.getNormalCodeunitWithLongName(); let navObject = new NAVObject(navTestObject.ObjectText, testSettings, navTestObject.ObjectFileName); - let foldersuggestion = WorkspaceFiles.getObjectTypeFolder(navObject); + let foldersuggestion = WorkspaceFiles.getObjectTypeFolder(navObject,testSettings); assert.strictEqual(foldersuggestion.toLowerCase().toString(), navObject.objectType.toString()); assert.notStrictEqual(foldersuggestion.toLowerCase().toString(), ''); }) + test("getObjectTypeFolder - return namespace", () => { + let testSettings = Settings.GetConfigSettings(null); + testSettings[Settings.ReorganizeByNamespace] = true; + let navTestObject = NAVTestObjectLibrary.getNormalCodeunitWithNamespace(); + let navObject = new NAVObject(navTestObject.ObjectText, testSettings, navTestObject.ObjectFileName); + + let foldersuggestion = WorkspaceFiles.getObjectTypeFolder(navObject,testSettings); + + assert.notStrictEqual(foldersuggestion.toLowerCase().toString(), navObject.objectNamespace.toLowerCase().toString()); + assert.strictEqual(foldersuggestion.toLowerCase().toString(), 'spycoclown\\test'); + }) }) \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index ccf7ab3..f5ceaea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,12 +16,13 @@ tslib "^2.2.0" "@azure/core-http@^2.2.3": - version "2.2.6" - resolved "https://registry.npmjs.org/@azure/core-http/-/core-http-2.2.6.tgz" + version "2.3.2" + resolved "https://registry.yarnpkg.com/@azure/core-http/-/core-http-2.3.2.tgz#f35d932bf914e8bf166bba57937440b0b4f3781d" dependencies: "@azure/abort-controller" "^1.0.0" "@azure/core-auth" "^1.3.0" "@azure/core-tracing" "1.0.0-preview.13" + "@azure/core-util" "^1.1.1" "@azure/logger" "^1.0.0" "@types/node-fetch" "^2.5.0" "@types/tunnel" "^0.0.3" @@ -32,7 +33,7 @@ tslib "^2.2.0" tunnel "^0.0.6" uuid "^8.3.0" - xml2js "^0.4.19" + xml2js "^0.5.0" "@azure/core-tracing@1.0.0-preview.13": version "1.0.0-preview.13" @@ -41,6 +42,13 @@ "@opentelemetry/api" "^1.0.1" tslib "^2.2.0" +"@azure/core-util@^1.1.1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.3.2.tgz#3f8cfda1e87fac0ce84f8c1a42fcd6d2a986632d" + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/logger@^1.0.0": version "1.0.3" resolved "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz" @@ -1421,8 +1429,8 @@ which@2.0.2, which@^2.0.1: isexe "^2.0.0" word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" + version "1.2.4" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" workerpool@6.2.0: version "6.2.0" @@ -1440,9 +1448,9 @@ wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" -xml2js@^0.4.19: - version "0.4.23" - resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz" +xml2js@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" dependencies: sax ">=0.6.0" xmlbuilder "~11.0.0"