PSScriptTools.psm1

#enable verbose messaging in the psm1 file
if ($MyInvocation.line -match '-verbose') {
    $VerbosePreference = 'continue'
}
Write-Verbose 'Loading public functions'

#exclude files that have special requirements
Get-ChildItem -Path $PSScriptRoot\functions\*.ps1 -Exclude 'Get-MyCounter.ps1', 'Get-FileExtensionInfo.ps1','CimMember.ps1' |
ForEach-Object -Process {
    Write-Verbose $_.FullName
    . $_.FullName
}

Write-Verbose 'Loading Windows-specific commands'
if ($IsWindows -OR ($PSEdition -eq 'Desktop')) {
    . "$PSScriptRoot\functions\Get-MyCounter.ps1"
    . "$PSScriptRoot\functions\CimMember.ps1"
}

if ($IsCoreCLR) {
    Write-Verbose 'Loading PowerShell 7 specific commands'
    . "$PSScriptRoot\functions\Get-FileExtensionInfo.ps1"
}

#load ANSIFile Entry format if user is not using $PSStyle
if (-Not $PSStyle.FileInfo) {
    Write-Verbose "Using module PSAnsiFile features"
    Write-Verbose 'Loading PSAnsiFile format files'
    Update-FormatData -AppendPath "$PSScriptRoot\formats\psansifileentry.format.ps1xml"
    Update-FormatData -AppendPath "$PSScriptRoot\formats\filesystem-ansi.format.ps1xml"

    Write-Verbose 'Define the global PSAnsiFileMap variable'
    $json = 'psansifilemap.json'

    #test for user version in $HOME
    $UserJSON = Join-Path -Path $HOME -ChildPath $json
    $moduleJSON = Join-Path -Path $PSScriptRoot -ChildPath $json

    if (Test-Path -Path $UserJSON) {
        $map = $UserJSON
    }
    else {
        #use the file from this module
        $map = $moduleJSON
    }

    #ConvertFrom-Json doesn't write simple objects to the pipeline in Windows PowerShell so I
    #need to process the results individually.
    $mapData = [System.Collections.Generic.List[object]]::new()

    Get-Content -Path $map | ConvertFrom-Json | ForEach-Object { $_ } | ForEach-Object {
        $entry = [PSCustomObject]@{
            PSTypeName  = 'PSAnsiFileEntry'
            Description = $_.description
            Pattern     = $_.pattern
            Ansi        = $_.ansi
        }
        $mapData.Add($entry)
    }
    Set-Variable -Name PSAnsiFileMap -Value $mapData -Scope Global
} #load PSAnsiFile features

Write-Verbose 'Define special character map'
$global:PSSpecialChar = @{
    FullBlock        = ([char]0x2588)
    LightShade       = ([char]0x2591)
    MediumShade      = ([char]0x2592)
    DarkShade        = ([char]0x2593)
    BlackSquare      = ([char]0x25A0)
    WhiteSquare      = ([char]0x25A1)
    BlackSmallSquare = ([char]0x25AA)
    WhiteSmallSquare = ([char]0x25AB)
    UpTriangle       = ([char]0x25B2)
    DownTriangle     = ([char]0x25BC)
    Lozenge          = ([char]0x25CA)
    WhiteCircle      = ([char]0x25CB)
    BlackCircle      = ([char]0x25CF)
    WhiteFace        = ([char]0x263A)
    BlackFace        = ([char]0x263B)
    SixPointStar     = ([char]0x2736)
    Diamond          = ([char]0x2666)
    Club             = ([char]0x2663)
    Heart            = ([char]0x2665)
    Spade            = ([char]0x2660)
    Section          = ([char]0x00A7)
    RightPointer     = ([char]0x25BA)
    LeftPointer      = ([char]0x25C4)
    BlackRectangle   = ([char]0x25AC)
}

Write-Verbose "Defining the variable `$PSSamplePath to the samples folder for this module"
$global:PSSamplePath = Join-Path -Path $PSScriptRoot -ChildPath Samples

#define a private variable with PSScriptTools data
$ToolDataPath = "$PSScriptRoot\PSScriptToolData.json"

#region editor integrations
Write-Verbose 'Add ToDo options to the ISE or VS Code'
if ($psEditor) {
    Write-Verbose 'Defining VSCode additions'
    $sb = {
        Param($context = $psEditor.GetEditorContext() )
        $prompt = 'What do you need to do?'
        $title = 'To Do'
        $item = Invoke-InputBox -Title $title -Prompt $prompt
        $todo = "# TODO: $item [$(Get-Date)]"
        $context.CurrentFile.InsertText($todo)
    }
    $rParams = @{
        Name           = 'Insert.ToDo'
        DisplayName    = 'Insert ToDo'
        ScriptBlock    = $sb
        SuppressOutput = $false
    }
    Register-EditorCommand @rParams

    Write-Verbose 'Adding Set-LocationToFile'
    Function Set-LocationToFile {
        #set location to directory of current file
        [CmdletBinding()]
        [alias('sd', 'jmp')]
        [OutputType('none')]
        Param ()

        if ($host.name -match 'Code') {

            $context = $psEditor.GetEditorContext()
            $ThisPath = $context.CurrentFile.Path
            $target = Split-Path -Path $ThisPath
            Write-Verbose "Using $ThisPath"
            Write-Verbose "Changing to $target"
            Set-Location -Path $target

            Clear-Host
        }
        else {
            Write-Warning 'This command must be run in the VS Code integrated PowerShell terminal.'
        }
    }
} #VSCode
elseif ($psISE) {
    Write-Verbose 'Defining ISE additions'

    if ($psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.DisplayName -NotContains 'ToDo') {

        $action = {
            $prompt = 'What do you need to do?'
            $title = 'To Do'
            $item = Invoke-InputBox -Title $title -Prompt $prompt
            $todo = "# [$(Get-Date)] TODO: $item"
            $psISE.CurrentFile.Editor.InsertText($todo)
            #jump cursor to the end
            $psISE.CurrentFile.editor.SetCaretPosition($psISE.CurrentFile.Editor.CaretLine, $psISE.CurrentFile.Editor.CaretColumn)
        }
        #add the action to the Add-Ons menu
        $psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('ToDo', $Action, 'Ctrl+Alt+2' ) | Out-Null
    }

    Function Set-LocationToFile {
        [cmdletbinding()]
        [alias('sd', 'jmp')]
        [OutputType('none')]
        Param()

        if ($host.name -match 'ISE') {
            $path = Split-Path -Path $psISE.CurrentFile.FullPath
            Set-Location -Path $path
            Clear-Host
        }
        Else {
            Write-Warning 'This command must be run the the PowerShell ISE.'
        }
    }
}
#endregion

#define a function to open the PDF version of the README and other documentation
Function Open-PSScriptToolsHelp {
    [cmdletbinding()]
    Param()
    Write-Verbose "Starting $($MyInvocation.MyCommand)"
    $pdf = Join-Path -Path $PSScriptRoot -ChildPath PSScriptToolsManual.pdf
    Write-Verbose "Testing the path $pdf"
    if (Test-Path -Path $pdf) {
        Try {
            Write-Verbose 'Invoking the PDF'
            Invoke-Item -Path $pdf -ErrorAction Stop
        }
        Catch {
            Write-Warning "Failed to automatically open the PDF. You will need to manually open $pdf."
        }
    }
    else {
        Write-Warning "Can't find $pdf."
    }
    Write-Verbose "Ending $($MyInvocation.MyCommand)"
}

$VerbosePreference = 'SilentlyContinue'