functions/public/Export-FunctionFromFile.ps1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
Function Export-FunctionFromFile { [cmdletbinding(SupportsShouldProcess, DefaultParameterSetName = "All")] [alias("eff")] [OutputType("None", "System.IO.FileInfo")] Param( [Parameter(Position = 0, Mandatory, HelpMessage = "Specify the .ps1 or .psm1 file with defined functions.")] [ValidateScript({ If (Test-Path $_ ) { $True If ($_ -match "\.ps(m)?1$") { $True } Else { Throw "The path must be to a .ps1 or .psm1 file." $False } } Else { Throw "Can't validate that $_ exists. Please verify and try again." $False } })] [string]$Path, [Parameter(HelpMessage = "Specify the output path. The default is the same directory as the .ps1 file.")] [string]$OutputPath, [Parameter(HelpMessage = "Specify a function by name", ParameterSetName = "byName")] [string[]]$Name, [Parameter(HelpMessage = "Export all detected functions.", ParameterSetName = "all")] [switch]$All, [Parameter(HelpMessage = "Pass the output file to the pipeline.")] [switch]$PassThru ) DynamicParam { If ( $Host.name -match 'ise|Code') { $paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary # Defining parameter attributes $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute] $attributes = New-Object System.Management.Automation.ParameterAttribute $attributes.ParameterSetName = '__AllParameterSets' $attributes.HelpMessage = 'Delete the function from the source file.' $attributeCollection.Add($attributes) # Adding a parameter alias $dynalias = New-Object System.Management.Automation.AliasAttribute -ArgumentList 'rm' $attributeCollection.Add($dynalias) # Defining the runtime parameter $dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter('Remove', [Switch], $attributeCollection) $paramDictionary.Add('Remove', $dynParam1) return $paramDictionary } # end if } #end DynamicParam Begin { Write-Verbose "[BEGIN ] Starting $($MyInvocation.MyCommand) [$($PSCmdlet.ParameterSetName)]" Write-Verbose ($PSBoundParameters | Out-String) if (-Not $OutputPath) { #use the parent path of the file unless the user specifies a different path $OutputPath = Split-Path -Path $Path -Parent } if ($Name) { Write-Verbose "[BEGIN ] Looking for functions $($name -join ',')" } #insert a temporary line for each line of the function #this will be deleted at the end. Define this in the Begin block so #that it remains static $line = "DEL-FUNCTION-$(Get-Date -f 'hhmmss')`n" } #begin Process { #Convert the path to a full file system path $path = Convert-Path -Path $path Write-Verbose "[PROCESS] Processing $path for functions" #the file will always be parsed regardless of WhatIfPreference $AST = _getAST $path #parse out functions using the AST $functions = $ast.FindAll({ $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) if ($functions.count -gt 0) { Write-Verbose "[PROCESS] Found $($functions.count) functions" Write-Verbose "[PROCESS] Creating files in $outputpath" Foreach ($item in $functions) { Write-Verbose "[PROCESS] Detected function $($item.name)" Switch ($PSCmdlet.ParameterSetName) { "byName" { if ($Name -contains $item.Name) { #export named function Write-Verbose "[PROCESS] $($item.name) matches by name" $export = $True } else { $export = $False } } "All" { if ($All -OR (Test-FunctionName -name $item.name)) { #only export functions with standard names or if -All is detected. $export = $True } else { $export = $False } } } #switch If ($export) { $NewFile = Join-Path -Path $OutputPath -ChildPath "$($item.name).ps1" Write-Verbose "[PROCESS] Creating new file $NewFile" Set-Content -Path $NewFile -Value $item.ToString() -Force if ($PSBoundParameters.ContainsKey("Remove")) { $f = $item.extent $span = $f.EndLineNumber - $f.StartLineNumber Switch -Regex ($host.name) { "PowerShell ISE" { Write-Verbose "[PROCESS] Removing from the PowerShell ISE" psedit $path $source = ($psISE.CurrentPowerShellTab.Files).where({ $_.fullpath -eq $path }) Write-Verbose "[PROCESS] Selecting a span of $span lines" $source.Editor.Select($f.StartLineNumber, $f.StartColumnNumber, $f.EndLineNumber, $f.EndColumnNumber) if ($PSCmdlet.ShouldProcess($item.name, "Deleting from $Path at $($f.StartLineNumber),$($f.StartColumnNumber),$($f.EndLineNumber),$($f.EndColumnNumber)")) { $source.Editor.InsertText($line * $span) #set a flag to save the file at the end $SaveISE = $True } } "Visual Studio Code" { Write-Verbose "[PROCESS] Removing from VS Code" Open-EditorFile $Path $ctx = $psEditor.getEditorContext() if ($PSCmdlet.ShouldProcess($item.name, "Deleting from $Path at $($f.StartLineNumber),$($f.StartColumnNumber),$($f.EndLineNumber),$($f.EndColumnNumber)")) { $psEditor.Window.SetStatusBarMessage("Removing lines $($f.StartLineNumber) to $($f.EndLineNumber) from $path", 1000) $ctx.CurrentFile.InsertText(($line * $span), $f.StartLineNumber, $f.StartColumnNumber, $f.EndLineNumber, $f.EndColumnNumber) Start-Sleep -Milliseconds 250 #set a flag to save the file at the end $SaveVSCode = $True } } } } if ($PassThru -AND (-Not $WhatIfPreference)) { Get-Item -Path $NewFile } } else { Write-Verbose "[PROCESS] Skipping $($item.name)" } } #foreach item } else { Write-Warning "No PowerShell functions detected in $Path." } } #process End { if ($SaveISE) { Write-Verbose "[END ] Removing temporary lines $line" $rev = $source.editor.Text -replace $line, '' $source.editor.text = $rev Write-Verbose "[END ] Saving $path in the PowerShell ISE" Start-Sleep -Milliseconds 500 $source.Save() #keep the file open in the ISE in case you need to do anything #else with it. $source.Editor.SetCaretPosition(1, 1) } elseif ($SaveVSCode) { Write-Verbose "[END ] Saving and re-opening $path" $ctx.CurrentFile.Save() #need to give the file time to close Start-Sleep -Seconds 2 Write-Verbose "[END ] Getting file contents" $txt = Get-Content $path -Raw # $txt | Out-File d:\temp\t.txt -force Write-Verbose "[END ] Filtering for line $($line.TrimEnd())" $rev = ([System.Text.RegularExpressions.Regex]::Matches($txt, "^((?!$($line.trimend())).)*$", "multiline")).value.replace("`r", "") Write-Verbose "[END ] Saving $path in VS Code" $rev | Out-File -FilePath $Path -Force #set cursor selection to top of file. #not sure how to force scrolling to the top $ctx.SetSelection(1, 1, 1, 1) } Write-Verbose "[END ] Ending $($MyInvocation.MyCommand)" } #end } |