Modules/businessdev.ALbuild.Apps/Private/Get-BcAlObjectIndex.ps1
|
function Get-BcAlObjectIndex { <# .SYNOPSIS Indexes every AL object in a workspace with the file and line of each of its procedures. .DESCRIPTION Internal helper for Resolve-BcTestFailureSource. Keyed by object name (lower-cased) AND by "<kind>:<id>", because a callstack frame gives both and either one may be the reliable match: names collide across apps, ids do not, but an id is missing from some frames. Deliberately a line scan rather than a parse. Everything needed - object header, procedure declarations and their line numbers - is on a single line each, and a scan cannot fail on syntax the parser does not yet cover. A frame that cannot be matched is reported unresolved, so the cost of missing something here is a missing answer, never a wrong one. #> [CmdletBinding()] [OutputType([hashtable])] param( [Parameter(Mandatory)] [string] $WorkspaceRoot ) $index = @{} $objectHeader = '^\s*(table|page|codeunit|report|query|xmlport|enum|interface|pageextension|tableextension|enumextension|reportextension|permissionset)\s+(\d+)\s+"?([^"\r\n{]+?)"?\s*(?:extends|\{|$)' $procHeader = '^\s*(?:local\s+|internal\s+|protected\s+)?procedure\s+([A-Za-z_]\w*)\s*\(' $triggerHeader = '^\s*trigger\s+([A-Za-z_]\w*)\s*\(' $files = Get-ChildItem -LiteralPath $WorkspaceRoot -Filter '*.al' -File -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.FullName -notmatch '[\\/](\.alpackages|\.snapshots|\.altestrunner)[\\/]' } foreach ($file in $files) { $lines = Get-Content -LiteralPath $file.FullName -ErrorAction SilentlyContinue $current = $null for ($i = 0; $i -lt $lines.Count; $i++) { $line = $lines[$i] $m = [regex]::Match($line, $objectHeader, 'IgnoreCase') if ($m.Success) { $current = [PSCustomObject]@{ Kind = $m.Groups[1].Value Id = [int] $m.Groups[2].Value Name = $m.Groups[3].Value.Trim() File = $file.FullName Procedures = @{} } $index[$current.Name.ToLowerInvariant()] = $current $index["$($current.Kind.ToLowerInvariant()):$($current.Id)"] = $current continue } if ($null -eq $current) { continue } $p = [regex]::Match($line, $procHeader, 'IgnoreCase') if (-not $p.Success) { $p = [regex]::Match($line, $triggerHeader, 'IgnoreCase') } if ($p.Success) { # 1-based file line of the declaration. The frame's own line number is counted FROM here. $current.Procedures[$p.Groups[1].Value.ToLowerInvariant()] = $i + 1 } } } return $index } |