lib/functions/Private/Get-SqlFragmentMarker.ps1
|
function Get-SqlFragmentMarker { <# .SYNOPSIS Converts a ScriptDom fragment into the line/column range a Monaco marker needs. .DESCRIPTION A fragment knows where it starts (StartLine, StartColumn) but not where it ends - only how many characters long it is. The end position is recovered from the token stream instead: the fragment's last token carries its own line and column, and its text gives the width. Both coordinate systems here are one-based and both are relative to the text that was parsed, which is the same convention ScriptDom uses for parse errors. That is what lets a schema or compatibility marker travel through Move-SqlDiagnosticToSelection unchanged when the user executed a selection rather than the whole script. .PARAMETER Fragment The node to place a marker on. .PARAMETER KeywordOnly Span the fragment's FIRST token instead of the whole fragment. Used by the execution-model rules of issue #61 section 3.4: underlining an entire UPDATE statement buries the point, whereas underlining the keyword says exactly what is not supported. .OUTPUTS [PSCustomObject] with Line, Column, EndLine and EndColumn, or $null when the fragment carries no usable position. #> [CmdletBinding()] param( [Parameter(Mandatory = $false, Position = 0)] [AllowNull()] $Fragment, [Parameter(Mandatory = $false)] [switch]$KeywordOnly ) # No tracer preamble: called per diagnostic, over the user's query (issue #61 section 5). if ($null -eq $Fragment) { return $null } $Line = [int]$Fragment.StartLine $Column = [int]$Fragment.StartColumn if ($Line -lt 1 -or $Column -lt 1) { return $null } $EndLine = $Line $EndColumn = $Column + 1 try { $Token = $Fragment.ScriptTokenStream $TokenIndex = if ($KeywordOnly) { [int]$Fragment.FirstTokenIndex } else { [int]$Fragment.LastTokenIndex } if ($null -ne $Token -and $TokenIndex -ge 0 -and $TokenIndex -lt $Token.Count) { $Last = $Token[$TokenIndex] $Width = 1 if (![string]::IsNullOrEmpty($Last.Text)) { $Width = $Last.Text.Length } $EndLine = [int]$Last.Line $EndColumn = [int]$Last.Column + $Width } } catch { # A marker that is one character wide is still on the right token. Failing to widen it must # never cost the diagnostic itself. $EndLine = $Line $EndColumn = $Column + 1 } # A range that ends before it starts would be rejected by Monaco and the diagnostic would vanish. if ($EndLine -lt $Line -or ($EndLine -eq $Line -and $EndColumn -le $Column)) { $EndLine = $Line $EndColumn = $Column + 1 } return [PSCustomObject][Ordered]@{ Line = $Line Column = $Column EndLine = $EndLine EndColumn = $EndColumn } } |